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"
21extern "C" {
22#include "dvd_input.h"
23}
24
25#define LOC QString("DVDStream: ")
26
29{
30 public:
31 BlockRange(uint32_t Start, uint32_t Count, int Title)
32 : m_start(Start),
33 m_end(Start + Count),
35 {
36 }
37
38 bool operator < (const BlockRange rhs) const
39 {
40 return m_end <= rhs.m_start;
41 }
42
43 uint32_t Start (void) const { return m_start; }
44 uint32_t End (void) const { return m_end; }
45 int Title (void) const { return m_title; }
46
47 private:
48 uint32_t m_start { 0 };
49 uint32_t m_end { 0 };
50 int m_title { 0 };
51};
52
53
54// Private but located in/shared with dvd_reader.c
55extern "C" int InternalUDFReadBlocksRaw(dvd_reader_t *device, uint32_t lb_number,
56 size_t block_count, unsigned char *data,
57 int encrypted);
58
60inline uint32_t Len2Blocks(uint32_t Length)
61{
62 return (Length + (DVD_VIDEO_LB_LEN - 1)) / DVD_VIDEO_LB_LEN;
63}
64
66MythDVDStream::MythDVDStream(const QString& Filename)
68{
70}
71
73{
75 m_rwLock.lockForWrite();
76 if (m_reader)
77 DVDClose(m_reader);
78 m_rwLock.unlock();
79}
80
87bool MythDVDStream::OpenFile(const QString &Filename, std::chrono::milliseconds /*Retry*/)
88{
89 m_rwLock.lockForWrite();
90
91 const QString root = Filename.section("/VIDEO_TS/", 0, 0);
92 const QString path = Filename.section(root, 1);
93
94 if (m_reader)
95 DVDClose(m_reader);
96
97 m_reader = DVDOpen(qPrintable(root));
98 if (!m_reader)
99 {
100 LOG(VB_GENERAL, LOG_ERR, LOC + QString("DVDOpen(%1) failed").arg(Filename));
101 m_rwLock.unlock();
102 return false;
103 }
104
105 if (!path.isEmpty())
106 {
107 // Locate the start block of the requested title
108 uint32_t len = 0;
109 m_start = UDFFindFile(m_reader, qPrintable(path), &len);
110 if (m_start == 0)
111 {
112 LOG(VB_GENERAL, LOG_ERR, LOC + QString("(%1) UDFFindFile(%2) failed").
113 arg(root, path));
114 DVDClose(m_reader);
115 m_reader = nullptr;
116 m_rwLock.unlock();
117 return false;
118 }
119 m_blocks.append(BlockRange(0, Len2Blocks(len), 0));
120 }
121 else
122 {
123 // Create a list of the possibly encrypted files
124 uint32_t len = 0;
125
126 // Root menu
127 QString name { "VIDEO_TS/VIDEO_TS.VOB" };
128 uint32_t start = UDFFindFile(m_reader, qPrintable(name), &len);
129 if( start != 0 && len != 0 )
130 m_blocks.append(BlockRange(start, Len2Blocks(len), 0));
131
132 const int kTitles = 100;
133 for (int title = 1; title < kTitles; ++title)
134 {
135 // Menu
136 name = QString("/VIDEO_TS/VTS_%1_0.VOB").arg(title,2,10,QChar('0'));
137 start = UDFFindFile(m_reader, qPrintable(name), &len);
138 if( start != 0 && len != 0 )
139 m_blocks.append(BlockRange(start, Len2Blocks(len), title));
140
141 for ( int part = 1; part < 10; ++part)
142 {
143 // A/V track
144 name = QString("/VIDEO_TS/VTS_%1_%2.VOB").arg(title,2,10,QChar('0')).arg(part);
145 start = UDFFindFile(m_reader, qPrintable(name), &len);
146 if( start != 0 && len != 0 )
147 m_blocks.append(BlockRange(start, Len2Blocks(len), title + (part * kTitles)));
148 }
149 }
150
151 std::sort(m_blocks.begin(), m_blocks.end());
152
153 // Open the root menu so that CSS keys are generated now
154 dvd_file_t *file = DVDOpenFile(m_reader, 0, DVD_READ_MENU_VOBS);
155 if (file)
156 DVDCloseFile(file);
157 else
158 LOG(VB_GENERAL, LOG_ERR, LOC + "DVDOpenFile(VOBS_1) failed");
159 }
160
161 m_rwLock.unlock();
162 return true;
163}
164
165bool MythDVDStream::IsOpen(void) const
166{
167 m_rwLock.lockForRead();
168 bool ret = m_reader != nullptr;
169 m_rwLock.unlock();
170 return ret;
171}
172
174{
175 uint32_t block = Size / DVD_VIDEO_LB_LEN;
176 if (block < 1)
177 {
178 LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead too small");
179 return -1;
180 }
181
182 if (!m_reader)
183 return -1;
184
185 int ret = 0;
186
187 // Are any blocks in the range encrypted?
188 auto it = std::lower_bound(m_blocks.begin(), m_blocks.end(), BlockRange(m_pos, block, -1));
189 uint32_t b {0};
190 if (it == m_blocks.end())
191 b = block;
192 else if (m_pos < it->Start())
193 b = it->Start() - m_pos;
194 if (b)
195 {
196 // Read the beginning unencrypted blocks
197 ret = InternalUDFReadBlocksRaw(m_reader, m_pos, b, static_cast<unsigned char*>(Buffer), DVDINPUT_NOFLAGS);
198 if (ret == -1)
199 {
200 LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
201 return ret;
202 }
203
204 m_pos += static_cast<uint>(ret);
205 block -= static_cast<uint>(ret);
206 if (it == m_blocks.end())
207 return ret * DVD_VIDEO_LB_LEN;
208
209 Buffer = static_cast<unsigned char*>(Buffer) +
210 (static_cast<ptrdiff_t>(ret) * DVD_VIDEO_LB_LEN);
211 }
212
213 b = it->End() - m_pos;
214 b = std::min(b, block);
215
216 // Request new key if change in title
217 int flags = DVDINPUT_READ_DECRYPT;
218 if (it->Title() != m_title)
219 {
220 m_title = it->Title();
221 flags |= DVDCSS_SEEK_KEY;
222 }
223
224 // Read the encrypted blocks
225 int ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos + m_start, b, static_cast<unsigned char*>(Buffer), flags);
226 if (ret2 == -1)
227 {
228 LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
229 m_title = -1;
230 return -1;
231 }
232
233 m_pos += static_cast<uint>(ret2);
234 ret += ret2;
235 block -= static_cast<uint>(ret2);
236 Buffer = static_cast<unsigned char*>(Buffer) +
237 (static_cast<ptrdiff_t>(ret2) * DVD_VIDEO_LB_LEN);
238
239 if (block > 0 && m_start == 0)
240 {
241 // Read the last unencrypted blocks
242 ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos, block, static_cast<unsigned char*>(Buffer), DVDINPUT_NOFLAGS);
243 if (ret2 == -1)
244 {
245 LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
246 return -1;
247 }
248
249 m_pos += static_cast<uint>(ret2);
250 ret += ret2;
251 }
252
253 return ret * DVD_VIDEO_LB_LEN;
254}
255
256long long MythDVDStream::SeekInternal(long long Position, int Whence)
257{
258 if (!m_reader)
259 return -1;
260
261 if (SEEK_END == Whence)
262 {
263 errno = EINVAL;
264 return -1;
265 }
266
267 auto block = static_cast<uint32_t>(Position / DVD_VIDEO_LB_LEN);
268 if (static_cast<int64_t>(block) * DVD_VIDEO_LB_LEN != Position)
269 {
270 LOG(VB_GENERAL, LOG_ERR, LOC + "Seek not block aligned");
271 return -1;
272 }
273
274 m_posLock.lockForWrite();
275 m_pos = block;
276 m_posLock.unlock();
277 m_generalWait.wakeAll();
278 return Position;
279}
280
282{
283 m_posLock.lockForRead();
284 long long ret = static_cast<long long>(m_pos) * DVD_VIDEO_LB_LEN;
285 m_posLock.unlock();
286 return ret;
287}
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
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: freesurround.h:24
#define LOC
int InternalUDFReadBlocksRaw(dvd_reader_t *device, uint32_t lb_number, size_t block_count, unsigned char *data, int encrypted)
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