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 stuartm, 7 years ago)

Updated patch against current master?

  • 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  
    3333/* The function pointers that is the exported interface of this file. */
    3434dvd_input_t (*dvdinput_open)  (const char *);
    3535int         (*dvdinput_close) (dvd_input_t);
    36 int         (*dvdinput_seek)  (dvd_input_t, int);
     36int         (*dvdinput_seek)  (dvd_input_t, int, int);
    3737int         (*dvdinput_title) (dvd_input_t, int);
    3838int         (*dvdinput_read)  (dvd_input_t, void *, int, int);
    3939char *      (*dvdinput_error) (dvd_input_t);
    static char *css_error(dvd_input_t dev) 
    116116/**
    117117 * seek into the device.
    118118 */
    119 static int css_seek(dvd_input_t dev, int blocks)
     119static int css_seek(dvd_input_t dev, int blocks, int flags)
    120120{
    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);
    123122}
    124123
    125124/**
    static char *file_error(dvd_input_t dev) 
    196195/**
    197196 * seek into the device.
    198197 */
    199 static int file_seek(dvd_input_t dev, int blocks)
     198static int file_seek(dvd_input_t dev, int blocks, int flags)
    200199{
    201200  off_t pos;
     201  (void)flags;
    202202
    203203  pos = mythfile_seek(dev->fd, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN, SEEK_SET);
    204204  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  
    2828#define DVDINPUT_NOFLAGS         0
    2929
    3030#define DVDINPUT_READ_DECRYPT    (1 << 0)
     31#define DVDCSS_SEEK_KEY          (1 << 1)
    3132
    3233typedef struct dvd_input_s *dvd_input_t;
    3334
    typedef struct dvd_input_s *dvd_input_t; 
    5051 */
    5152extern dvd_input_t (*dvdinput_open)  (const char *);
    5253extern int         (*dvdinput_close) (dvd_input_t);
    53 extern int         (*dvdinput_seek)  (dvd_input_t, int);
     54extern int         (*dvdinput_seek)  (dvd_input_t, int, int);
    5455extern int         (*dvdinput_title) (dvd_input_t, int);
    5556extern int         (*dvdinput_read)  (dvd_input_t, void *, int, int);
    5657extern 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, 
    11181118    return 0;
    11191119  }
    11201120
    1121   ret = dvdinput_seek( device->dev, (int) lb_number );
     1121  ret = dvdinput_seek( device->dev, (int) lb_number, encrypted & DVDCSS_SEEK_KEY );
    11221122  if( ret != (int) lb_number ) {
    11231123    fprintf( stderr, "libdvdread: Can't seek to block %u\n", lb_number );
    11241124    return 0;
    11251125  }
    11261126
    11271127  ret = dvdinput_read( device->dev, (char *) data,
    1128                        (int) block_count, encrypted );
     1128                       (int) block_count, encrypted & DVDINPUT_READ_DECRYPT );
    11291129  return ret;
    11301130}
    11311131
    static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset, 
    11631163
    11641164    if( offset < dvd_file->title_sizes[ i ] ) {
    11651165      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 );
    11671167        if( off < 0 || off != (int)offset ) {
    11681168          fprintf( stderr, "libdvdread: Can't seek to block %d\n",
    11691169                   offset );
    static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset, 
    11781178         * (This is only true if you try and read >1GB at a time) */
    11791179
    11801180        /* 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 );
    11821182        if( off < 0 || off != (int)offset ) {
    11831183          fprintf( stderr, "libdvdread: Can't seek to block %d\n",
    11841184                   offset );
    static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset, 
    11951195          return ret;
    11961196
    11971197        /* 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 );
    11991199        if( off < 0 || off != 0 ) {
    12001200          fprintf( stderr, "libdvdread: Can't seek to block %d\n",
    12011201                   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
     13extern "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
     21class DVDStream::BlockRange
     22{
     23    uint32_t start, end;
     24    int title;
     25
     26public:
     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
     38extern "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
     44inline uint32_t Len2Blocks(uint32_t len)
     45{
     46    return (len + (DVD_VIDEO_LB_LEN - 1)) / DVD_VIDEO_LB_LEN;
     47}
     48
     49DVDStream::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
     55DVDStream::~DVDStream()
     56{
     57    rwlock.lockForWrite();
     58
     59    if (m_reader)
     60        DVDClose(m_reader);
     61
     62    rwlock.unlock();
     63}
     64
     65bool 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
     147bool DVDStream::IsOpen(void) const
     148{
     149    rwlock.lockForRead();
     150    bool ret = m_reader != 0;
     151    rwlock.unlock();
     152    return ret;
     153}
     154
     155//virtual
     156int 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
     235long 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
     273long 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
     14typedef struct dvd_reader_s dvd_reader_t;
     15
     16
     17/**
     18 * Stream content from a DVD image file
     19 */
     20class MTV_PUBLIC DVDStream : public RingBuffer
     21{
     22    Q_DISABLE_COPY(DVDStream)
     23
     24public:
     25    DVDStream(const QString&);
     26    virtual ~DVDStream();
     27
     28public:
     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
     35protected:
     36    virtual int safe_read(void *data, uint sz);
     37
     38    // Implementation
     39private:
     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 
    237237SOURCES += channelscan/scaninfo.cpp channelscan/channelimporter.cpp
    238238SOURCES += channelscan/iptvchannelfetcher.cpp
    239239
     240HEADERS += dvdstream.h
     241SOURCES += dvdstream.cpp
     242
    240243# subtitles: srt
    241244HEADERS += srtwriter.h
    242245SOURCES += srtwriter.cpp
    INSTALLS += inc 
    249252
    250253#DVD stuff
    251254DEPENDPATH  += ../libmythdvdnav/
     255DEPENDPATH  += ../libmythdvdnav/dvdread # for dvd_reader.h & dvd_input.h
    252256INCLUDEPATH += ../libmythdvdnav
    253257POST_TARGETDEPS += ../libmythdvdnav/libmythdvdnav-$${MYTH_LIB_EXT}
    254258HEADERS += 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  
    1717#include "ThreadedFileWriter.h"
    1818#include "fileringbuffer.h"
    1919#include "streamingringbuffer.h"
     20#include "dvdstream.h"
    2021#include "livetvchain.h"
    2122#include "mythcontext.h"
    2223#include "ringbuffer.h"
    RingBuffer *RingBuffer::Create( 
    171172        return new BDRingBuffer(lfilename);
    172173    }
    173174
     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
    174186    return new FileRingBuffer(
    175187        lfilename, write, usereadahead, timeout_ms);
    176188}