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 {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 
256 long 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 
281 long long MythDVDStream::GetReadPosition(void) const
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 }
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:256
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:59
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:246
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
kMythBufferFile
@ kMythBufferFile
Definition: mythmediabuffer.h:51
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
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:281
MythMediaBuffer::KillReadAheadThread
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
Definition: mythmediabuffer.cpp:653
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:617
MythDVDStream::m_blocks
QList< BlockRange > m_blocks
Definition: mythdvdstream.h:40
MythMediaBuffer::m_posLock
QReadWriteLock m_posLock
Definition: mythmediabuffer.h:173
MythDVDStream::BlockRange::End
uint32_t End(void) const
Definition: mythdvdstream.cpp:44
MythMediaBuffer::m_rwLock
QReadWriteLock m_rwLock
Definition: mythmediabuffer.h:192
MythDVDStream::BlockRange::operator<
bool operator<(const BlockRange rhs) const
Definition: mythdvdstream.cpp:38
uint
unsigned int uint
Definition: freesurround.h:24