Ticket #10019: 0001-MythPlayer-Improve-low-bit-rate-high-latency-stream-.patch

File 0001-MythPlayer-Improve-low-bit-rate-high-latency-stream-.patch, 15.0 KB (added by Lawrence Rust <lvr@…>, 8 years ago)
  • mythtv/libs/libmythtv/avformatdecoder.cpp

    From 831de7855973c206fad7af9b297c4f2739a3f9ea Mon Sep 17 00:00:00 2001
    From: Lawrence Rust <lvr@softsystem.co.uk>
    Date: Thu, 2 Jun 2011 12:55:13 +0200
    Subject: [PATCH 1/2] MythPlayer: Improve low bit rate / high latency stream playback
    
    DVB-S radio programs are low bit rate (64Kbps..256Kbps) and suffer
    occasional latency.
    
    MHEG interaction streams are internet sourced and often suffer congestion
    resulting in high latency for some packets.
    
    - Fix a number of issues to do with changing program to DVB-S radio and
      the lack of video stream causing garbage video display.
    
    - Make FileRingBuffer::safe_read retry if reading from a remote file.
      Low bit rate sources such as radio can have considerable latency.
    
    - FileRingBuffer::OpenFile reduce the initial rawbitrate from 8 Mbps to
      128 or 256 Kbps, otherwise considerable delay can be caused during
      radio program startup.
    
    - Increase the wait period in MythPlayer::OpenFile from 1 to 30 secs
      while reading the probe buffer. This allows for typical http stream
      startup latency.
    
    Signed-off-by: Lawrence Rust <lvr@softsystem.co.uk>
    ---
     mythtv/libs/libmythtv/avformatdecoder.cpp |   13 ++++
     mythtv/libs/libmythtv/fileringbuffer.cpp  |   48 +++++++++-----
     mythtv/libs/libmythtv/mythplayer.cpp      |  106 ++++++++++++++++++-----------
     mythtv/libs/libmythtv/tv_play.cpp         |    4 +-
     4 files changed, 113 insertions(+), 58 deletions(-)
    
    diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp
    index 89cc07b..0f6e4eb 100644
    a b bool AvFormatDecoder::GetFrame(DecodeType decodetype) 
    43134313                av_init_packet(pkt);
    43144314            }
    43154315
     4316            // NB av_read_frame will block until
     4317            // either a frame is read or an error occurs
     4318            // so MythPlayer::DecoderLoop will be unable to pause or stop
    43164319            int retval = 0;
    43174320            if (!ic || ((retval = av_read_frame(ic, pkt)) < 0))
    43184321            {
    bool AvFormatDecoder::GetFrame(DecodeType decodetype) 
    44224425            case CODEC_TYPE_AUDIO:
    44234426            {
    44244427                if (!ProcessAudioPacket(curstream, pkt, decodetype))
     4428                {
    44254429                    have_err = true;
     4430                    if (!hasVideo)
     4431                    {
     4432                        LOG(VB_PLAYBACK, LOG_INFO, LOC +
     4433                            "GetFrame: exiting due to audio decode error");
     4434                        av_free_packet(pkt);
     4435                        delete pkt;
     4436                        return false;
     4437                    }
     4438                }
    44264439                else
    44274440                    GenerateDummyVideoFrames();
    44284441                break;
  • mythtv/libs/libmythtv/fileringbuffer.cpp

    diff --git a/mythtv/libs/libmythtv/fileringbuffer.cpp b/mythtv/libs/libmythtv/fileringbuffer.cpp
    index 944c98d..1449883 100644
    a b bool FileRingBuffer::OpenFile(const QString &lfilename, uint retry_ms) 
    356356    commserror = false;
    357357    numfailures = 0;
    358358
    359     rawbitrate = 8000;
     359    // The initial bitrate needs to be set with consideration for low bit rate
     360    // streams (e.g. radio @ 64Kbps) such that fill_min bytes are received
     361    // in a reasonable time period to enable decoders to peek the first few KB
     362    // to determine type & settings.
     363    if (is_local)
     364        rawbitrate = 256; // Allow for radio
     365    else
     366        rawbitrate = 128; // remotefile
     367
    360368    CalcReadAheadThresh();
    361369
    362370    bool ok = fd2 >= 0 || remotefile;
    int FileRingBuffer::safe_read(int fd, void *data, uint sz) 
    487495 */
    488496int FileRingBuffer::safe_read(RemoteFile *rf, void *data, uint sz)
    489497{
    490     int ret = rf->Read(data, sz);
    491     if (ret < 0)
    492     {
    493         LOG(VB_GENERAL, LOG_ERR, LOC +
    494             "safe_read(RemoteFile* ...): read failed");
    495            
    496         poslock.lockForRead();
    497         rf->Seek(internalreadpos - readAdjust, SEEK_SET);
    498         poslock.unlock();
    499         numfailures++;
    500     }
    501     else if (ret == 0)
     498    for (int retries = 0; ; ++retries)
    502499    {
    503         LOG(VB_FILE, LOG_INFO, LOC +
    504             "safe_read(RemoteFile* ...): at EOF");
     500        int ret = rf->Read(data, sz);
     501        if (ret > 0)
     502            return ret;
     503        else if (ret < 0)
     504        {
     505            LOG(VB_GENERAL, LOG_ERR, LOC +
     506                "safe_read(RemoteFile* ...): read failed");
     507
     508            poslock.lockForRead();
     509            rf->Seek(internalreadpos - readAdjust, SEEK_SET);
     510            poslock.unlock();
     511            numfailures++;
     512            return ret;
     513        }
     514        // Retry for 300mS if liveTV for low bit rate (radio) streams
     515        else if (!livetvchain || retries >= 5)
     516            break;
     517
     518        usleep(60000);
    505519    }
    506520
    507     return ret;
     521    LOG(VB_FILE, LOG_INFO, LOC +
     522        "safe_read(RemoteFile* ...): at EOF");
     523    return 0;
    508524}
    509525
    510526long long FileRingBuffer::GetReadPosition(void) const
  • mythtv/libs/libmythtv/mythplayer.cpp

    diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp
    index 5e93d4f..59fa3b8 100644
    a b bool MythPlayer::Pause(void) 
    363363
    364364bool MythPlayer::Play(float speed, bool normal, bool unpauseaudio)
    365365{
    366     pauseLock.lock();
     366    QMutexLocker locker(&pauseLock);
    367367    LOG(VB_PLAYBACK, LOG_INFO, LOC +
    368368        QString("Play(%1, normal %2, unpause audio %3)")
    369369            .arg(speed,5,'f',1).arg(normal).arg(unpauseaudio));
    bool MythPlayer::Play(float speed, bool normal, bool unpauseaudio) 
    371371    if (deleteMap.IsEditing())
    372372    {
    373373        LOG(VB_GENERAL, LOG_ERR, LOC + "Ignoring Play(), in edit mode.");
    374         pauseLock.unlock();
    375374        return false;
    376375    }
    377376
    bool MythPlayer::Play(float speed, bool normal, bool unpauseaudio) 
    383382    allpaused = false;
    384383    next_play_speed   = speed;
    385384    next_normal_speed = normal;
    386     pauseLock.unlock();
    387385    return true;
    388386}
    389387
    int MythPlayer::OpenFile(uint retries) 
    902900        MythTimer peekTimer; peekTimer.start();
    903901        while (player_ctx->buffer->Peek(testbuf, testreadsize) != testreadsize)
    904902        {
    905             if (peekTimer.elapsed() > 1000 || bigTimer.elapsed() > timeout)
     903            // NB need to allow for streams encountering network congestion
     904            if (peekTimer.elapsed() > 30000 || bigTimer.elapsed() > timeout)
    906905            {
    907906                LOG(VB_GENERAL, LOG_ERR, LOC +
    908907                    QString("OpenFile(): Could not read first %1 bytes of '%2'")
    int MythPlayer::OpenFile(uint retries) 
    912911                return -1;
    913912            }
    914913            LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenFile() waiting on data");
    915             usleep(50 * 1000);
     914            usleep(150 * 1000);
    916915        }
    917916
    918917        player_ctx->LockPlayingInfo(__FILE__, __LINE__);
    bool MythPlayer::PrebufferEnoughFrames(int min_buffers) 
    20652064            // to recover from serious problems if frames get leaked.
    20662065            DiscardVideoFrames(true);
    20672066        }
    2068         if (waited_for > 20000) // 20 seconds
     2067        if (waited_for > 30000) // 30 seconds for internet streamed media
    20692068        {
    20702069            LOG(VB_GENERAL, LOG_ERR, LOC +
    20712070                "Waited too long for decoder to fill video buffers. Exiting..");
    void MythPlayer::DisplayNormalFrame(bool check_prebuffer) 
    21092108    SetBuffering(false);
    21102109
    21112110    // retrieve the next frame
    2112     videoOutput->StartDisplayingFrame();
     2111    bool const bDisplayFrame = videoOutput->ValidVideoFrames() > 0;
     2112    if (bDisplayFrame)
     2113        videoOutput->StartDisplayingFrame();
     2114
    21132115    VideoFrame *frame = videoOutput->GetLastShownFrame();
    21142116
    21152117    // Check aspect ratio
    void MythPlayer::DisplayNormalFrame(bool check_prebuffer) 
    21182120    // Player specific processing (dvd, bd, mheg etc)
    21192121    PreProcessNormalFrame();
    21202122
    2121     // handle scan type changes
    2122     AutoDeint(frame);
    2123     detect_letter_box->SwitchTo(frame);
     2123    if (GetTrackCount(kTrackTypeVideo))
     2124    {
     2125        // handle scan type changes
     2126        AutoDeint(frame);
     2127        detect_letter_box->SwitchTo(frame);
     2128    }
    21242129
    21252130    FrameScanType ps = m_scan;
    21262131    if (kScan_Detect == m_scan || kScan_Ignore == m_scan)
    void MythPlayer::DisplayNormalFrame(bool check_prebuffer) 
    21332138    osdLock.unlock();
    21342139
    21352140    AVSync(frame, 0);
    2136     videoOutput->DoneDisplayingFrame(frame);
     2141    if (bDisplayFrame)
     2142        videoOutput->DoneDisplayingFrame(frame);
    21372143}
    21382144
    21392145void MythPlayer::PreProcessNormalFrame(void)
    void MythPlayer::PreProcessNormalFrame(void) 
    21422148    // handle Interactive TV
    21432149    if (GetInteractiveTV())
    21442150    {
    2145         osdLock.lock();
    2146         itvLock.lock();
     2151        QMutexLocker lk1(&osdLock);
     2152
    21472153        if (osd && videoOutput->GetOSDPainter())
    21482154        {
     2155            QMutexLocker lk2(&itvLock);
     2156
    21492157            InteractiveScreen *window =
    21502158                (InteractiveScreen*)osd->GetWindow(OSD_WIN_INTERACT);
    21512159            if ((interactiveTV->ImageHasChanged() || !itvVisible) && window)
    void MythPlayer::PreProcessNormalFrame(void) 
    21532161                interactiveTV->UpdateOSD(window, videoOutput->GetOSDPainter());
    21542162                itvVisible = true;
    21552163            }
     2164            // Hide the iTV window if OSD is active otherwise OSD messages
     2165            // can be hidden
     2166            if (window && itvVisible && GetTrackCount(kTrackTypeVideo) == 0)
     2167                window->SetVisible(!osd->IsVisible());
    21562168        }
    2157         itvLock.unlock();
    2158         osdLock.unlock();
    21592169    }
    21602170#endif // USING_MHEG
    21612171}
    bool MythPlayer::VideoLoop(void) 
    23012311        DisplayPauseFrame();
    23022312    }
    23032313    else
    2304         DisplayNormalFrame();
     2314        DisplayNormalFrame(GetTrackCount(kTrackTypeVideo));
    23052315
    23062316    if (FlagIsSet(kVideoIsNull) && decoder)
    23072317        decoder->UpdateFramesPlayed();
    void MythPlayer::SwitchToProgram(void) 
    24012411    int newid = -1;
    24022412    ProgramInfo *pginfo = player_ctx->tvchain->GetSwitchProgram(d1, d2, newid);
    24032413    if (!pginfo)
     2414    {
     2415        LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchToProgram - No ProgramInfo");
    24042416        return;
     2417    }
    24052418
    24062419    bool newIsDummy = player_ctx->tvchain->GetCardType(newid) == "DUMMY";
    24072420
    void MythPlayer::JumpToProgram(void) 
    25032516    long long nextpos = player_ctx->tvchain->GetJumpPos();
    25042517    ProgramInfo *pginfo = player_ctx->tvchain->GetSwitchProgram(d1, d2, newid);
    25052518    if (!pginfo)
     2519    {
     2520        LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToProgram - No ProgramInfo");
    25062521        return;
     2522    }
    25072523
    25082524    bool newIsDummy = player_ctx->tvchain->GetCardType(newid) == "DUMMY";
    25092525    SetPlayingInfo(*pginfo);
    25102526
    25112527    Pause();
    2512     ChangeSpeed();
    25132528    ResetCaptions();
    25142529    player_ctx->tvchain->SetProgram(*pginfo);
    25152530    player_ctx->buffer->Reset(true);
    void MythPlayer::EventLoop(void) 
    27032718        player_ctx->tvchain->JumpToNext(true, 1);
    27042719        JumpToProgram();
    27052720    }
    2706     else if ((!allpaused || GetEof()) && player_ctx->tvchain &&
    2707              (decoder && !decoder->GetWaitForChange()))
     2721    else if ((!allpaused || GetEof()) &&
     2722             decoder && !decoder->GetWaitForChange() &&
     2723             player_ctx->tvchain && player_ctx->tvchain->NeedsToSwitch())
    27082724    {
    27092725        // Switch to the next program in livetv
    2710         if (player_ctx->tvchain->NeedsToSwitch())
    2711             SwitchToProgram();
     2726        SwitchToProgram();
    27122727    }
    27132728
    27142729    // Jump to the next program in livetv
    void MythPlayer::AudioEnd(void) 
    28642879
    28652880bool MythPlayer::PauseDecoder(void)
    28662881{
    2867     decoderPauseLock.lock();
     2882    QMutexLocker locker(&decoderPauseLock);
    28682883    if (is_current_thread(decoderThread))
    28692884    {
     2885        pauseDecoder = false;
    28702886        decoderPaused = true;
    28712887        decoderThreadPause.wakeAll();
    2872         decoderPauseLock.unlock();
    2873         return decoderPaused;
     2888        return true;
    28742889    }
    28752890
    2876     int tries = 0;
    28772891    pauseDecoder = true;
    2878     while (decoderThread && !killdecoder && (tries++ < 100) &&
    2879            !decoderThreadPause.wait(&decoderPauseLock, 100))
     2892    int tries = 0;
     2893    while (!decoderPaused && decoderThread && !killdecoder && (tries++ < 10) &&
     2894          !decoderThreadPause.wait(locker.mutex(), 100))
    28802895    {
    28812896        LOG(VB_GENERAL, LOG_WARNING, LOC + "Waited 100ms for decoder to pause");
    28822897    }
    28832898    pauseDecoder = false;
    2884     decoderPauseLock.unlock();
    28852899    return decoderPaused;
    2886 }
     2900 }
    28872901
    28882902void MythPlayer::UnpauseDecoder(void)
    28892903{
    2890     decoderPauseLock.lock();
     2904    QMutexLocker locker(&decoderPauseLock);
    28912905
    28922906    if (is_current_thread(decoderThread))
    28932907    {
     2908        unpauseDecoder = false;
    28942909        decoderPaused = false;
    28952910        decoderThreadUnpause.wakeAll();
    2896         decoderPauseLock.unlock();
    28972911        return;
    28982912    }
    28992913
    2900     int tries = 0;
    29012914    unpauseDecoder = true;
    2902     while (decoderThread && !killdecoder && (tries++ < 100) &&
    2903           !decoderThreadUnpause.wait(&decoderPauseLock, 100))
     2915    int tries = 0;
     2916    while (decoderPaused && decoderThread && !killdecoder && (tries++ < 10) &&
     2917          !decoderThreadUnpause.wait(locker.mutex(), 100))
    29042918    {
    29052919        LOG(VB_GENERAL, LOG_WARNING, LOC +
    29062920            "Waited 100ms for decoder to unpause");
    29072921    }
    29082922    unpauseDecoder = false;
    2909     decoderPauseLock.unlock();
    29102923}
    29112924
    29122925void MythPlayer::DecoderStart(bool start_paused)
    void MythPlayer::DecoderEnd(void) 
    29322945    SetPlaying(false);
    29332946    killdecoder = true;
    29342947    int tries = 0;
    2935     while (decoderThread && !decoderThread->wait(100) && (tries++ < 50))
     2948    while (decoderThread && !decoderThread->wait(100) && (tries++ < 20))
    29362949        LOG(VB_PLAYBACK, LOG_INFO, LOC +
    29372950            "Waited 100ms for decoder loop to stop");
    29382951
    void MythPlayer::DecoderEnd(void) 
    29442957
    29452958void MythPlayer::DecoderPauseCheck(void)
    29462959{
    2947     if (is_current_thread(decoderThread))
     2960    if (!is_current_thread(decoderThread))
     2961        return;
     2962
     2963    QMutexLocker locker(&decoderPauseLock);
     2964
     2965    if (pauseDecoder)
    29482966    {
    2949         if (pauseDecoder)
    2950             PauseDecoder();
    2951         if (unpauseDecoder)
    2952             UnpauseDecoder();
     2967        pauseDecoder = false;
     2968        decoderPaused = true;
     2969        decoderThreadPause.wakeAll();
     2970    }
     2971
     2972    if (unpauseDecoder)
     2973    {
     2974        unpauseDecoder = false;
     2975        decoderPaused = false;
     2976        decoderThreadUnpause.wakeAll();
    29532977    }
    29542978}
    29552979
  • mythtv/libs/libmythtv/tv_play.cpp

    diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp
    index 3bc2fec..c206ab9 100644
    a b void TV::ChangeChannel(PlayerContext *ctx, uint chanid, const QString &chan) 
    70927092    if (ctx->prevChan.empty())
    70937093        ctx->PushPreviousChannel();
    70947094
    7095     PauseAudioUntilBuffered(ctx);
     7095    if (ctx->player)
     7096        ctx->player->GetAudio()->Pause(true);
    70967097    PauseLiveTV(ctx);
    70977098
    70987099    ctx->LockDeletePlayer(__FILE__, __LINE__);
    void TV::ChangeChannel(PlayerContext *ctx, uint chanid, const QString &chan) 
    71097110        ctx->player->GetAudio()->Reset();
    71107111
    71117112    UnpauseLiveTV(ctx, chanid && GetQueuedChanID());
     7113    PauseAudioUntilBuffered(ctx);
    71127114
    71137115    if (oldinputname != ctx->recorder->GetInput())
    71147116        UpdateOSDInput(ctx);