MythTV master
mythdvdstream.cpp
Go to the documentation of this file.
1/* MythDVDStream
2 * Copyright 2011 Lawrence Rust <lvr at softsystem dot co dot uk>
3 */
4
5// Qt
6#include <QMutexLocker>
7#include <QtGlobal>
8#include <QtAlgorithms>
9
10// MythTV
12#include "mythdvdstream.h"
13
14// Std
15#include <algorithm>
16#include <cstdio>
17
18// DVD
19#include "dvdread/dvd_reader.h"
20#include "dvdread/dvd_udf.h"
21
22#define LOC QString("DVDStream: ")
23
26{
27 public:
28 BlockRange(uint32_t Start, uint32_t Count, int Title)
29 : m_start(Start),
30 m_end(Start + Count),
32 {
33 }
34
35 bool operator < (const BlockRange rhs) const
36 {
37 return m_end <= rhs.m_start;
38 }
39
40 uint32_t Start (void) const { return m_start; }
41 uint32_t End (void) const { return m_end; }
42 int Title (void) const { return m_title; }
43
44 private:
45 uint32_t m_start { 0 };
46 uint32_t m_end { 0 };
47 int m_title { 0 };
48};
49
50// from internal libdvdread header dvd_input.h
54static constexpr int DVDINPUT_NOFLAGS { 0 };
55static constexpr int DVDINPUT_READ_DECRYPT { 1 << 0 };
56static constexpr int DVDCSS_SEEK_KEY { 1 << 1 };
57
58// Private but located in/shared with dvd_reader.c
59extern "C" int InternalUDFReadBlocksRaw(dvd_reader_t *device, uint32_t lb_number,
60 size_t block_count, unsigned char *data,
61 int encrypted);
62
64inline uint32_t Len2Blocks(uint32_t Length)
65{
66 return (Length + (DVD_VIDEO_LB_LEN - 1)) / DVD_VIDEO_LB_LEN;
67}
68
70MythDVDStream::MythDVDStream(const QString& Filename)
72{
74}
75
77{
79 m_rwLock.lockForWrite();
80 if (m_reader)
81 DVDClose(m_reader);
82 m_rwLock.unlock();
83}
84
91bool MythDVDStream::OpenFile(const QString &Filename, std::chrono::milliseconds /*Retry*/)
92{
93 m_rwLock.lockForWrite();
94
95 const QString root = Filename.section("/VIDEO_TS/", 0, 0);
96 const QString path = Filename.section(root, 1);
97
98 if (m_reader)
99 DVDClose(m_reader);
100
101 m_reader = DVDOpen(qPrintable(root));
102 if (!m_reader)
103 {
104 LOG(VB_GENERAL, LOG_ERR, LOC + QString("DVDOpen(%1) failed").arg(Filename));
105 m_rwLock.unlock();
106 return false;
107 }
108
109 if (!path.isEmpty())
110 {
111 // Locate the start block of the requested title
112 uint32_t len = 0;
113 m_start = UDFFindFile(m_reader, qPrintable(path), &len);
114 if (m_start == 0)
115 {
116 LOG(VB_GENERAL, LOG_ERR, LOC + QString("(%1) UDFFindFile(%2) failed").
117 arg(root, path));
118 DVDClose(m_reader);
119 m_reader = nullptr;
120 m_rwLock.unlock();
121 return false;
122 }
123 m_blocks.append(BlockRange(0, Len2Blocks(len), 0));
124 }
125 else
126 {
127 // Create a list of the possibly encrypted files
128 uint32_t len = 0;
129
130 // Root menu
131 QString name { "VIDEO_TS/VIDEO_TS.VOB" };
132 uint32_t start = UDFFindFile(m_reader, qPrintable(name), &len);
133 if( start != 0 && len != 0 )
134 m_blocks.append(BlockRange(start, Len2Blocks(len), 0));
135
136 const int kTitles = 100;
137 for (int title = 1; title < kTitles; ++title)
138 {
139 // Menu
140 name = QString("/VIDEO_TS/VTS_%1_0.VOB").arg(title,2,10,QChar('0'));
141 start = UDFFindFile(m_reader, qPrintable(name), &len);
142 if( start != 0 && len != 0 )
143 m_blocks.append(BlockRange(start, Len2Blocks(len), title));
144
145 for ( int part = 1; part < 10; ++part)
146 {
147 // A/V track
148 name = QString("/VIDEO_TS/VTS_%1_%2.VOB").arg(title,2,10,QChar('0')).arg(part);
149 start = UDFFindFile(m_reader, qPrintable(name), &len);
150 if( start != 0 && len != 0 )
151 m_blocks.append(BlockRange(start, Len2Blocks(len), title + (part * kTitles)));
152 }
153 }
154
155 // QList doesn't play well with std::ranges
156 // NOLINTNEXTLINE(modernize-use-ranges)
157 std::sort(m_blocks.begin(), m_blocks.end());
158
159 // Open the root menu so that CSS keys are generated now
160 dvd_file_t *file = DVDOpenFile(m_reader, 0, DVD_READ_MENU_VOBS);
161 if (file)
162 DVDCloseFile(file);
163 else
164 LOG(VB_GENERAL, LOG_ERR, LOC + "DVDOpenFile(VOBS_1) failed");
165 }
166
167 m_rwLock.unlock();
168 return true;
169}
170
171bool MythDVDStream::IsOpen(void) const
172{
173 m_rwLock.lockForRead();
174 bool ret = m_reader != nullptr;
175 m_rwLock.unlock();
176 return ret;
177}
178
180{
181 uint32_t block = Size / DVD_VIDEO_LB_LEN;
182 if (block < 1)
183 {
184 LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead too small");
185 return -1;
186 }
187
188 if (!m_reader)
189 return -1;
190
191 int ret = 0;
192
193 // Are any blocks in the range encrypted?
194 // QList doesn't play well with std::ranges
195 // NOLINTNEXTLINE(modernize-use-ranges)
196 auto it = std::lower_bound(m_blocks.begin(), m_blocks.end(), BlockRange(m_pos, block, -1));
197 uint32_t b {0};
198 if (it == m_blocks.end())
199 b = block;
200 else if (m_pos < it->Start())
201 b = it->Start() - m_pos;
202 if (b)
203 {
204 // Read the beginning unencrypted blocks
205 ret = InternalUDFReadBlocksRaw(m_reader, m_pos, b, static_cast<unsigned char*>(Buffer), DVDINPUT_NOFLAGS);
206 if (ret == -1)
207 {
208 LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
209 return ret;
210 }
211
212 m_pos += static_cast<uint>(ret);
213 block -= static_cast<uint>(ret);
214 if (it == m_blocks.end())
215 return ret * DVD_VIDEO_LB_LEN;
216
217 Buffer = static_cast<unsigned char*>(Buffer) +
218 (static_cast<ptrdiff_t>(ret) * DVD_VIDEO_LB_LEN);
219 }
220
221 b = it->End() - m_pos;
222 b = std::min(b, block);
223
224 // Request new key if change in title
225 int flags = DVDINPUT_READ_DECRYPT;
226 if (it->Title() != m_title)
227 {
228 m_title = it->Title();
229 flags |= DVDCSS_SEEK_KEY;
230 }
231
232 // Read the encrypted blocks
233 int ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos + m_start, b, static_cast<unsigned char*>(Buffer), flags);
234 if (ret2 == -1)
235 {
236 LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
237 m_title = -1;
238 return -1;
239 }
240
241 m_pos += static_cast<uint>(ret2);
242 ret += ret2;
243 block -= static_cast<uint>(ret2);
244 Buffer = static_cast<unsigned char*>(Buffer) +
245 (static_cast<ptrdiff_t>(ret2) * DVD_VIDEO_LB_LEN);
246
247 if (block > 0 && m_start == 0)
248 {
249 // Read the last unencrypted blocks
250 ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos, block, static_cast<unsigned char*>(Buffer), DVDINPUT_NOFLAGS);
251 if (ret2 == -1)
252 {
253 LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
254 return -1;
255 }
256
257 m_pos += static_cast<uint>(ret2);
258 ret += ret2;
259 }
260
261 return ret * DVD_VIDEO_LB_LEN;
262}
263
264long long MythDVDStream::SeekInternal(long long Position, int Whence)
265{
266 if (!m_reader)
267 return -1;
268
269 if (SEEK_END == Whence)
270 {
271 errno = EINVAL;
272 return -1;
273 }
274
275 auto block = static_cast<uint32_t>(Position / DVD_VIDEO_LB_LEN);
276 if (static_cast<int64_t>(block) * DVD_VIDEO_LB_LEN != Position)
277 {
278 LOG(VB_GENERAL, LOG_ERR, LOC + "Seek not block aligned");
279 return -1;
280 }
281
282 m_posLock.lockForWrite();
283 m_pos = block;
284 m_posLock.unlock();
285 m_generalWait.wakeAll();
286 return Position;
287}
288
290{
291 m_posLock.lockForRead();
292 long long ret = static_cast<long long>(m_pos) * DVD_VIDEO_LB_LEN;
293 m_posLock.unlock();
294 return ret;
295}
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:281
bool operator<(const BlockRange rhs) const
uint32_t Start(void) const
BlockRange(uint32_t Start, uint32_t Count, int Title)
uint32_t End(void) const
QList< BlockRange > m_blocks
Definition: mythdvdstream.h:40
bool IsOpen(void) const override
long long SeekInternal(long long Position, int Whence) override
bool OpenFile(const QString &Filename, std::chrono::milliseconds Retry=0ms) override
long long GetReadPosition(void) const override
~MythDVDStream() override
MythDVDStream(const QString &Filename)
uint32_t m_start
Definition: mythdvdstream.h:39
uint32_t m_pos
Definition: mythdvdstream.h:41
dvd_reader_t * m_reader
Definition: mythdvdstream.h:38
int SafeRead(void *Buffer, uint Size) override
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
void Start(void)
Starts the read-ahead thread.
QReadWriteLock m_posLock
QReadWriteLock m_rwLock
QWaitCondition m_generalWait
Condition to signal that the read ahead thread is running.
unsigned int uint
Definition: compat.h:60
static constexpr int DVDINPUT_NOFLAGS
Defines and flags.
#define LOC
int InternalUDFReadBlocksRaw(dvd_reader_t *device, uint32_t lb_number, size_t block_count, unsigned char *data, int encrypted)
static constexpr int DVDINPUT_READ_DECRYPT
static constexpr int DVDCSS_SEEK_KEY
uint32_t Len2Blocks(uint32_t Length)
Roundup bytes to DVD blocks.
dvd_reader_s dvd_reader_t
Definition: mythdvdstream.h:17
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
@ kMythBufferFile