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 extern "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),
34  m_title(Title)
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
55 extern "C" int InternalUDFReadBlocksRaw(dvd_reader_t *device, uint32_t lb_number,
56  size_t block_count, unsigned char *data,
57  int encrypted);
58 
60 inline uint32_t Len2Blocks(uint32_t Length)
61 {
62  return (Length + (DVD_VIDEO_LB_LEN - 1)) / DVD_VIDEO_LB_LEN;
63 }
64 
66 MythDVDStream::MythDVDStream(const QString& Filename)
68 {
69  MythDVDStream::OpenFile(Filename);
70 }
71 
73 {
75  m_rwLock.lockForWrite();
76  if (m_reader)
77  DVDClose(m_reader);
78  m_rwLock.unlock();
79 }
80 
87 bool 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 
165 bool 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 = (it == m_blocks.end()) ? block : (m_pos < it->Start() ? it->Start() - m_pos : 0);
190  if (b)
191  {
192  // Read the beginning unencrypted blocks
193  ret = InternalUDFReadBlocksRaw(m_reader, m_pos, b, static_cast<unsigned char*>(Buffer), DVDINPUT_NOFLAGS);
194  if (ret == -1)
195  {
196  LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
197  return ret;
198  }
199 
200  m_pos += static_cast<uint>(ret);
201  block -= static_cast<uint>(ret);
202  if (it == m_blocks.end())
203  return ret * DVD_VIDEO_LB_LEN;
204 
205  Buffer = static_cast<unsigned char*>(Buffer) +
206  static_cast<ptrdiff_t>(ret) * DVD_VIDEO_LB_LEN;
207  }
208 
209  b = it->End() - m_pos;
210  if (b > block)
211  b = block;
212 
213  // Request new key if change in title
214  int flags = DVDINPUT_READ_DECRYPT;
215  if (it->Title() != m_title)
216  {
217  m_title = it->Title();
218  flags |= DVDCSS_SEEK_KEY;
219  }
220 
221  // Read the encrypted blocks
222  int ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos + m_start, b, static_cast<unsigned char*>(Buffer), flags);
223  if (ret2 == -1)
224  {
225  LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
226  m_title = -1;
227  return -1;
228  }
229 
230  m_pos += static_cast<uint>(ret2);
231  ret += ret2;
232  block -= static_cast<uint>(ret2);
233  Buffer = static_cast<unsigned char*>(Buffer) +
234  static_cast<ptrdiff_t>(ret2) * DVD_VIDEO_LB_LEN;
235 
236  if (block > 0 && m_start == 0)
237  {
238  // Read the last unencrypted blocks
239  ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos, block, static_cast<unsigned char*>(Buffer), DVDINPUT_NOFLAGS);
240  if (ret2 == -1)
241  {
242  LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
243  return -1;
244  }
245 
246  m_pos += static_cast<uint>(ret2);
247  ret += ret2;
248  }
249 
250  return ret * DVD_VIDEO_LB_LEN;
251 }
252 
253 long long MythDVDStream::SeekInternal(long long Position, int Whence)
254 {
255  if (!m_reader)
256  return -1;
257 
258  if (SEEK_END == Whence)
259  {
260  errno = EINVAL;
261  return -1;
262  }
263 
264  auto block = static_cast<uint32_t>(Position / DVD_VIDEO_LB_LEN);
265  if (static_cast<int64_t>(block) * DVD_VIDEO_LB_LEN != Position)
266  {
267  LOG(VB_GENERAL, LOG_ERR, LOC + "Seek not block aligned");
268  return -1;
269  }
270 
271  m_posLock.lockForWrite();
272  m_pos = block;
273  m_posLock.unlock();
274  m_generalWait.wakeAll();
275  return Position;
276 }
277 
278 long long MythDVDStream::GetReadPosition(void) const
279 {
280  m_posLock.lockForRead();
281  long long ret = static_cast<long long>(m_pos) * DVD_VIDEO_LB_LEN;
282  m_posLock.unlock();
283  return ret;
284 }
mythdvdstream.h
MythDVDStream::BlockRange
Definition: mythdvdstream.cpp:28
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
MythDVDStream::SeekInternal
long long SeekInternal(long long Position, int Whence) override
Definition: mythdvdstream.cpp:253
MythDVDStream::OpenFile
bool OpenFile(const QString &Filename, std::chrono::milliseconds Retry=0ms) override
Definition: mythdvdstream.cpp:87
MythDVDStream::m_title
int m_title
Definition: mythdvdstream.h:42
MythDVDStream::~MythDVDStream
~MythDVDStream() override
Definition: mythdvdstream.cpp:72
Len2Blocks
uint32_t Len2Blocks(uint32_t Length)
Roundup bytes to DVD blocks.
Definition: mythdvdstream.cpp:60
MythMediaBuffer
Definition: mythmediabuffer.h:50
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMediaBuffer::m_generalWait
QWaitCondition m_generalWait
Condition to signal that the read ahead thread is running.
Definition: mythmediabuffer.h:237
build_compdb.file
file
Definition: build_compdb.py:55
MythDVDStream::SafeRead
int SafeRead(void *Buffer, uint Size) override
Definition: mythdvdstream.cpp:173
MythDVDStream::IsOpen
bool IsOpen(void) const override
Definition: mythdvdstream.cpp:165
MythDVDStream::BlockRange::m_end
uint32_t m_end
Definition: mythdvdstream.cpp:49
MythDVDStream::m_reader
dvd_reader_t * m_reader
Definition: mythdvdstream.h:38
MythDVDStream::m_start
uint32_t m_start
Definition: mythdvdstream.h:39
InternalUDFReadBlocksRaw
int InternalUDFReadBlocksRaw(dvd_reader_t *device, uint32_t lb_number, size_t block_count, unsigned char *data, int encrypted)
mythlogging.h
MythDVDStream::MythDVDStream
MythDVDStream(const QString &Filename)
Definition: mythdvdstream.cpp:66
MythDVDStream::BlockRange::BlockRange
BlockRange(uint32_t Start, uint32_t Count, int Title)
Definition: mythdvdstream.cpp:31
MythDVDStream::BlockRange::m_start
uint32_t m_start
Definition: mythdvdstream.cpp:48
uint
unsigned int uint
Definition: compat.h:81
dvd_reader_t
struct dvd_reader_s dvd_reader_t
Definition: mythdvdstream.h:17
LOC
#define LOC
Definition: mythdvdstream.cpp:25
MythDVDStream::GetReadPosition
long long GetReadPosition(void) const override
Definition: mythdvdstream.cpp:278
MythMediaBuffer::KillReadAheadThread
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
Definition: mythmediabuffer.cpp:647
Buffer
Definition: MythExternControl.h:36
MythDVDStream::BlockRange::Title
int Title(void) const
Definition: mythdvdstream.cpp:45
MythDVDStream::m_pos
uint32_t m_pos
Definition: mythdvdstream.h:41
MythDVDStream::BlockRange::m_title
int m_title
Definition: mythdvdstream.cpp:50
MythDVDStream::BlockRange::Start
uint32_t Start(void) const
Definition: mythdvdstream.cpp:43
MythMediaBuffer::Start
void Start(void)
Starts the read-ahead thread.
Definition: mythmediabuffer.cpp:611
MythDVDStream::m_blocks
QList< BlockRange > m_blocks
Definition: mythdvdstream.h:40
kMythBufferFile
@ kMythBufferFile
Definition: mythmediabuffer.h:42
MythMediaBuffer::m_posLock
QReadWriteLock m_posLock
Definition: mythmediabuffer.h:164
MythDVDStream::BlockRange::End
uint32_t End(void) const
Definition: mythdvdstream.cpp:44
MythMediaBuffer::m_rwLock
QReadWriteLock m_rwLock
Definition: mythmediabuffer.h:183
MythDVDStream::BlockRange::operator<
bool operator<(const BlockRange rhs) const
Definition: mythdvdstream.cpp:38