Ticket #11602: 0029-libmythtv-Play-encrypted-dvd-s-and-iso-images-from-s.patch
| File 0029-libmythtv-Play-encrypted-dvd-s-and-iso-images-from-s.patch, 16.5 KB (added by , 13 years ago) |
|---|
-
mythtv/libs/libmythdvdnav/dvdread/dvd_input.c
From 61c056dc2ac4aa1e412a310cc1f158f8ab9674bc Mon Sep 17 00:00:00 2001 From: Lawrence Rust <lvr@softsystem.co.uk> Date: Sat, 26 Nov 2011 21:22:43 +0100 Subject: [PATCH 029/116] libmythtv: Play encrypted dvd's and iso images from storage groups This change enables the backend to use libdvdcss to decrypt dvd's and iso images before sending the blocks over the myth protocol to the frontend. This replaces the current POSIX file read in RingBuffer with a class like DVDRingBufferPriv that uses libdvdread to decrypt the raw blocks. The real heart of the change is in creating a list of blocks that might need decrypting and then in safe_read() lookup the blocks requested in that list and decrypt them if necessary. Unfortunately the lookup is necessary since if a block is unencrypted, like ifo files, then the decryption operation corrupts the data. Signed-off-by: Lawrence Rust <lvr@softsystem.co.uk> --- mythtv/libs/libmythdvdnav/dvdread/dvd_input.c | 10 +- mythtv/libs/libmythdvdnav/dvdread/dvd_input.h | 3 +- mythtv/libs/libmythdvdnav/dvdread/dvd_reader.c | 10 +- mythtv/libs/libmythtv/dvdstream.cpp | 281 ++++++++++++++++++++++++ mythtv/libs/libmythtv/dvdstream.h | 51 +++++ mythtv/libs/libmythtv/libmythtv.pro | 4 + mythtv/libs/libmythtv/ringbuffer.cpp | 12 + 7 files changed, 360 insertions(+), 11 deletions(-) create mode 100644 mythtv/libs/libmythtv/dvdstream.cpp create mode 100644 mythtv/libs/libmythtv/dvdstream.h diff --git a/mythtv/libs/libmythdvdnav/dvdread/dvd_input.c b/mythtv/libs/libmythdvdnav/dvdread/dvd_input.c index 99cb76f..bf8d485 100644
a b 33 33 /* The function pointers that is the exported interface of this file. */ 34 34 dvd_input_t (*dvdinput_open) (const char *); 35 35 int (*dvdinput_close) (dvd_input_t); 36 int (*dvdinput_seek) (dvd_input_t, int );36 int (*dvdinput_seek) (dvd_input_t, int, int); 37 37 int (*dvdinput_title) (dvd_input_t, int); 38 38 int (*dvdinput_read) (dvd_input_t, void *, int, int); 39 39 char * (*dvdinput_error) (dvd_input_t); … … static char *css_error(dvd_input_t dev) 116 116 /** 117 117 * seek into the device. 118 118 */ 119 static int css_seek(dvd_input_t dev, int blocks )119 static int css_seek(dvd_input_t dev, int blocks, int flags) 120 120 { 121 /* DVDINPUT_NOFLAGS should match the DVDCSS_NOFLAGS value. */ 122 return DVDcss_seek(dev->dvdcss, blocks, DVDINPUT_NOFLAGS); 121 return DVDcss_seek(dev->dvdcss, blocks, flags); 123 122 } 124 123 125 124 /** … … static char *file_error(dvd_input_t dev) 196 195 /** 197 196 * seek into the device. 198 197 */ 199 static int file_seek(dvd_input_t dev, int blocks )198 static int file_seek(dvd_input_t dev, int blocks, int flags) 200 199 { 201 200 off_t pos; 201 (void)flags; 202 202 203 203 pos = mythfile_seek(dev->fd, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN, SEEK_SET); 204 204 if(pos < 0) { -
mythtv/libs/libmythdvdnav/dvdread/dvd_input.h
diff --git a/mythtv/libs/libmythdvdnav/dvdread/dvd_input.h b/mythtv/libs/libmythdvdnav/dvdread/dvd_input.h index 208512e..73b0c95 100644
a b 28 28 #define DVDINPUT_NOFLAGS 0 29 29 30 30 #define DVDINPUT_READ_DECRYPT (1 << 0) 31 #define DVDCSS_SEEK_KEY (1 << 1) 31 32 32 33 typedef struct dvd_input_s *dvd_input_t; 33 34 … … typedef struct dvd_input_s *dvd_input_t; 50 51 */ 51 52 extern dvd_input_t (*dvdinput_open) (const char *); 52 53 extern int (*dvdinput_close) (dvd_input_t); 53 extern int (*dvdinput_seek) (dvd_input_t, int );54 extern int (*dvdinput_seek) (dvd_input_t, int, int); 54 55 extern int (*dvdinput_title) (dvd_input_t, int); 55 56 extern int (*dvdinput_read) (dvd_input_t, void *, int, int); 56 57 extern char * (*dvdinput_error) (dvd_input_t); -
mythtv/libs/libmythdvdnav/dvdread/dvd_reader.c
diff --git a/mythtv/libs/libmythdvdnav/dvdread/dvd_reader.c b/mythtv/libs/libmythdvdnav/dvdread/dvd_reader.c index fd00d6c..69f3a14 100644
a b int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number, 1118 1118 return 0; 1119 1119 } 1120 1120 1121 ret = dvdinput_seek( device->dev, (int) lb_number );1121 ret = dvdinput_seek( device->dev, (int) lb_number, encrypted & DVDCSS_SEEK_KEY ); 1122 1122 if( ret != (int) lb_number ) { 1123 1123 fprintf( stderr, "libdvdread: Can't seek to block %u\n", lb_number ); 1124 1124 return 0; 1125 1125 } 1126 1126 1127 1127 ret = dvdinput_read( device->dev, (char *) data, 1128 (int) block_count, encrypted );1128 (int) block_count, encrypted & DVDINPUT_READ_DECRYPT ); 1129 1129 return ret; 1130 1130 } 1131 1131 … … static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset, 1163 1163 1164 1164 if( offset < dvd_file->title_sizes[ i ] ) { 1165 1165 if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) { 1166 off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );1166 off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset, DVDINPUT_NOFLAGS ); 1167 1167 if( off < 0 || off != (int)offset ) { 1168 1168 fprintf( stderr, "libdvdread: Can't seek to block %d\n", 1169 1169 offset ); … … static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset, 1178 1178 * (This is only true if you try and read >1GB at a time) */ 1179 1179 1180 1180 /* Read part 1 */ 1181 off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );1181 off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset, DVDINPUT_NOFLAGS ); 1182 1182 if( off < 0 || off != (int)offset ) { 1183 1183 fprintf( stderr, "libdvdread: Can't seek to block %d\n", 1184 1184 offset ); … … static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset, 1195 1195 return ret; 1196 1196 1197 1197 /* Read part 2 */ 1198 off = dvdinput_seek( dvd_file->title_devs[ i + 1 ], 0 );1198 off = dvdinput_seek( dvd_file->title_devs[ i + 1 ], 0, DVDINPUT_NOFLAGS ); 1199 1199 if( off < 0 || off != 0 ) { 1200 1200 fprintf( stderr, "libdvdread: Can't seek to block %d\n", 1201 1201 0 ); -
new file mythtv/libs/libmythtv/dvdstream.cpp
diff --git a/mythtv/libs/libmythtv/dvdstream.cpp b/mythtv/libs/libmythtv/dvdstream.cpp new file mode 100644 index 0000000..ba2aa97
- + 1 /* DVD stream 2 * Copyright 2011 Lawrence Rust <lvr at softsystem dot co dot uk> 3 */ 4 #include "dvdstream.h" 5 6 #include <stdio.h> 7 8 #include <QMutexLocker> 9 #include <QtAlgorithms> 10 11 #include "dvdread/dvd_reader.h" 12 #include "dvdread/dvd_udf.h" // for UDFFindFile 13 extern "C" { 14 #include "dvdread/dvd_input.h" // for DVDINPUT_READ_DECRYPT & DVDCSS_SEEK_KEY 15 } 16 17 #include "mythlogging.h" 18 19 20 // A range of block numbers 21 class DVDStream::BlockRange 22 { 23 uint32_t start, end; 24 int title; 25 26 public: 27 BlockRange(uint32_t b, uint32_t n, int t) : start(b), end(b+n), title(t) { } 28 29 bool operator < (const BlockRange& rhs) const { return end <= rhs.start; } 30 31 uint32_t Start() const { return start; } 32 uint32_t End() const { return end; } 33 int Title() const { return title; } 34 }; 35 36 37 // Private but located in/shared with dvd_reader.c 38 extern "C" int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number, 39 size_t block_count, unsigned char *data, 40 int encrypted ); 41 42 43 // Roundup bytes to DVD blocks 44 inline uint32_t Len2Blocks(uint32_t len) 45 { 46 return (len + (DVD_VIDEO_LB_LEN - 1)) / DVD_VIDEO_LB_LEN; 47 } 48 49 DVDStream::DVDStream(const QString& filename) 50 : RingBuffer(kRingBuffer_File), m_reader(0), m_start(0), m_pos(0), m_title(-1) 51 { 52 OpenFile(filename); 53 } 54 55 DVDStream::~DVDStream() 56 { 57 rwlock.lockForWrite(); 58 59 if (m_reader) 60 DVDClose(m_reader); 61 62 rwlock.unlock(); 63 } 64 65 bool DVDStream::OpenFile(const QString &filename, uint /*retry_ms*/) 66 { 67 rwlock.lockForWrite(); 68 69 const QString root = filename.section("/VIDEO_TS/", 0, 0); 70 const QString path = filename.section(root, 1); 71 72 if (m_reader) 73 DVDClose(m_reader); 74 75 m_reader = DVDOpen(qPrintable(root)); 76 if (!m_reader) 77 { 78 LOG(VB_GENERAL, LOG_ERR, QString("DVDStream DVDOpen(%1) failed").arg(filename)); 79 rwlock.unlock(); 80 return false; 81 } 82 83 if (!path.isEmpty()) 84 { 85 // Locate the start block of the requested title 86 uint32_t len; 87 m_start = UDFFindFile(m_reader, const_cast<char*>(qPrintable(path)), &len); 88 if (m_start == 0) 89 { 90 LOG(VB_GENERAL, LOG_ERR, QString("DVDStream(%1) UDFFindFile(%2) failed"). 91 arg(root).arg(path)); 92 DVDClose(m_reader); 93 m_reader = 0; 94 rwlock.unlock(); 95 return false; 96 } 97 else 98 { 99 m_list.append(BlockRange(0, Len2Blocks(len), 0)); 100 } 101 } 102 else 103 { 104 // Create a list of the possibly encrypted files 105 uint32_t len, start; 106 107 // Root menu 108 char name[64] = "VIDEO_TS/VIDEO_TS.VOB"; 109 start = UDFFindFile(m_reader, name, &len); 110 if( start != 0 && len != 0 ) 111 m_list.append(BlockRange(start, Len2Blocks(len), 0)); 112 113 const int kTitles = 100; 114 for ( int title = 1; title < kTitles; ++title) 115 { 116 // Menu 117 snprintf(name, sizeof name, "/VIDEO_TS/VTS_%02d_0.VOB", title); 118 start = UDFFindFile(m_reader, name, &len); 119 if( start != 0 && len != 0 ) 120 m_list.append(BlockRange(start, Len2Blocks(len), title)); 121 122 for ( int part = 1; part < 10; ++part) 123 { 124 // A/V track 125 snprintf(name, sizeof name, "/VIDEO_TS/VTS_%02d_%d.VOB", title, part); 126 start = UDFFindFile(m_reader, name, &len); 127 if( start != 0 && len != 0 ) 128 m_list.append(BlockRange(start, Len2Blocks(len), title + part * kTitles)); 129 } 130 } 131 132 qSort( m_list); 133 134 // Open the root menu so that CSS keys are generated now 135 dvd_file_t *file = DVDOpenFile(m_reader, 0, DVD_READ_MENU_VOBS); 136 if (file) 137 DVDCloseFile(file); 138 else 139 LOG(VB_GENERAL, LOG_ERR, "DVDStream DVDOpenFile(VOBS_1) failed"); 140 } 141 142 rwlock.unlock(); 143 return true; 144 } 145 146 //virtual 147 bool DVDStream::IsOpen(void) const 148 { 149 rwlock.lockForRead(); 150 bool ret = m_reader != 0; 151 rwlock.unlock(); 152 return ret; 153 } 154 155 //virtual 156 int DVDStream::safe_read(void *data, uint size) 157 { 158 uint32_t lb = size / DVD_VIDEO_LB_LEN; 159 if (lb < 1) 160 { 161 LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read too small"); 162 return -1; 163 } 164 165 if (!m_reader) 166 return -1; 167 168 int ret = 0; 169 170 // Are any blocks in the range encrypted? 171 list_t::const_iterator it = qBinaryFind(m_list, BlockRange(m_pos, lb, -1)); 172 uint32_t b = it == m_list.end() ? lb : m_pos < it->Start() ? it->Start() - m_pos : 0; 173 if (b) 174 { 175 // Read the beginning unencrypted blocks 176 ret = UDFReadBlocksRaw(m_reader, m_pos, b, (unsigned char*)data, DVDINPUT_NOFLAGS); 177 if (ret == -1) 178 { 179 LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read DVDReadBlocks error"); 180 return -1; 181 } 182 183 m_pos += ret; 184 lb -= ret; 185 if (it == m_list.end()) 186 return ret * DVD_VIDEO_LB_LEN; 187 188 data = (unsigned char*)data + ret * DVD_VIDEO_LB_LEN; 189 } 190 191 b = it->End() - m_pos; 192 if (b > lb) 193 b = lb; 194 195 // Request new key if change in title 196 int flags = DVDINPUT_READ_DECRYPT; 197 if (it->Title() != m_title) 198 { 199 m_title = it->Title(); 200 flags |= DVDCSS_SEEK_KEY; 201 } 202 203 // Read the encrypted blocks 204 int ret2 = UDFReadBlocksRaw(m_reader, m_pos + m_start, b, (unsigned char*)data, flags); 205 if (ret2 == -1) 206 { 207 LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read DVDReadBlocks error"); 208 m_title = -1; 209 return -1; 210 } 211 212 m_pos += ret2; 213 ret += ret2; 214 lb -= ret2; 215 data = (unsigned char*)data + ret2 * DVD_VIDEO_LB_LEN; 216 217 if (lb > 0 && m_start == 0) 218 { 219 // Read the last unencrypted blocks 220 ret2 = UDFReadBlocksRaw(m_reader, m_pos, lb, (unsigned char*)data, DVDINPUT_NOFLAGS); 221 if (ret2 == -1) 222 { 223 LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read DVDReadBlocks error"); 224 return -1; 225 } 226 227 m_pos += ret2; 228 ret += ret2;; 229 } 230 231 return ret * DVD_VIDEO_LB_LEN; 232 } 233 234 //virtual 235 long long DVDStream::Seek(long long pos, int whence, bool has_lock) 236 { 237 if (!m_reader) 238 return -1; 239 240 if (SEEK_END == whence) 241 { 242 errno = EINVAL; 243 return -1; 244 } 245 246 uint32_t lb = pos / DVD_VIDEO_LB_LEN; 247 if ((qlonglong)lb * DVD_VIDEO_LB_LEN != pos) 248 { 249 LOG(VB_GENERAL, LOG_ERR, "DVDStream::Seek not block aligned"); 250 return -1; 251 } 252 253 // lockForWrite takes priority over lockForRead, so this will 254 // take priority over the lockForRead in the read ahead thread. 255 if (!has_lock) 256 rwlock.lockForWrite(); 257 258 poslock.lockForWrite(); 259 260 m_pos = lb; 261 262 poslock.unlock(); 263 264 generalWait.wakeAll(); 265 266 if (!has_lock) 267 rwlock.unlock(); 268 269 return pos; 270 } 271 272 //virtual 273 long long DVDStream::GetReadPosition(void) const 274 { 275 poslock.lockForRead(); 276 long long ret = (long long)m_pos * DVD_VIDEO_LB_LEN; 277 poslock.unlock(); 278 return ret; 279 } 280 281 // End of dvdstream,.cpp -
new file mythtv/libs/libmythtv/dvdstream.h
diff --git a/mythtv/libs/libmythtv/dvdstream.h b/mythtv/libs/libmythtv/dvdstream.h new file mode 100644 index 0000000..293a69d
- + 1 /* DVD stream 2 * Copyright 2011 Lawrence Rust <lvr at softsystem dot co dot uk> 3 */ 4 #ifndef DVDSTREAM_H 5 #define DVDSTREAM_H 6 7 #include <stdint.h> 8 9 #include <QString> 10 #include <QList> 11 12 #include "ringbuffer.h" 13 14 typedef struct dvd_reader_s dvd_reader_t; 15 16 17 /** 18 * Stream content from a DVD image file 19 */ 20 class MTV_PUBLIC DVDStream : public RingBuffer 21 { 22 Q_DISABLE_COPY(DVDStream) 23 24 public: 25 DVDStream(const QString&); 26 virtual ~DVDStream(); 27 28 public: 29 // RingBuffer methods 30 virtual long long GetReadPosition(void) const; 31 virtual bool IsOpen(void) const; 32 virtual bool OpenFile(const QString &lfilename, uint retry_ms = 0); 33 virtual long long Seek(long long pos, int whence, bool has_lock); 34 35 protected: 36 virtual int safe_read(void *data, uint sz); 37 38 // Implementation 39 private: 40 dvd_reader_t *m_reader; 41 uint32_t m_start; 42 43 class BlockRange; 44 typedef QList<BlockRange> list_t; 45 list_t m_list; // List of possibly encryoted block ranges 46 47 uint32_t m_pos; // Current read position (blocks) 48 int m_title; // Last title decrypted 49 }; 50 51 #endif /* ndef DVDSTREAM_H */ -
mythtv/libs/libmythtv/libmythtv.pro
diff --git a/mythtv/libs/libmythtv/libmythtv.pro b/mythtv/libs/libmythtv/libmythtv.pro index fcf1170..5b12711 100644
a b HEADERS += channelscan/iptvchannelfetcher.h 237 237 SOURCES += channelscan/scaninfo.cpp channelscan/channelimporter.cpp 238 238 SOURCES += channelscan/iptvchannelfetcher.cpp 239 239 240 HEADERS += dvdstream.h 241 SOURCES += dvdstream.cpp 242 240 243 # subtitles: srt 241 244 HEADERS += srtwriter.h 242 245 SOURCES += srtwriter.cpp … … INSTALLS += inc 249 252 250 253 #DVD stuff 251 254 DEPENDPATH += ../libmythdvdnav/ 255 DEPENDPATH += ../libmythdvdnav/dvdread # for dvd_reader.h & dvd_input.h 252 256 INCLUDEPATH += ../libmythdvdnav 253 257 POST_TARGETDEPS += ../libmythdvdnav/libmythdvdnav-$${MYTH_LIB_EXT} 254 258 HEADERS += DVD/dvdringbuffer.h -
mythtv/libs/libmythtv/ringbuffer.cpp
diff --git a/mythtv/libs/libmythtv/ringbuffer.cpp b/mythtv/libs/libmythtv/ringbuffer.cpp index b4f0e76..cff6e4d 100644
a b 17 17 #include "ThreadedFileWriter.h" 18 18 #include "fileringbuffer.h" 19 19 #include "streamingringbuffer.h" 20 #include "dvdstream.h" 20 21 #include "livetvchain.h" 21 22 #include "mythcontext.h" 22 23 #include "ringbuffer.h" … … RingBuffer *RingBuffer::Create( 171 172 return new BDRingBuffer(lfilename); 172 173 } 173 174 175 if (!mythurl && dvdext) 176 { 177 LOG(VB_PLAYBACK, LOG_INFO, "ISO image at " + lfilename); 178 return new DVDStream(lfilename); 179 } 180 if (!mythurl && lower.endsWith(".vob") && lfilename.contains("/VIDEO_TS/")) 181 { 182 LOG(VB_PLAYBACK, LOG_INFO, "DVD VOB at " + lfilename); 183 return new DVDStream(lfilename); 184 } 185 174 186 return new FileRingBuffer( 175 187 lfilename, write, usereadahead, timeout_ms); 176 188 }
