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
11 #include "mythlogging.h"
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) + ret * DVD_VIDEO_LB_LEN;
206  }
207 
208  b = it->End() - m_pos;
209  if (b > block)
210  b = block;
211 
212  // Request new key if change in title
213  int flags = DVDINPUT_READ_DECRYPT;
214  if (it->Title() != m_title)
215  {
216  m_title = it->Title();
217  flags |= DVDCSS_SEEK_KEY;
218  }
219 
220  // Read the encrypted blocks
221  int ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos + m_start, b, static_cast<unsigned char*>(Buffer), flags);
222  if (ret2 == -1)
223  {
224  LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
225  m_title = -1;
226  return -1;
227  }
228 
229  m_pos += static_cast<uint>(ret2);
230  ret += ret2;
231  block -= static_cast<uint>(ret2);
232  Buffer = static_cast<unsigned char*>(Buffer) + ret2 * DVD_VIDEO_LB_LEN;
233 
234  if (block > 0 && m_start == 0)
235  {
236  // Read the last unencrypted blocks
237  ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos, block, static_cast<unsigned char*>(Buffer), DVDINPUT_NOFLAGS);
238  if (ret2 == -1)
239  {
240  LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
241  return -1;
242  }
243 
244  m_pos += static_cast<uint>(ret2);
245  ret += ret2;
246  }
247 
248  return ret * DVD_VIDEO_LB_LEN;
249 }
250 
251 long long MythDVDStream::SeekInternal(long long Position, int Whence)
252 {
253  if (!m_reader)
254  return -1;
255 
256  if (SEEK_END == Whence)
257  {
258  errno = EINVAL;
259  return -1;
260  }
261 
262  auto block = static_cast<uint32_t>(Position / DVD_VIDEO_LB_LEN);
263  if (static_cast<int64_t>(block) * DVD_VIDEO_LB_LEN != Position)
264  {
265  LOG(VB_GENERAL, LOG_ERR, LOC + "Seek not block aligned");
266  return -1;
267  }
268 
269  m_posLock.lockForWrite();
270  m_pos = block;
271  m_posLock.unlock();
272  m_generalWait.wakeAll();
273  return Position;
274 }
275 
276 long long MythDVDStream::GetReadPosition(void) const
277 {
278  m_posLock.lockForRead();
279  long long ret = static_cast<long long>(m_pos) * DVD_VIDEO_LB_LEN;
280  m_posLock.unlock();
281  return ret;
282 }
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:286
MythDVDStream::SeekInternal
long long SeekInternal(long long Position, int Whence) override
Definition: mythdvdstream.cpp:251
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:23
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:140
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:276
MythMediaBuffer::KillReadAheadThread
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
Definition: mythmediabuffer.cpp:646
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:610
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