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