Ticket #10019: 0008-MythPlayer-Add-support-for-InteractiveTV-streams.patch

File 0008-MythPlayer-Add-support-for-InteractiveTV-streams.patch, 17.8 KB (added by Lawrence Rust <lvr@…>, 9 years ago)
  • new file mythtv/libs/libmythtv/icringbuffer.cpp

    From de6bdfee21eff3516bbb1eb857ca0cbbad106106 Mon Sep 17 00:00:00 2001
    From: Lawrence Rust <lvr@softsystem.co.uk>
    Date: Tue, 19 Jul 2011 14:49:12 +0200
    Subject: [PATCH 8/9] MythPlayer: Add support for InteractiveTV streams
    
    This patch adds functionality to MythPlayer to enable interactive TV content
    to select altenative streamed media for display.
    
    NB this patch needs to be applied together with that for InteractionChannel
    streaming.
    
    Signed-off-by: Lawrence Rust <lvr@softsystem.co.uk>
    ---
     mythtv/libs/libmythtv/icringbuffer.cpp  |  107 ++++++++++++++++++++
     mythtv/libs/libmythtv/icringbuffer.h    |   40 ++++++++
     mythtv/libs/libmythtv/interactivetv.cpp |   18 ++--
     mythtv/libs/libmythtv/interactivetv.h   |    2 +
     mythtv/libs/libmythtv/libmythtv.pro     |    6 +-
     mythtv/libs/libmythtv/mythplayer.cpp    |  165 ++++++++++++++++++++++++++++---
     mythtv/libs/libmythtv/mythplayer.h      |    7 ++
     mythtv/libs/libmythtv/ringbuffer.h      |    4 +
     8 files changed, 325 insertions(+), 24 deletions(-)
     create mode 100644 mythtv/libs/libmythtv/icringbuffer.cpp
     create mode 100644 mythtv/libs/libmythtv/icringbuffer.h
    
    diff --git a/mythtv/libs/libmythtv/icringbuffer.cpp b/mythtv/libs/libmythtv/icringbuffer.cpp
    new file mode 100644
    index 0000000..f099de2
    - +  
     1#include "icringbuffer.h"
     2
     3#include <stdio.h> // SEEK_SET
     4
     5#include <QScopedPointer>
     6#include <QWriteLocker>
     7
     8#include "netstream.h"
     9#include "mythlogging.h"
     10
     11
     12#define LOC QString("ICRingBuf ")
     13
     14
     15ICRingBuffer::ICRingBuffer(const QString &url, RingBuffer *parent)
     16  : RingBuffer(kRingBufferType), m_stream(0), m_parent(parent)
     17{
     18    startreadahead = true;
     19    OpenFile(url);
     20}
     21
     22ICRingBuffer::~ICRingBuffer()
     23{
     24    delete m_stream;
     25    delete m_parent;
     26}
     27
     28bool ICRingBuffer::IsOpen(void) const
     29{
     30    return m_stream ? m_stream->IsOpen() : false;
     31}
     32
     33bool ICRingBuffer::OpenFile(const QString &url, uint retry_ms)
     34{
     35    if (!NetStream::IsSupported(url))
     36    {
     37        LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unsupported URL %1").arg(url) );
     38        return false;
     39    }
     40
     41    QScopedPointer<NetStream> stream(new NetStream(url));
     42    if (!stream || !stream->IsOpen())
     43    {
     44        LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open %1").arg(url) );
     45        return false;
     46    }
     47
     48    if (!stream->WaitTillReady(30000))
     49    {
     50        LOG(VB_GENERAL, LOG_ERR, LOC + QString("Stream not ready%1").arg(url) );
     51        return false;
     52    }
     53
     54    if (m_parent)
     55        m_parent->Pause();
     56
     57    QWriteLocker locker(&rwlock);
     58
     59    safefilename = url;
     60    filename = url;
     61
     62    delete m_stream;
     63    m_stream = stream.take();
     64
     65    // The initial bitrate needs to be set with consideration for low bit rate
     66    // streams (e.g. radio @ 64Kbps) such that fill_min bytes are received
     67    // in a reasonable time period to enable decoders to peek the first few KB
     68    // to determine type & settings.
     69    rawbitrate = 128; // remotefile
     70    CalcReadAheadThresh();
     71
     72    locker.unlock();
     73    Reset(true, false, true);
     74
     75    LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened %1").arg(url));
     76    return true;
     77}
     78
     79long long ICRingBuffer::GetReadPosition(void) const
     80{
     81    return m_stream ? m_stream->GetReadPosition() : 0;
     82}
     83
     84long long ICRingBuffer::Seek(long long pos, int whence, bool has_lock)
     85{
     86    return m_stream ? (whence == SEEK_SET ? m_stream->Seek(pos) : -1) : -1;
     87}
     88
     89int ICRingBuffer::safe_read(void *data, uint sz)
     90{
     91    return m_stream ? m_stream->safe_read(data, sz, 10) : (ateof = true, 0);
     92}
     93
     94long long ICRingBuffer::GetRealFileSize(void) const
     95{
     96    return m_stream ? m_stream->GetSize() : -1;
     97}
     98
     99// Take ownership of parent RingBuffer
     100RingBuffer *ICRingBuffer::Take()
     101{
     102    RingBuffer *parent = m_parent;
     103    m_parent = 0;
     104    return parent;
     105}
     106
     107// End of file
  • new file mythtv/libs/libmythtv/icringbuffer.h

    diff --git a/mythtv/libs/libmythtv/icringbuffer.h b/mythtv/libs/libmythtv/icringbuffer.h
    new file mode 100644
    index 0000000..15d4b1c
    - +  
     1#ifndef ICRINGBUFFER_H
     2#define ICRINGBUFFER_H
     3
     4#include "ringbuffer.h"
     5
     6class NetStream;
     7
     8class ICRingBuffer : public RingBuffer
     9{
     10  public:
     11    static enum RingBufferType const kRingBufferType = kRingBuffer_MHEG;
     12
     13    ICRingBuffer(const QString &url, RingBuffer *parent = 0);
     14    virtual ~ICRingBuffer();
     15
     16    // RingBuffer implementation
     17    virtual bool IsOpen(void) const;
     18    virtual long long GetReadPosition(void) const;
     19    virtual bool OpenFile(const QString &url,
     20                          uint retry_ms = kDefaultOpenTimeout);
     21    virtual long long Seek(long long pos, int whence, bool has_lock);
     22    virtual long long GetRealFileSize(void) const;
     23    virtual bool IsStreamed(void)       { return true;  }
     24    virtual bool IsSeekingAllowed(void) { return false; }
     25    virtual bool IsBookmarkAllowed(void) { return false; }
     26
     27  protected:
     28    virtual int safe_read(void *data, uint sz);
     29
     30    // Operations
     31  public:
     32    // Take ownership of parent RingBuffer
     33    RingBuffer *Take();
     34
     35  private:
     36    NetStream *m_stream;
     37    RingBuffer *m_parent; // parent RingBuffer
     38};
     39
     40#endif // ICRINGBUFFER_H
  • mythtv/libs/libmythtv/interactivetv.cpp

    diff --git a/mythtv/libs/libmythtv/interactivetv.cpp b/mythtv/libs/libmythtv/interactivetv.cpp
    index 2cdbea0..d1efa87 100644
    a b InteractiveTV::InteractiveTV(MythPlayer *nvp) 
    1717{
    1818    Restart(0, 0, false);
    1919
    20     if (VERBOSE_LEVEL_CHECK(VB_MHEG, LOG_ANY))
    21     {
    22         MHSetLogging(stdout, MHLogAll);
    23     }
    24     else
    25     {
    26         MHSetLogging(stdout, MHLogError);
    27     }
     20    MHSetLogging(stdout,
     21        VERBOSE_LEVEL_CHECK(VB_MHEG, LOG_DEBUG) ? MHLogAll :
     22        VERBOSE_LEVEL_CHECK(VB_MHEG, LOG_ANY) ?
     23            MHLogError | MHLogWarning | MHLogNotifications /*| MHLogLinks | MHLogActions | MHLogDetail*/ :
     24        MHLogError | MHLogWarning );
    2825}
    2926
    3027InteractiveTV::~InteractiveTV()
    void InteractiveTV::SetNetBootInfo(const unsigned char *data, uint length) 
    7976{
    8077    m_context->SetNetBootInfo(data, length);
    8178}
     79
     80bool InteractiveTV::StreamStarted(bool bStarted)
     81{
     82    return m_context->StreamStarted(bStarted);
     83}
  • mythtv/libs/libmythtv/interactivetv.h

    diff --git a/mythtv/libs/libmythtv/interactivetv.h b/mythtv/libs/libmythtv/interactivetv.h
    index c8e00e6..ba426de 100644
    a b class InteractiveTV 
    3939
    4040    // Get the initial component tags.
    4141    void GetInitialStreams(int &audioTag, int &videoTag);
     42    // Called when a stream starts or stops. Returns true if event is handled
     43    bool StreamStarted(bool bStarted = true);
    4244
    4345    MythPlayer *GetNVP(void) { return m_nvp; }
    4446
  • mythtv/libs/libmythtv/libmythtv.pro

    diff --git a/mythtv/libs/libmythtv/libmythtv.pro b/mythtv/libs/libmythtv/libmythtv.pro
    index a67539b..8e40e03 100644
    a b HEADERS += mythsystemevent.h 
    164164HEADERS += avfringbuffer.h          ThreadedFileWriter.h
    165165HEADERS += ringbuffer.h             fileringbuffer.h
    166166HEADERS += dvdringbuffer.h          bdringbuffer.h
    167 HEADERS += streamingringbuffer.h    metadataimagehelper.h
     167HEADERS += streamingringbuffer.h    icringbuffer.h
     168HEADERS += metadataimagehelper.h
    168169
    169170SOURCES += recordinginfo.cpp
    170171SOURCES += dbcheck.cpp
    SOURCES += mythsystemevent.cpp 
    192193SOURCES += avfringbuffer.cpp        ThreadedFileWriter.cpp
    193194SOURCES += ringbuffer.cpp           fileringBuffer.cpp
    194195SOURCES += dvdringbuffer.cpp        bdringbuffer.cpp
    195 SOURCES += streamingringbuffer.cpp  metadataimagehelper.cpp
     196SOURCES += streamingringbuffer.cpp  icringbuffer.cpp
     197SOURCES += metadataimagehelper.cpp
    196198
    197199# DiSEqC
    198200HEADERS += diseqc.h                 diseqcsettings.h
  • mythtv/libs/libmythtv/mythplayer.cpp

    diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp
    index 3e2966c..0523d77 100644
    a b using namespace std; 
    2626#include <QCoreApplication>
    2727#include <QKeyEvent>
    2828#include <QDir>
     29#include <QScopedPointer>
    2930
    3031// MythTV headers
    3132#include "mthread.h"
    using namespace std; 
    5960#include "mythimage.h"
    6061#include "mythuiimage.h"
    6162#include "mythlogging.h"
     63#include "icringbuffer.h"
    6264
    6365extern "C" {
    6466#include "vbitext/vbi.h"
    int MythPlayer::OpenFile(uint retries, bool allow_libmpeg2) 
    916918        MythTimer peekTimer; peekTimer.start();
    917919        while (player_ctx->buffer->Peek(testbuf, testreadsize) != testreadsize)
    918920        {
    919             if (peekTimer.elapsed() > 1000 || bigTimer.elapsed() > timeout)
     921            // NB need to allow for streams encountering network congestion
     922            if (peekTimer.elapsed() > 30000 || bigTimer.elapsed() > timeout)
    920923            {
    921924                LOG(VB_GENERAL, LOG_ERR, LOC +
    922925                    QString("OpenFile(): Could not read first %1 bytes of '%2'")
    int MythPlayer::OpenFile(uint retries, bool allow_libmpeg2) 
    926929                return -1;
    927930            }
    928931            LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenFile() waiting on data");
    929             usleep(50 * 1000);
     932            usleep(150 * 1000);
    930933        }
    931934
    932935        player_ctx->LockPlayingInfo(__FILE__, __LINE__);
    void MythPlayer::DisplayPauseFrame(void) 
    19371940    SetBuffering(false);
    19381941
    19391942    RefreshPauseFrame();
     1943    PreProcessNormalFrame(); // Allow interactiveTV to draw on pause frame
    19401944
    19411945    osdLock.lock();
    19421946    videofiltersLock.lock();
    bool MythPlayer::PrebufferEnoughFrames(int min_buffers) 
    20012005            // to recover from serious problems if frames get leaked.
    20022006            DiscardVideoFrames(true);
    20032007        }
    2004         if (waited_for > 20000) // 20 seconds
     2008        if (waited_for > 30000) // 30 seconds for internet streamed media
    20052009        {
    20062010            LOG(VB_GENERAL, LOG_ERR, LOC +
    20072011                "Waited too long for decoder to fill video buffers. Exiting..");
    void MythPlayer::SwitchToProgram(void) 
    23302334        return;
    23312335    }
    23322336
     2337    if (player_ctx->buffer->GetType() == ICRingBuffer::kRingBufferType)
     2338    {
     2339        // Restore original ringbuffer
     2340        ICRingBuffer *ic = dynamic_cast< ICRingBuffer* >(player_ctx->buffer);
     2341        player_ctx->buffer = ic->Take();
     2342        delete ic;
     2343    }
     2344
    23332345    player_ctx->buffer->OpenFile(
    23342346        pginfo->GetPlaybackURL(), RingBuffer::kLiveTVOpenTimeout);
    23352347
    void MythPlayer::JumpToProgram(void) 
    24372449
    24382450    SendMythSystemPlayEvent("PLAY_CHANGED", pginfo);
    24392451
     2452    if (player_ctx->buffer->GetType() == ICRingBuffer::kRingBufferType)
     2453    {
     2454        // Restore original ringbuffer
     2455        ICRingBuffer *ic = dynamic_cast< ICRingBuffer* >(player_ctx->buffer);
     2456        player_ctx->buffer = ic->Take();
     2457        delete ic;
     2458    }
     2459
    24402460    player_ctx->buffer->OpenFile(
    24412461        pginfo->GetPlaybackURL(), RingBuffer::kLiveTVOpenTimeout);
    24422462
    void MythPlayer::EventLoop(void) 
    26232643        JumpToProgram();
    26242644    }
    26252645
     2646    // Change interactive stream if requested
     2647    { QMutexLocker locker(&itvLock);
     2648    if (!m_newStream.isEmpty())
     2649    {
     2650        QString stream = m_newStream;
     2651        m_newStream.clear();
     2652        locker.unlock();
     2653        JumpToStream(stream);
     2654    }}
     2655
    26262656    // Disable fastforward if we are too close to the end of the buffer
    26272657    if (ffrew_skip > 1 && (CalcMaxFFTime(100, false) < 100))
    26282658    {
    void MythPlayer::EventLoop(void) 
    26592689    }
    26602690
    26612691    // Handle end of file
    2662     if (GetEof())
     2692    if (GetEof() && !allpaused)
    26632693    {
    2664         if (player_ctx->tvchain)
     2694        if (interactiveTV && interactiveTV->StreamStarted(false))
    26652695        {
    2666             if (!allpaused && player_ctx->tvchain->HasNext())
    2667             {
    2668                 LOG(VB_GENERAL, LOG_NOTICE, LOC + "LiveTV forcing JumpTo 1");
    2669                 player_ctx->tvchain->JumpToNext(true, 1);
    2670                 return;
    2671             }
     2696            Pause();
     2697            return;
    26722698        }
    2673         else if (!allpaused)
     2699
     2700        if (player_ctx->tvchain && player_ctx->tvchain->HasNext())
    26742701        {
    2675             SetPlaying(false);
     2702            LOG(VB_GENERAL, LOG_NOTICE, LOC + "LiveTV forcing JumpTo 1");
     2703            player_ctx->tvchain->JumpToNext(true, 1);
    26762704            return;
    26772705        }
     2706
     2707        SetPlaying(false);
     2708        return;
    26782709    }
    26792710
    26802711    // Handle rewind
    void MythPlayer::UnpauseDecoder(void) 
    28052836
    28062837    int tries = 0;
    28072838    unpauseDecoder = true;
    2808     while (decoderThread && !killdecoder && (tries++ < 100) &&
     2839    while (decoderPaused && decoderThread && !killdecoder && (tries++ < 10) &&
    28092840          !decoderThreadUnpause.wait(&decoderPauseLock, 100))
    28102841    {
    28112842        LOG(VB_GENERAL, LOG_WARNING, LOC +
    bool MythPlayer::SetVideoByComponentTag(int tag) 
    46414672    return false;
    46424673}
    46434674
     4675// Called from MHIContext::Begin/End/Stream on the MHIContext::StartMHEGEngine thread
     4676bool MythPlayer::SetStream(const QString &stream)
     4677{
     4678    // The stream name is empty if the stream is closing
     4679    LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetStream '%1'").arg(stream));
     4680
     4681    QMutexLocker locker(&itvLock);
     4682    m_newStream = stream;
     4683    m_newStream.detach();
     4684    // Stream will be changed by JumpToStream called from EventLoop
     4685    // If successful will call interactiveTV->StreamStarted();
     4686    return !stream.isEmpty();
     4687}
     4688
     4689// Called from EventLoop pn the main application thread
     4690void MythPlayer::JumpToStream(const QString &stream)
     4691{
     4692    LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToStream - begin");
     4693
     4694    if (stream.isEmpty())
     4695        return; // Shouldn't happen
     4696
     4697    Pause();
     4698    ResetCaptions();
     4699
     4700    if (player_ctx->buffer->GetType() != ICRingBuffer::kRingBufferType)
     4701        player_ctx->buffer = new ICRingBuffer(stream, player_ctx->buffer);
     4702    else
     4703        player_ctx->buffer->OpenFile(stream);
     4704
     4705    if (!player_ctx->buffer->IsOpen())
     4706    {
     4707        LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToStream buffer OpenFile failed");
     4708        SetEof(true);
     4709        SetErrored(QObject::tr("Error opening remote stream buffer"));
     4710        return;
     4711    }
     4712
     4713    if (OpenFile(120) < 0) // 120 retries ~= 60 seconds
     4714    {
     4715        LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToStream OpenFile failed.");
     4716        SetEof(true);
     4717        SetErrored(QObject::tr("Error opening remote stream"));
     4718        return;
     4719    }
     4720
     4721    SetEof(false);
     4722
     4723    if (player_ctx->tvchain) CheckTVChain();
     4724    //player_ctx->buffer->IgnoreLiveEOF(false);
     4725
     4726    Play();
     4727    ChangeSpeed();
     4728
     4729    player_ctx->SetPlayerChangingBuffers(false);
     4730    if (interactiveTV) interactiveTV->StreamStarted();
     4731
     4732    LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToStream - end");
     4733}
     4734
     4735static inline int SafeFPS(DecoderBase *decoder)
     4736{
     4737    if (!decoder)
     4738        return 25;
     4739    double fps = decoder->GetFPS();
     4740    return fps > 0 ? (int)(fps + 0.5) : 25;
     4741}
     4742
     4743long MythPlayer::GetStreamPos()
     4744{
     4745    return (1000L * GetFramesPlayed()) / SafeFPS(decoder);
     4746}
     4747
     4748long MythPlayer::GetStreamMaxPos()
     4749{
     4750    uint kbps = decoder ? decoder->GetRawBitrate() : 0;
     4751    long pos = GetStreamPos(), maxpos = 0;
     4752    long long len = player_ctx->buffer->GetRealFileSize();
     4753    if (len > 0)
     4754    {
     4755        const uint kMin = 64;
     4756        maxpos = (long)((8 * len) / (kbps < kMin ? kMin : kbps));
     4757    }
     4758
     4759    if (maxpos < pos)
     4760        maxpos = pos;
     4761    LOG(VB_PLAYBACK, LOG_INFO, LOC +
     4762        QString("GetStreamMaxPos => %1 mS (%2 KB @ %3 KBPS)")
     4763        .arg(maxpos).arg(len > 0 ? len/1024 : len).arg((kbps+4)/8) );
     4764    return maxpos;
     4765}
     4766
     4767long MythPlayer::SetStreamPos(long ms)
     4768{
     4769    LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetStreamPos %1").arg(ms));
     4770    return JumpToFrame((uint64_t)((ms * SafeFPS(decoder)) / 1000));
     4771}
     4772
     4773void MythPlayer::StreamPlay(bool play)
     4774{
     4775    if (play)
     4776        Play();
     4777    else
     4778        Pause();
     4779}
     4780
    46444781/** \fn MythPlayer::SetDecoder(DecoderBase*)
    46454782 *  \brief Sets the stream decoder, deleting any existing recorder.
    46464783 */
  • mythtv/libs/libmythtv/mythplayer.h

    diff --git a/mythtv/libs/libmythtv/mythplayer.h b/mythtv/libs/libmythtv/mythplayer.h
    index daeb4dc..e4eae0b 100644
    a b class MTV_PUBLIC MythPlayer 
    259259    // Public MHEG/MHI stream selection
    260260    bool SetAudioByComponentTag(int tag);
    261261    bool SetVideoByComponentTag(int tag);
     262    bool SetStream(const QString &);
     263    long GetStreamPos(); // mS
     264    long GetStreamMaxPos(); // mS
     265    long SetStreamPos(long); // mS
     266    void StreamPlay(bool play = true);
    262267
    263268    // LiveTV public stuff
    264269    void CheckTVChain();
    class MTV_PUBLIC MythPlayer 
    524529    // Private LiveTV stuff
    525530    void  SwitchToProgram(void);
    526531    void  JumpToProgram(void);
     532    void  JumpToStream(const QString&);
    527533
    528534    void calcSliderPosPriv(osdInfo &info, bool paddedFields,
    529535                           int playbackLen, float secsplayed, bool islive);
    class MTV_PUBLIC MythPlayer 
    660666    InteractiveTV *interactiveTV;
    661667    bool       itvEnabled;
    662668    QMutex     itvLock;
     669    QString    m_newStream; // Guarded by itvLock
    663670
    664671    // OSD stuff
    665672    OSD  *osd;
  • mythtv/libs/libmythtv/ringbuffer.h

    diff --git a/mythtv/libs/libmythtv/ringbuffer.h b/mythtv/libs/libmythtv/ringbuffer.h
    index 8dc633d..08a007c 100644
    a b enum RingBufferType 
    3838    kRingBuffer_DVD,
    3939    kRingBuffer_BD,
    4040    kRingBuffer_HTTP,
     41    kRingBuffer_MHEG
    4142};
    4243
    4344class MTV_PUBLIC RingBuffer : protected MThread
    4445{
     46    friend class ICRingBuffer;
     47
    4548  public:
    4649    static RingBuffer *Create(const QString &lfilename, bool write,
    4750                              bool usereadahead = true,
    class MTV_PUBLIC RingBuffer : protected MThread 
    8487    virtual bool IsBookmarkAllowed(void) { return true; }
    8588    virtual int  BestBufferSize(void)   { return 32768; }
    8689    static QString BitrateToString(uint64_t rate);
     90    RingBufferType GetType() const { return type; }
    8791
    8892    // DVD and bluray methods
    8993    bool IsDisc(void) const { return IsDVD() || IsBD(); }