MythTV  master
dvdstream.cpp
Go to the documentation of this file.
1 /* DVD stream
2  * Copyright 2011 Lawrence Rust <lvr at softsystem dot co dot uk>
3  */
4 #include "dvdstream.h"
5 
6 #include <algorithm>
7 #include <cstdio>
8 
9 #include <QMutexLocker>
10 #include <QtGlobal>
11 #include <QtAlgorithms>
12 
13 #include "dvdread/dvd_reader.h"
14 #include "dvdread/dvd_udf.h" // for UDFFindFile
15 extern "C" {
16 #include "dvd_input.h" // for DVDINPUT_READ_DECRYPT & DVDCSS_SEEK_KEY
17 }
18 
19 #include "mythlogging.h"
20 
21 
22 // A range of block numbers
24 {
25  uint32_t m_start;
26  uint32_t m_end;
27  int m_title;
28 
29 public:
30  BlockRange(uint32_t b, uint32_t n, int t) : m_start(b), m_end(b+n), m_title(t) { }
31 
32  bool operator < (const BlockRange& rhs) const { return m_end <= rhs.m_start; }
33 
34  uint32_t Start() const { return m_start; }
35  uint32_t End() const { return m_end; }
36  int Title() const { return m_title; }
37 };
38 
39 
40 // Private but located in/shared with dvd_reader.c
41 extern "C" int InternalUDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
42  size_t block_count, unsigned char *data,
43  int encrypted );
44 
45 
46 // Roundup bytes to DVD blocks
47 inline uint32_t Len2Blocks(uint32_t len)
48 {
49  return (len + (DVD_VIDEO_LB_LEN - 1)) / DVD_VIDEO_LB_LEN;
50 }
51 
54 {
56 }
57 
59 {
61 
62  m_rwLock.lockForWrite();
63 
64  if (m_reader)
65  DVDClose(m_reader);
66 
67  m_rwLock.unlock();
68 }
69 
78 bool DVDStream::OpenFile(const QString &filename, uint /*retry_ms*/)
79 {
80  m_rwLock.lockForWrite();
81 
82  const QString root = filename.section("/VIDEO_TS/", 0, 0);
83  const QString path = filename.section(root, 1);
84 
85  if (m_reader)
86  DVDClose(m_reader);
87 
88  m_reader = DVDOpen(qPrintable(root));
89  if (!m_reader)
90  {
91  LOG(VB_GENERAL, LOG_ERR, QString("DVDStream DVDOpen(%1) failed").arg(filename));
92  m_rwLock.unlock();
93  return false;
94  }
95 
96  if (!path.isEmpty())
97  {
98  // Locate the start block of the requested title
99  uint32_t len = 0;
100  m_start = UDFFindFile(m_reader, qPrintable(path), &len);
101  if (m_start == 0)
102  {
103  LOG(VB_GENERAL, LOG_ERR, QString("DVDStream(%1) UDFFindFile(%2) failed").
104  arg(root).arg(path));
105  DVDClose(m_reader);
106  m_reader = nullptr;
107  m_rwLock.unlock();
108  return false;
109  }
110  m_list.append(BlockRange(0, Len2Blocks(len), 0));
111  }
112  else
113  {
114  // Create a list of the possibly encrypted files
115  uint32_t len = 0;
116 
117  // Root menu
118  char name[64] = "VIDEO_TS/VIDEO_TS.VOB";
119  uint32_t start = UDFFindFile(m_reader, name, &len);
120  if( start != 0 && len != 0 )
121  m_list.append(BlockRange(start, Len2Blocks(len), 0));
122 
123  const int kTitles = 100;
124  for ( int title = 1; title < kTitles; ++title)
125  {
126  // Menu
127  snprintf(name, sizeof name, "/VIDEO_TS/VTS_%02d_0.VOB", title);
128  start = UDFFindFile(m_reader, name, &len);
129  if( start != 0 && len != 0 )
130  m_list.append(BlockRange(start, Len2Blocks(len), title));
131 
132  for ( int part = 1; part < 10; ++part)
133  {
134  // A/V track
135  snprintf(name, sizeof name, "/VIDEO_TS/VTS_%02d_%d.VOB", title, part);
136  start = UDFFindFile(m_reader, name, &len);
137  if( start != 0 && len != 0 )
138  m_list.append(BlockRange(start, Len2Blocks(len), title + part * kTitles));
139  }
140  }
141 
142  std::sort(m_list.begin(), m_list.end());
143 
144  // Open the root menu so that CSS keys are generated now
145  dvd_file_t *file = DVDOpenFile(m_reader, 0, DVD_READ_MENU_VOBS);
146  if (file)
147  DVDCloseFile(file);
148  else
149  LOG(VB_GENERAL, LOG_ERR, "DVDStream DVDOpenFile(VOBS_1) failed");
150  }
151 
152  m_rwLock.unlock();
153  return true;
154 }
155 
156 //virtual
157 bool DVDStream::IsOpen(void) const
158 {
159  m_rwLock.lockForRead();
160  bool ret = m_reader != nullptr;
161  m_rwLock.unlock();
162  return ret;
163 }
164 
165 //virtual
166 int DVDStream::safe_read(void *data, uint size)
167 {
168  uint32_t lb = size / DVD_VIDEO_LB_LEN;
169  if (lb < 1)
170  {
171  LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read too small");
172  return -1;
173  }
174 
175  if (!m_reader)
176  return -1;
177 
178  int ret = 0;
179 
180  // Are any blocks in the range encrypted?
181  list_t::const_iterator it;
182  it = std::lower_bound(m_list.begin(), m_list.end(), BlockRange(m_pos, lb, -1));
183  uint32_t b = it == m_list.end() ? lb : m_pos < it->Start() ? it->Start() - m_pos : 0;
184  if (b)
185  {
186  // Read the beginning unencrypted blocks
187  ret = InternalUDFReadBlocksRaw(m_reader, m_pos, b, (unsigned char*)data, DVDINPUT_NOFLAGS);
188  if (ret == -1)
189  {
190  LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read DVDReadBlocks error");
191  return -1;
192  }
193 
194  m_pos += ret;
195  lb -= ret;
196  if (it == m_list.end())
197  return ret * DVD_VIDEO_LB_LEN;
198 
199  data = (unsigned char*)data + ret * DVD_VIDEO_LB_LEN;
200  }
201 
202  b = it->End() - m_pos;
203  if (b > lb)
204  b = lb;
205 
206  // Request new key if change in title
207  int flags = DVDINPUT_READ_DECRYPT;
208  if (it->Title() != m_title)
209  {
210  m_title = it->Title();
211  flags |= DVDCSS_SEEK_KEY;
212  }
213 
214  // Read the encrypted blocks
215  int ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos + m_start, b, (unsigned char*)data, flags);
216  if (ret2 == -1)
217  {
218  LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read DVDReadBlocks error");
219  m_title = -1;
220  return -1;
221  }
222 
223  m_pos += ret2;
224  ret += ret2;
225  lb -= ret2;
226  data = (unsigned char*)data + ret2 * DVD_VIDEO_LB_LEN;
227 
228  if (lb > 0 && m_start == 0)
229  {
230  // Read the last unencrypted blocks
231  ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos, lb, (unsigned char*)data, DVDINPUT_NOFLAGS);
232  if (ret2 == -1)
233  {
234  LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read DVDReadBlocks error");
235  return -1;
236  }
237 
238  m_pos += ret2;
239  ret += ret2;;
240  }
241 
242  return ret * DVD_VIDEO_LB_LEN;
243 }
244 
245 //virtual
246 long long DVDStream::SeekInternal(long long pos, int whence)
247 {
248  if (!m_reader)
249  return -1;
250 
251  if (SEEK_END == whence)
252  {
253  errno = EINVAL;
254  return -1;
255  }
256 
257  uint32_t lb = pos / DVD_VIDEO_LB_LEN;
258  if ((qlonglong)lb * DVD_VIDEO_LB_LEN != pos)
259  {
260  LOG(VB_GENERAL, LOG_ERR, "DVDStream::Seek not block aligned");
261  return -1;
262  }
263 
264  m_posLock.lockForWrite();
265 
266  m_pos = lb;
267 
268  m_posLock.unlock();
269 
270  m_generalWait.wakeAll();
271 
272  return pos;
273 }
274 
275 //virtual
276 long long DVDStream::GetReadPosition(void) const
277 {
278  m_posLock.lockForRead();
279  long long ret = (long long)m_pos * DVD_VIDEO_LB_LEN;
280  m_posLock.unlock();
281  return ret;
282 }
283 
284 // End of dvdstream,.cpp
int safe_read(void *data, uint size) override
Definition: dvdstream.cpp:166
uint32_t m_pos
Definition: dvdstream.h:47
uint32_t Len2Blocks(uint32_t len)
Definition: dvdstream.cpp:47
dvd_reader_t * m_reader
Definition: dvdstream.h:40
struct dvd_reader_s dvd_reader_t
Definition: dvdstream.h:14
QReadWriteLock m_rwLock
uint32_t Start() const
Definition: dvdstream.cpp:34
bool operator<(const BlockRange &rhs) const
Definition: dvdstream.cpp:32
int m_title
Definition: dvdstream.h:48
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
Definition: ringbuffer.cpp:693
int InternalUDFReadBlocksRaw(dvd_reader_t *device, uint32_t lb_number, size_t block_count, unsigned char *data, int encrypted)
QReadWriteLock m_posLock
uint32_t m_start
Definition: dvdstream.h:41
bool IsOpen(void) const override
Returns true if open for either reading or writing.
Definition: dvdstream.cpp:157
long long GetReadPosition(void) const override
Returns how far into the file we have read.
Definition: dvdstream.cpp:276
unsigned int uint
Definition: compat.h:140
DVDStream(const QString &filename)
Definition: dvdstream.cpp:52
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
list_t m_list
Definition: dvdstream.h:45
bool OpenFile(const QString &lfilename, uint retry_ms=0) override
Opens a dvd device for streaming.
Definition: dvdstream.cpp:78
QWaitCondition m_generalWait
Condition to signal that the read ahead thread is running.
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
Implements a file/stream reader/writer.
long long SeekInternal(long long pos, int whence) override
Definition: dvdstream.cpp:246
~DVDStream() override
Definition: dvdstream.cpp:58
uint32_t End() const
Definition: dvdstream.cpp:35
BlockRange(uint32_t b, uint32_t n, int t)
Definition: dvdstream.cpp:30