Ticket #2287: multi-pip-v7.patch

File multi-pip-v7.patch, 108.2 KB (added by danielk, 15 years ago)

updated patch

  • libs/libmythtv/NuppelVideoPlayer.cpp

     
    214214      audio_channels(2),            audio_bits(-1),
    215215      audio_samplerate(44100),      audio_stretchfactor(1.0f),
    216216      audio_codec(NULL),
    217       // Picture-in-Picture
    218       setpipplayer(NULL),           needsetpipplayer(false),
     217      // Picture-in-Picture stuff
     218      pip_active(false),
    219219      // Preview window support
    220220      argb_buf(NULL),               argb_size(0,0),
    221221      yuv2argb_conv(yuv2rgb_init_mmx(32, MODE_RGB)),
     
    11521152    SetDecoder(dec);
    11531153}
    11541154
     1155extern "C" {
     1156#include "libavutil/crc.h"
     1157}
    11551158int NuppelVideoPlayer::OpenFile(bool skipDsp, uint retries,
    11561159                                bool allow_libmpeg2)
    11571160{
     
    11891192            return -1;
    11901193        }
    11911194
     1195        long long pos = player_ctx->buffer->Seek(0, SEEK_CUR);
     1196        uint checksum = av_crc(av_crc_get_table(AV_CRC_32_IEEE),
     1197                               (uint32_t) -1,
     1198                               (uint8_t*)testbuf, testreadsize - 1);
     1199        VERBOSE(VB_PLAYBACK, LOC +
     1200                QString("Looking for decoder for '%1'@%2 "
     1201                        "probe size %3 crc 0x%4")
     1202                .arg(player_ctx->buffer->GetFilename())
     1203                .arg(pos).arg(testreadsize).arg(checksum,0,16));
     1204
    11921205        player_ctx->LockPlayingInfo(__FILE__, __LINE__);
    11931206        if (NuppelDecoder::CanHandle(testbuf, testreadsize))
    11941207            SetDecoder(new NuppelDecoder(this, *player_ctx->playingInfo));
     
    29562969
    29572970    while (!killvideo)
    29582971    {
    2959         if (needsetpipplayer)
    2960         {
    2961             pip_players.clear();
    2962             if (setpipplayer)
    2963             {
    2964                 pip_players[setpipplayer] =
    2965                     (PIPLocation) gContext->GetNumSetting("PIPLocation", 0);
    2966             }
    2967             needsetpipplayer = false;
    2968             PIPState state = player_ctx->GetPIPState();
    2969             if (m_tv && (state == kPIPOff  || state == kPBPLeft))
    2970                 m_tv->WakeSetPIPPlayerCond();
    2971         }
     2972        HandlePIPPlayerLists(4);
    29722973
    29732974        if (player_ctx->buffer->isDVD())
    29742975        {
     
    30513052
    30523053    while (!killvideo)
    30533054    {
    3054         if (needsetpipplayer)
    3055         {
    3056             pip_players.clear();
    3057             if (setpipplayer)
    3058             {
    3059                 pip_players[setpipplayer] =
    3060                     (PIPLocation) gContext->GetNumSetting("PIPLocation", 0);
    3061             }
    3062             needsetpipplayer = false;
    3063         }
     3055        HandlePIPPlayerLists(1);
    30643056
    30653057        resetvideo = false;
    30663058        SetVideoActuallyPaused(pausevideo);
     
    32603252    }
    32613253    else
    32623254    {
     3255        if (player_ctx)
     3256            player_ctx->SetNVPChangingBuffers(true);
    32633257        GetDecoder()->SetReadAdjust(player_ctx->buffer->SetAdjustFilesize());
    32643258        GetDecoder()->SetWaitForChange();
    3265         if (m_tv && player_ctx && !player_ctx->isPIP())
    3266             m_tv->SetIgnoreKeys(true);
    32673259    }
    32683260
    32693261    delete pginfo;
     
    33033295
    33043296    player_ctx->buffer->Unpause();
    33053297
    3306     if (m_tv && player_ctx && !player_ctx->isPIP())
    3307         m_tv->SetIgnoreKeys(false);
     3298    player_ctx->SetNVPChangingBuffers(false);
    33083299
    33093300    player_ctx->LockPlayingInfo(__FILE__, __LINE__);
    33103301    player_ctx->tvchain->SetProgram(*player_ctx->playingInfo);
     
    38773868        GetDecoder()->setTranscoding(value);
    38783869};
    38793870
     3871bool NuppelVideoPlayer::AddPIPPlayer(
     3872    NuppelVideoPlayer *pip, PIPLocation loc, uint timeout)
     3873{
     3874    QMutexLocker locker(&pip_players_lock);
     3875    pip_players_add[pip] = loc;
     3876
     3877    pip_players_wait.wait(&pip_players_lock, timeout);
     3878
     3879    if (pip_players.find(pip) != pip_players.end())
     3880        return true;
     3881
     3882    PIPMap::iterator it = pip_players_add.find(pip);
     3883    if (it != pip_players_add.end())
     3884        pip_players_add.erase(it);
     3885
     3886    return false;
     3887}
     3888
     3889bool NuppelVideoPlayer::RemovePIPPlayer(NuppelVideoPlayer *pip, uint timeout)
     3890{
     3891    QMutexLocker locker(&pip_players_lock);
     3892
     3893    pip_players_rm[pip] = kPIP_END;
     3894
     3895    pip_players_wait.wait(&pip_players_lock, timeout);
     3896
     3897    if (pip_players.find(pip) == pip_players.end())
     3898        return true;
     3899
     3900    PIPMap::iterator it = pip_players_rm.find(pip);
     3901    if (it != pip_players_rm.end())
     3902        pip_players_rm.erase(it);
     3903
     3904    return false;
     3905}
     3906
     3907void NuppelVideoPlayer::HandlePIPPlayerLists(uint max_cnt)
     3908{
     3909    QMutexLocker locker(&pip_players_lock);
     3910    PIPMap::const_iterator it = pip_players_rm.begin();
     3911    for (; it != pip_players_rm.end(); ++it)
     3912    {
     3913        PIPMap::iterator it2 = pip_players.find(it.key());
     3914        if (it2 != pip_players.end())
     3915            pip_players.erase(it2);
     3916    }
     3917
     3918    for (it = pip_players_add.begin();
     3919         (it != pip_players_add.end()) &&
     3920         ((uint)pip_players.size() <= max_cnt); ++it)
     3921    {
     3922        if (pip_players.find(it.key())    == pip_players.end() &&
     3923            pip_players_rm.find(it.key()) == pip_players_rm.end())
     3924        {
     3925            pip_players[it.key()] = *it;
     3926        }
     3927    }
     3928    pip_players_add.clear();
     3929    pip_players_rm.clear();
     3930    pip_players_wait.wakeAll();
     3931}
     3932
     3933PIPLocation NuppelVideoPlayer::GetNextPIPLocation(void) const
     3934{
     3935    QMutexLocker locker(&pip_players_lock);
     3936    PIPMap sim_pip_players = pip_players;
     3937
     3938    PIPMap::const_iterator it = pip_players_rm.begin();
     3939    for (; it != pip_players_rm.end(); ++it)
     3940    {
     3941        PIPMap::iterator it2 = sim_pip_players.find(it.key());
     3942        if (it2 != sim_pip_players.end())
     3943            sim_pip_players.erase(it2);
     3944    }
     3945
     3946    for (it = pip_players_add.begin(); it != pip_players_add.end(); ++it)
     3947    {
     3948        if (sim_pip_players.find(it.key()) == sim_pip_players.end() &&
     3949            pip_players_rm.find(it.key())  == pip_players_rm.end())
     3950        {
     3951            sim_pip_players[it.key()] = *it;
     3952        }
     3953    }
     3954
     3955    // order of preference, could be stored in db if we want it configurable
     3956    PIPLocation ols[] =
     3957        { kPIPTopLeft, kPIPTopRight, kPIPBottomLeft, kPIPBottomRight };
     3958
     3959    for (uint i = 0; i < sizeof(ols)/sizeof(PIPLocation); i++)
     3960    {
     3961        PIPMap::const_iterator it = sim_pip_players.begin();
     3962        for (; it != sim_pip_players.end() && (*it != ols[i]); ++it);
     3963
     3964        if (it == sim_pip_players.end())
     3965            return ols[i];
     3966    }
     3967
     3968    return kPIP_END;
     3969}
     3970
    38803971void NuppelVideoPlayer::WrapTimecode(long long &timecode, TCTypes tc_type)
    38813972{
    38823973    if ((tc_type == TC_AUDIO) && (tc_wrap[TC_AUDIO] == LONG_LONG_MIN))
  • libs/libmythtv/avformatdecoder.cpp

     
    873873 *  \param novideo if true then no video is sought in ScanSreams.
    874874 *  \param testbuf this parameter is not used by AvFormatDecoder.
    875875 */
     876extern "C" {
     877#include "libavutil/crc.h"
     878}
    876879int AvFormatDecoder::OpenFile(RingBuffer *rbuffer, bool novideo,
    877880                              char testbuf[kDecoderProbeBufferSize],
    878881                              int testbufsize)
     
    899902        probe.buf_size = kDecoderProbeBufferSize - AVPROBE_PADDING_SIZE;
    900903
    901904    fmt = av_probe_input_format(&probe, true);
     905    uint checksum = av_crc(av_crc_get_table(AV_CRC_32_IEEE),
     906                           (uint32_t) -1,
     907                           (uint8_t*)testbuf, probe.buf_size - 1);
    902908    if (!fmt)
    903909    {
    904910        VERBOSE(VB_IMPORTANT, LOC_ERR +
    905                 QString("Probe failed for file: \"%1\".").arg(filename));
     911                QString("Probe failed for file: '%1' probe size %2 crc 0x%3")
     912                .arg(filename).arg(probe.buf_size).arg(checksum,0,16));
    906913        return -1;
    907914    }
     915    VERBOSE(VB_IMPORTANT, LOC +
     916            QString("Probe suceeded for file: '%1' probe size %2 crc 0x%3")
     917            .arg(filename).arg(probe.buf_size).arg(checksum,0,16));
    908918
    909919    fmt->flags |= AVFMT_NOFILE;
    910920
  • libs/libmythtv/tv_play.h

     
    150150
    151151    // LiveTV commands
    152152    bool LiveTV(bool showDialogs = true, bool startInGuide = false);
    153     /// This command is used to exit the player in order to record using
    154     /// the recording being used by LiveTV.
    155     void StopLiveTV(void) { SetExitPlayer(true, true); }
    156153
    157154    // Embedding commands for the guidegrid to use in LiveTV
    158155    void EmbedOutput(PlayerContext*, WId wid, int x, int y, int w, int h);
     
    184181    bool IsBookmarkAllowed(const PlayerContext*) const;
    185182    bool IsDeleteAllowed(const PlayerContext*) const;
    186183
    187     bool CreatePBP(PlayerContext*, const ProgramInfo *info);
    188     bool CreatePIP(PlayerContext*, const ProgramInfo *info);
    189     void ResizePIPWindow(PlayerContext*);
     184    bool CreatePBP(PlayerContext *lctx, const ProgramInfo *info);
     185    bool CreatePIP(PlayerContext *lctx, const ProgramInfo *info);
     186    bool ResizePIPWindow(PlayerContext*);
    190187    bool IsPIPSupported(const PlayerContext *ctx = NULL) const;
    191188
    192189    // Boolean queries
     
    238235                        bool inPlaylist = false, bool initByNetworkCommand = false);
    239236    static void SetFuncPtr(const char *, void *);
    240237
    241     void SetIgnoreKeys(bool ignore) { ignoreKeys = ignore; }
    242 
    243238    // Used by EPG
    244239    void ChangeVolume(PlayerContext*, bool up);
    245240    void ToggleMute(PlayerContext*);
    246241
    247     void WakeSetPIPPlayerCond(void);
    248     void SetNextProgPIPState(PIPState state) { nextProgPIPState = state; }
     242    void SetNextProgPIPState(PIPState state) { jumpToProgramPIPState = state; }
    249243
    250244    // Used for UDPNotify
    251245    bool HasUDPNotifyEvent(void) const;
     
    276270    static EMBEDRETURNVOID RunViewScheduledPtr;
    277271
    278272  private:
    279     void SetActive(PlayerContext *, int index);
     273    void SetActive(PlayerContext *lctx, int index, bool osd_msg);
    280274
    281275    PlayerContext       *GetPlayerWriteLock(
    282276        int which, const char *file, int location);
     
    297291
    298292    int  StartTimer(int interval, int line);
    299293    void KillTimer(int id);
    300     void ForceNextStateNone(PlayerContext*);
     294    void ForceNextStateNone(PlayerContext*, int line);
    301295    void ScheduleStateChange(PlayerContext*);
    302296    void SetErrored(PlayerContext*);
    303     void SetExitPlayer(bool set_it, bool wants_to) const;
     297    void SetExitPlayerReal(bool set_it, bool wants_to, int line) const;
    304298    void SetUpdateOSDPosition(bool set_it);
    305299
    306     bool PIPHandleAction(PlayerContext*,const QStringList &actions);
     300    bool PxPHandleAction(PlayerContext*,const QStringList &actions);
    307301    bool ToggleHandleAction(PlayerContext*,
    308302                            const QStringList &actions, bool isDVD);
    309303    bool FFRewHandleAction(PlayerContext*, const QStringList &actions);
    310304    bool ActivePostQHandleAction(PlayerContext*,
    311305                                 const QStringList &actions, bool isDVD);
     306    bool HandleJumpToProgramAction(PlayerContext *ctx,
     307                                   const QStringList   &actions);
    312308
    313309    bool RequestNextRecorder(PlayerContext *, bool);
    314310    void DeleteRecorder();
     
    422418                                const QStringList &actions);
    423419
    424420    void DoDisplayJumpMenu(void);
    425     void SetJumpToProgram(QString progKey = "", int progIndex = 0);
    426421 
    427422    bool ClearOSD(const PlayerContext*);
    428423    void ToggleOSD(const PlayerContext*, bool includeStatusOSD);
     
    442437                      int editType = kScheduleProgramGuide);
    443438
    444439    void TeardownPlayer(PlayerContext *mctx, PlayerContext *ctx);
    445     void TeardownPBP(PlayerContext*);
    446440
    447441    void HandleStateChange(PlayerContext *mctx, PlayerContext *ctx);
    448442
    449443    bool StartPlayer(PlayerContext *mctx, PlayerContext *ctx,
    450                      TVState desiredState, bool ispip = false);
    451     bool StartPIPPlayer(PlayerContext *mctx, PlayerContext *pipctx,
    452                         TVState desiredState);
    453     void SetPIPPlayer(PlayerContext *mctx, PlayerContext *pipctx);
    454     void TogglePIPView(PlayerContext*, bool ispbp = false);
    455     void ToggleActiveWindow(PlayerContext*, int ctx_index);
    456     void SwapPIP(PlayerContext *lctx, int ctx_index, bool ispbp);
    457     void TogglePIPState(PlayerContext*, PIPState changeTo);
    458     void RestartPIPWindows(PlayerContext *lctx, const vector<long long> &pos,
    459                            MuteState mctx_mute);
    460     void TeardownPIPWindows(PlayerContext*);
     444                     TVState desiredState);
    461445
     446    vector<long long> TeardownAllNVPs(PlayerContext*);
     447    void RestartAllNVPs(PlayerContext *lctx,
     448                        const vector<long long> &pos,
     449                        MuteState mctx_mute);
     450
     451    void PxPToggleView(  PlayerContext *actx, bool wantPBP);
     452    void PxPCreateView(  PlayerContext *actx, bool wantPBP);
     453    void PxPTeardownView(PlayerContext *actx);
     454    void PxPToggleType(  PlayerContext *mctx, bool wantPBP);
     455    void PxPSwap(        PlayerContext *mctx, int ctx_index = -1);
     456    bool HandlePxPTimerEvent(void);
     457
     458    bool PIPAddPlayer(   PlayerContext *mctx, PlayerContext *ctx);
     459    bool PIPRemovePlayer(PlayerContext *mctx, PlayerContext *ctx);
     460    void PBPRestartMainNVP(PlayerContext *mctx);
     461
    462462    void ToggleAutoExpire(PlayerContext*);
    463463
    464464    void BrowseStart(PlayerContext*);
     
    479479
    480480    void BuildOSDTreeMenu(const PlayerContext*);
    481481    void ShowOSDTreeMenu(const PlayerContext*);
     482    void FillMenuPxP(const PlayerContext*, OSDGenericTree *treeMenu,
     483                     bool freeRecorders);
    482484    void FillMenuLiveTV(OSDGenericTree *treeMenu);
    483485    void FillMenuPlaying(const PlayerContext*, OSDGenericTree *treeMenu);
    484486
     
    509511    void IdleDialogTimeout(void);
    510512
    511513    // Program jumping stuff
    512     void setLastProgram(ProgramInfo *rcinfo);
    513     ProgramInfo *getLastProgram(void) { return lastProgram; }
     514    void SetLastProgram(ProgramInfo *rcinfo);
     515    ProgramInfo *GetLastProgram(void) const;
    514516
    515517    static bool LoadExternalSubtitles(NuppelVideoPlayer *nvp,
    516518                                      const QString &videoFile);
     
    537539    QString db_time_format;
    538540    QString db_short_date_format;
    539541    uint    db_idle_timeout;
     542    uint    db_udpnotify_port;
     543    int     db_playback_exit_prompt;
     544    int     db_autoexpire_default;
    540545    bool    db_auto_set_watched;
    541546    bool    db_end_of_rec_exit_prompt;
     547    bool    db_jump_prefer_osd;
     548    bool    db_use_gui_size_for_tv;
     549    bool    db_start_in_guide;
     550    bool    db_toggle_bookmark;
     551    bool    db_run_jobs_on_remote;
     552    bool    db_use_dvd_bookmark;
     553    bool    db_continue_embedded;
     554    bool    db_use_fixed_size;
     555
    542556    bool    smartChannelChange;
    543557    bool    MuteIndividualChannels;
    544558    bool    arrowAccel;
     
    582596    QMap<QString,AskProgramInfo> askAllowPrograms;
    583597    QMutex                       askAllowLock;
    584598
    585     bool ignoreKeys;
    586     PIPState needToMakePIPChange;
    587     PIPState nextProgPIPState;
     599    MythDeque<QString> changePxP;
     600    QMutex progListsLock;
    588601    QMap<QString,ProgramList> progLists;
    589602
    590603    mutable QMutex chanEditMapLock; ///< Lock for chanEditMap and ddMap
     
    639652
    640653    // Program Info for currently playing video
    641654    // (or next video if InChangeState() is true)
     655    mutable QMutex lastProgramLock;
    642656    ProgramInfo *lastProgram;   ///< last program played with this player
    643     bool         jumpToProgram;
    644 
    645657    bool         inPlaylist; ///< show is part of a playlist
    646658    bool         underNetworkControl; ///< initial show started via by the network control interface
    647659    bool         isnearend;
    648660
     661    // Program Jumping
     662    PIPState     jumpToProgramPIPState;
     663    bool         jumpToProgram;
     664
    649665    // Video Players
    650666    vector<PlayerContext*>  player;
    651667    /// Video Player to which events are sent to
    652668    int                     playerActive;
    653669    /// lock on player and playerActive changes
    654670    mutable QReadWriteLock  playerLock;
    655     QWaitCondition pipPlayerSetCondition;
    656671
    657672    // Remote Encoders
    658673    /// Main recorder to use after a successful SwitchCards() call.
     
    741756    static const uint kInputKeysMax; ///< When to start discarding early keys
    742757    static const uint kNextSource;
    743758    static const uint kPreviousSource;
     759    static const uint kMaxPIPCount;
     760    static const uint kMaxPBPCount;
    744761
    745762    ///< Timeout for entry modes in msec
    746763    static const uint kInputModeTimeout;
  • libs/libmythtv/playercontext.cpp

     
    3030}
    3131
    3232PlayerContext::PlayerContext() :
    33     nvp(NULL), recorder(NULL),
     33    nvp(NULL), nvpUnsafe(false), recorder(NULL),
    3434    tvchain(NULL), buffer(NULL), playingInfo(NULL),
    3535    decoding(false), last_cardid(-1), last_framerate(30.0f),
    3636    // Fast forward state
     
    102102        newPlaygroup = playingInfo->playgroup;
    103103    }
    104104
    105     ChangeState(newState);
     105    ChangeState(newState, __LINE__);
    106106    SetPlayGroup(newPlaygroup);
    107107}
    108108
     
    141141    if (pos > -1)
    142142    {
    143143        pipLocation = pos;
    144         name = QString("pip player %1")
    145                 .arg(PIPLocationToString());
     144        name = QString("pip player %1").arg(toString((PIPLocation)pos));
    146145    }
    147146    else
    148147        name = "pip player";
     
    216215    }
    217216}
    218217
    219 QString PlayerContext::PIPLocationToString(void)
    220 {
    221     QString pipstr = QString ("Unknown PIP Location %1").arg(pipLocation);
    222     switch (pipLocation)
    223     {
    224         case (kPIPTopLeft):
    225             pipstr = "PIP: Top Left";
    226             break;
    227         case (kPIPTopRight):
    228             pipstr = "PIP: Top Right";
    229             break;
    230         case (kPIPBottomLeft):
    231             pipstr = "PIP: Bottom Left";
    232             break;
    233         case (kPIPBottomRight):
    234             pipstr = "PIP: Bottom Right";
    235             break;
    236     }
    237 
    238     return pipstr;
    239 }
    240 
    241 QString PlayerContext::PIPStateToString(void)
    242 {
    243     QString pipstr = QString("Unknown PIP State %1").arg(pipState);
    244     switch (pipState)
    245     {
    246         case (kPIPOff):
    247             pipstr = "PIP is off";
    248             break;
    249         case (kPIPonTV):
    250             pipstr = "PIP is visible on TV";
    251             break;
    252         case (kPIPStandAlone):
    253             pipstr = "Standalone PIP";
    254             break;
    255         case (kPBPLeft):
    256             pipstr = "Left side Picture-by-Picture";
    257             break;
    258         case (kPBPRight):
    259             pipstr = "Right side Picture-by-Picture";
    260             break;
    261         case (kPIPToPBP):
    262             pipstr = "Preparing to switch from PIP to PBP";
    263             break;
    264         case (kPBPToPIP):
    265             pipstr = "Preparing to switch from PBP to PIP";
    266             break;
    267         case (kPBPToggle):
    268             pipstr = "Disabling PBP/PIP";
    269             break;
    270         case (kPIPSwap):
    271             pipstr = "Swapping PBP/PIP";
    272             break;
    273     }
    274 
    275     return pipstr;
    276 }
    277 
    278218bool PlayerContext::StartPIPPlayer(TV *tv, TVState desiredState)
    279219{
    280220    bool ok = false;
     
    440380        nvp->SetMuted(true);
    441381
    442382    int maxWait = -1;
    443     if (isPIP())
    444        maxWait = 1000;
     383    //if (isPIP())
     384    //   maxWait = 1000;
    445385
    446386    return StartDecoderThread(maxWait);
    447387}
     
    649589/**
    650590*   \brief Puts a state change on the nextState queue.
    651591*/
    652 void PlayerContext::ChangeState(TVState newState)
     592void PlayerContext::ChangeState(TVState newState, int line)
    653593{
     594    VERBOSE(VB_IMPORTANT, QString("ChangeState(%1,%2)")
     595            .arg(StateToString(newState)).arg(line));
    654596    QMutexLocker locker(&stateLock);
    655597    nextState.enqueue(newState);
    656598}
     
    664606/**
    665607 * \brief Removes any pending state changes, and puts kState_None on the queue.
    666608 */
    667 void PlayerContext::ForceNextStateNone(void)
     609void PlayerContext::ForceNextStateNone(int line)
    668610{
     611    VERBOSE(VB_IMPORTANT, QString("ForceNextStateNone(%2)").arg(line));
    669612    QMutexLocker locker(&stateLock);
    670613    nextState.clear();
    671614    nextState.push_back(kState_None);
  • libs/libmythtv/NuppelVideoPlayer.h

     
    149149    void SetVideoFilters(const QString &override);
    150150    void SetFramesPlayed(long long played)    { framesPlayed = played; }
    151151    void SetEof(void)                         { eof = true; }
    152     void SetPIPPlayer(NuppelVideoPlayer *pip)
    153         { setpipplayer = pip; needsetpipplayer = true; }
     152    void SetPIPActive(bool is_active)         { pip_active = is_active; }
     153    bool AddPIPPlayer(NuppelVideoPlayer *pip, PIPLocation loc, uint timeout);
     154    bool RemovePIPPlayer(NuppelVideoPlayer *pip, uint timeout);
    154155
    155156    void SetTranscoding(bool value);
    156157    void SetWatchingRecording(bool mode);
     
    203204    QString   GetXDS(const QString &key) const;
    204205    QString   GetErrorMsg(void) const { return errmsg; }
    205206    bool      GetAudioBufferStatus(uint &fill, uint &total) const;
     207    PIPLocation GetNextPIPLocation(void) const;
    206208
    207209    // Bool Gets
    208210    bool    GetRawAudioState(void) const;
    209211    bool    GetLimitKeyRepeat(void) const     { return limitKeyRepeat; }
    210212    bool    GetEof(void) const                { return eof; }
    211     bool    PipPlayerSet(void) const          { return !needsetpipplayer; }
    212213    bool    IsErrored(void) const             { return errored; }
    213214    bool    IsPlaying(uint wait_ms = 0, bool wait_for = true) const;
    214215    bool    AtNormalSpeed(void) const         { return next_normal_speed; }
     
    218219    bool    PlayingSlowForPrebuffer(void) const { return m_playing_slower; }
    219220    bool    HasAudioIn(void) const            { return !no_audio_in; }
    220221    bool    HasAudioOut(void) const           { return !no_audio_out; }
     222    bool    IsPIPActive(void) const           { return pip_active; }
    221223    bool    IsMuted(void) const        { return GetMuteState() == kMuteAll; }
    222224    bool    IsIVTVDecoder(void) const;
    223225    bool    UsingNullVideo(void) const { return using_null_videoout; }
     
    530532    long long GetDVDBookmark(void) const;
    531533    void SetDVDBookmark(long long frames);
    532534
     535    // Private PIP stuff
     536    void HandlePIPPlayerLists(uint max_pip_players);
     537
    533538  private:
    534539    DecoderBase   *decoder;
    535540    QMutex         decoder_change_lock;
     
    702707    bool     audio_passthru;
    703708
    704709    // Picture-in-Picture
     710    mutable QMutex pip_players_lock;
     711    QWaitCondition pip_players_wait;
    705712    PIPMap         pip_players;
    706     NuppelVideoPlayer *setpipplayer;
    707     bool needsetpipplayer;
     713    PIPMap         pip_players_add;
     714    PIPMap         pip_players_rm;
     715    volatile bool  pip_active;
    708716
    709717    // Preview window support
    710718    unsigned char      *argb_buf;
  • libs/libmythtv/tv_play.cpp

     
    6565
    6666#define GetPlayer(X,Y) GetPlayerHaveLock(X, Y, __FILE__ , __LINE__)
    6767#define GetOSDLock(X) GetOSDL(X, __FILE__, __LINE__)
     68#define SetExitPlayer(X,Y) SetExitPlayerReal(X,Y,__LINE__)
    6869
    6970const int  TV::kInitFFRWSpeed                = 0;
    7071const uint TV::kInputKeysMax                 = 6;
    7172const uint TV::kNextSource                   = 1;
    7273const uint TV::kPreviousSource               = 2;
     74const uint TV::kMaxPIPCount                  = 4;
     75const uint TV::kMaxPBPCount                  = 2;
    7376
     77
    7478const uint TV::kInputModeTimeout             = 5000;
    7579const uint TV::kMuteTimeout                  = 800;
    7680const uint TV::kLCDTimeout                   = 1000;
     
    154158    if (!lastProgramStringList.empty())
    155159    {
    156160        ProgramInfo *p = new ProgramInfo();
    157         p->FromStringList(lastProgramStringList, 0);
    158         tv->setLastProgram(p);
     161        if (p->FromStringList(lastProgramStringList, 0))
     162            tv->SetLastProgram(p);
    159163        delete p;
    160164    }
    161165
     
    175179            VERBOSE(VB_PLAYBACK, LOC + "tv->LiveTV() -- begin");
    176180            if (!tv->LiveTV(showDialogs, startInGuide))
    177181            {
    178                 tv->StopLiveTV();
     182                tv->SetExitPlayerReal(true, true, __LINE__);
    179183                quitAll = true;
    180184            }
    181185            VERBOSE(VB_PLAYBACK, LOC + "tv->LiveTV() -- end");
     
    270274
    271275        if (tv->getJumpToProgram())
    272276        {
     277            ProgramInfo *nextProgram = tv->GetLastProgram();
    273278
    274             ProgramInfo *tmpProgram  = tv->getLastProgram();
    275             ProgramInfo *nextProgram = new ProgramInfo(*tmpProgram);
    276 
     279            tv->SetLastProgram(curProgram);
    277280            if (curProgram)
    278             {
    279                 tv->setLastProgram(curProgram);
    280281                delete curProgram;
    281             }
    282             else
    283                 tv->setLastProgram(NULL);
    284282
    285283            curProgram = nextProgram;
    286284
     
    420418    REG_KEY("TV Playback", "VOLUMEDOWN", "Volume down", "[,{,F10,Volume Down");
    421419    REG_KEY("TV Playback", "VOLUMEUP",   "Volume up",   "],},F11,Volume Up");
    422420    REG_KEY("TV Playback", "MUTE",       "Mute",        "|,\\,F9,Volume Mute");
    423     REG_KEY("TV Playback", "TOGGLEPIPMODE", "Toggle Picture-in-Picture mode",
     421    REG_KEY("TV Playback", "TOGGLEPIPMODE", "Toggle Picture-in-Picture view",
    424422            "V");
    425     REG_KEY("TV Playback", "TOGGLEPBPMODE", "Toggle Picture-by-Picture mode",
     423    REG_KEY("TV Playback", "TOGGLEPBPMODE", "Toggle Picture-by-Picture view",
    426424            "Ctrl+V");
    427     REG_KEY("TV Playback", "TOGGLEPIPWINDOW", "Toggle active PIP/PBP window", "B");
     425    REG_KEY("TV Playback", "CREATEPIPVIEW", "Create Picture-in-Picture view",
     426            "1");
     427    REG_KEY("TV Playback", "CREATEPBPVIEW", "Create Picture-by-Picture view",
     428            "Ctrl+1");
     429    REG_KEY("TV Playback", "NEXTPIPWINDOW", "Toggle active PIP/PBP window", "B");
    428430    REG_KEY("TV Playback", "SWAPPIP", "Swap PBP/PIP Windows", "N");
    429     REG_KEY("TV Playback", "TOGGLEPIPSTATE", "Change PIP from PBP or visa versa"
    430             ,"");
     431    REG_KEY("TV Playback", "TOGGLEPIPSTATE", "Change PxP view", "");
    431432    REG_KEY("TV Playback", "TOGGLEASPECT",
    432433            "Toggle the video aspect ratio", "Ctrl+W");
    433434    REG_KEY("TV Playback", "TOGGLEFILL", "Next Preconfigured Zoom mode", "W");
     
    569570      baseFilters(""),
    570571      db_channel_format("<num> <sign>"),
    571572      db_time_format("h:mm AP"), db_short_date_format("M/d"),
    572       db_idle_timeout(0),
    573       db_auto_set_watched(false),
    574       db_end_of_rec_exit_prompt(false),
     573      db_idle_timeout(0),           db_udpnotify_port(0),
     574      db_playback_exit_prompt(0),   db_autoexpire_default(0),
     575      db_auto_set_watched(false),   db_end_of_rec_exit_prompt(false),
     576      db_jump_prefer_osd(true),     db_use_gui_size_for_tv(false),
     577      db_start_in_guide(false),     db_toggle_bookmark(false),
     578      db_run_jobs_on_remote(false), db_use_dvd_bookmark(false),
     579      db_continue_embedded(false),  db_use_fixed_size(true),
     580
    575581      smartChannelChange(false),
    576582      MuteIndividualChannels(false), arrowAccel(false),
    577583      osd_general_timeout(2), osd_prog_info_timeout(3),
     
    594600      adjustingPicture(kAdjustingPicture_None),
    595601      adjustingPictureAttribute(kPictureAttribute_None),
    596602      askAllowType(kAskAllowCancel), askAllowLock(QMutex::Recursive),
    597       ignoreKeys(false),
    598       needToMakePIPChange(kPIPOff),
    599       nextProgPIPState(kPIPOff),
    600603      // Channel Editing
    601604      chanEditMapLock(QMutex::Recursive),
    602605      ddMapSourceId(0), ddMapLoaderRunning(false),
     
    615618      browsemode(false), persistentbrowsemode(false),
    616619      browsechannum(""), browsechanid(""), browsestarttime(""),
    617620      // Program Info for currently playing video
    618       lastProgram(NULL), jumpToProgram(false),
     621      lastProgram(NULL),
    619622      inPlaylist(false), underNetworkControl(false),
    620623      isnearend(false),
     624      // Jump to program stuff
     625      jumpToProgramPIPState(kPIPOff),
     626      jumpToProgram(false),
    621627      // Video Player currently receiving UI input
    622628      playerActive(-1),
    623629      //Recorder switching info
     
    647653
    648654    db_idle_timeout = gContext->GetNumSetting("LiveTVIdleTimeout", 0);
    649655    db_idle_timeout *= 60 * 1000; // convert from minutes to ms.
    650     db_auto_set_watched = gContext->GetNumSetting("AutomaticSetWatched", 0);
     656    db_udpnotify_port      = gContext->GetNumSetting("UDPNotifyPort", 0);
     657    db_playback_exit_prompt= gContext->GetNumSetting("PlaybackExitPrompt", 0);
     658    db_autoexpire_default  = gContext->GetNumSetting("AutoExpireDefault", 0);
     659
     660    db_auto_set_watched     = gContext->GetNumSetting("AutomaticSetWatched", 0);
    651661    db_end_of_rec_exit_prompt =
    652662        gContext->GetNumSetting("EndOfRecordingExitPrompt", 0);
     663    db_jump_prefer_osd     = gContext->GetNumSetting("JumpToProgramOSD", 1);
     664    db_use_gui_size_for_tv = gContext->GetNumSetting("GuiSizeForTV", 0);
     665    db_start_in_guide      = gContext->GetNumSetting("WatchTVGuide", 0);
     666    db_toggle_bookmark     = gContext->GetNumSetting("AltClearSavedPosition",1);
     667    db_run_jobs_on_remote  = gContext->GetNumSetting("JobsRunOnRecordHost", 0);
     668    db_use_dvd_bookmark    = gContext->GetNumSetting("EnableDVDBookmark", 0);
     669    db_continue_embedded   = gContext->GetNumSetting(
     670        "ContinueEmbeddedTVPlay", 0);
     671    db_use_fixed_size      = gContext->GetNumSettingOnHost(
     672        "UseFixedWindowSize", gContext->GetHostName(), 1);
    653673
    654674    sleep_times.push_back(SleepTimerInfo(QObject::tr("Off"),       0));
    655675    sleep_times.push_back(SleepTimerInfo(QObject::tr("30m"),   30*60));
     
    662682
    663683    playerLock.lockForWrite();
    664684    player.push_back(new PlayerContext());
     685    playerActive = 0;
    665686    playerLock.unlock();
    666     SetActive(NULL, 0);
    667687}
    668688
    669689/** \fn TV::Init(bool)
     
    768788            }
    769789        }
    770790
    771         bool use_fixed_size = gContext->GetNumSettingOnHost(
    772             "UseFixedWindowSize", gContext->GetHostName(), 1);
    773 
    774791        // player window sizing
    775792        myWindow = new MythDialog(mainWindow, "video playback window");
    776793
     
    780797        myWindow->setGeometry(win_bounds);
    781798        myWindow->setBaseSize(win_bounds.size());
    782799        myWindow->setMinimumSize(
    783             (use_fixed_size) ? win_bounds.size() : QSize(16, 16));
     800            (db_use_fixed_size) ? win_bounds.size() : QSize(16, 16));
    784801        myWindow->setMaximumSize(
    785             (use_fixed_size) ? win_bounds.size() :
     802            (db_use_fixed_size) ? win_bounds.size() :
    786803            QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
    787804
    788805        // resize main window
    789806        mainWindow->setGeometry(player_bounds);
    790807        mainWindow->setBaseSize(player_bounds.size());
    791808        mainWindow->setMinimumSize(
    792             (use_fixed_size) ? player_bounds.size() : QSize(16, 16));
     809            (db_use_fixed_size) ? player_bounds.size() : QSize(16, 16));
    793810        mainWindow->setMaximumSize(
    794             (use_fixed_size) ? player_bounds.size() :
     811            (db_use_fixed_size) ? player_bounds.size() :
    795812            QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
    796813        mainWindow->installEventFilter(this);
    797814
     
    834851        mwnd->resize(saved_gui_bounds.size());
    835852        mwnd->setFixedSize(saved_gui_bounds.size());
    836853        mwnd->show();
    837         if (!gContext->GetNumSetting("GuiSizeForTV", 0))
     854        if (!db_use_gui_size_for_tv)
    838855            mwnd->move(saved_gui_bounds.topLeft());
    839856    }
    840857
     
    923940                    .arg(db_idle_timeout*(1.0f/60000.0f)));
    924941        }
    925942
    926         if (startInGuide || gContext->GetNumSetting("WatchTVGuide", 0))
     943        if (startInGuide || db_start_in_guide)
    927944        {
    928945            MSqlQuery query(MSqlQuery::InitCon());
    929946            query.prepare("SELECT keylist FROM keybindings WHERE "
     
    13361353                break;
    13371354            case 2:
    13381355                // return to main menu
    1339                 StopLiveTV();
     1356                SetExitPlayer(true, true);
    13401357                break;
    13411358            case 3:
    13421359                // cancel scheduled recording
     
    13531370            default:
    13541371            case 1:
    13551372                // return to main menu
    1356                 StopLiveTV();
     1373                SetExitPlayer(true, true);
    13571374                break;
    13581375            case 2:
    13591376            {
     
    16281645        {
    16291646            // Cache starting frame rate for this recorder
    16301647            ctx->last_framerate = ctx->recorder->GetFrameRate();
    1631             ok = StartPlayer(mctx, ctx, desiredNextState, ctx->isPIP());
     1648            ok = StartPlayer(mctx, ctx, desiredNextState);
    16321649        }
    16331650        if (!ok)
    16341651        {
     
    16481665                lockTimerOn = true;
    16491666            }
    16501667        }
     1668
     1669        if (mctx != ctx)
     1670            SetActive(ctx, find_player_index(ctx), false);
    16511671    }
    16521672    else if (TRANSITION(kState_WatchingLiveTV, kState_None))
    16531673    {
    16541674        SET_NEXT();
    16551675        RestoreScreenSaver(ctx);
    16561676        StopStuff(mctx, ctx, true, true, true);
     1677
     1678        if ((mctx != ctx) && (GetPlayer(ctx,-1) == ctx))
     1679            SetActive(mctx, 0, true);
    16571680    }
    16581681    else if (TRANSITION(kState_WatchingRecording, kState_WatchingPreRecorded))
    16591682    {
     
    17041727                }
    17051728            }
    17061729
    1707             ok = StartPlayer(mctx, ctx, desiredNextState, ctx->isPIP());
     1730            ok = StartPlayer(mctx, ctx, desiredNextState);
    17081731
    17091732            if (ok)
    17101733            {
     
    17271750            SET_LAST();
    17281751            SetErrored(ctx);
    17291752        }
     1753        else if (mctx != ctx)
     1754        {
     1755            SetActive(ctx, find_player_index(ctx), false);
     1756        }
    17301757    }
    17311758    else if (TRANSITION(kState_WatchingPreRecorded, kState_None) ||
    17321759             TRANSITION(kState_WatchingRecording, kState_None))
     
    17351762
    17361763        RestoreScreenSaver(ctx);
    17371764        StopStuff(mctx, ctx, true, true, false);
     1765
     1766        if ((mctx != ctx) && (GetPlayer(ctx,-1) == ctx))
     1767            SetActive(mctx, 0, true);
    17381768    }
    17391769    else if (TRANSITION(kState_None, kState_None))
    17401770    {
     
    17541784
    17551785        nextState = kState_None;
    17561786        changed = true;
     1787
     1788        if ((mctx != ctx) && (GetPlayer(ctx,-1) == ctx))
     1789            SetActive(mctx, 0, true);
    17571790    }
    17581791
    17591792    // Print state changed message...
     
    19011934            LOC + QString("StopStuff() for player ctx %1 -- begin")
    19021935            .arg(find_player_index(ctx)));
    19031936
    1904     SetActive(mctx, 0);
     1937    SetActive(mctx, 0, false);
    19051938
    19061939    if (ctx->buffer && ctx->buffer->isDVD())
    19071940    {
     
    19511984    QString loc = LOC + QString("TeardownPlayer() player ctx %1")
    19521985        .arg(ctx_index);
    19531986
    1954     SetActive(mctx, 0);
     1987    if (!mctx || !ctx || ctx_index < 0)
     1988    {
     1989        VERBOSE(VB_IMPORTANT, loc + "-- error");
     1990        return;
     1991    }
    19551992
     1993    VERBOSE(VB_PLAYBACK, loc);
     1994
    19561995    if (mctx != ctx)
    19571996    {
    19581997        if (ctx->nvp)
    19591998        {
     1999            PIPRemovePlayer(mctx, ctx);
    19602000            ctx->nvp->StopPlaying();
    19612001            ctx->SetNVP(NULL);
    19622002        }
    19632003
    1964         SetPIPPlayer(mctx, ctx);
    19652004        player.erase(player.begin() + ctx_index);
    19662005        delete ctx;
    1967         TeardownPBP(ctx);
     2006        PBPRestartMainNVP(mctx);
     2007        SetActive(mctx, playerActive, false);
    19682008        return;
    19692009    }
    19702010
     
    20222062    mctx = GetPlayerWriteLock(0, __FILE__, __LINE__);
    20232063    if (!mctx->IsErrored() && (GetState(mctx) != kState_None))
    20242064    {
    2025         mctx->ForceNextStateNone();
     2065        mctx->ForceNextStateNone(__LINE__);
    20262066        HandleStateChange(mctx, mctx);
    20272067        if (jumpToProgram)
    20282068            TeardownPlayer(mctx, mctx);
     
    20762116        HandlePseudoLiveTVTimerEvent();
    20772117    else if (timer_id == speedChangeTimerId)
    20782118        HandleSpeedChangeTimerEvent();
     2119    else if (timer_id == pipChangeTimerId)
     2120        HandlePxPTimerEvent();
    20792121    else
    20802122        handled = false;
    20812123
     
    22752317
    22762318    // Check if it matches networkControlTimerId
    22772319    QString netCmd = QString::null;
    2278     if (!ignoreKeys)
    22792320    {
    22802321        QMutexLocker locker(&timerIdLock);
    22812322        if (timer_id == networkControlTimerId)
     
    22922333
    22932334    if (!netCmd.isEmpty())
    22942335    {
    2295         PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
     2336        PlayerContext *actx = GetPlayerWriteLock(-1, __FILE__, __LINE__);
    22962337        ProcessNetworkControlCommand(actx, netCmd);
    22972338        ReturnPlayerLock(actx);
    22982339        handled = true;
     
    23292370                }
    23302371                ReturnOSDLock(mctx, osd);
    23312372                lastProgramStringList.clear();
    2332                 setLastProgram(NULL);
     2373                SetLastProgram(NULL);
    23332374                VERBOSE(VB_PLAYBACK, LOC_ERR +
    23342375                            "Last Program File does not exist");
    23352376                jumpToProgram = false;
    23362377            }
    23372378            else
    2338                 ForceNextStateNone(mctx);
     2379                ForceNextStateNone(mctx, __LINE__);
    23392380        }
    23402381        else
    2341             ForceNextStateNone(mctx);
     2382            ForceNextStateNone(mctx, __LINE__);
    23422383
    23432384        ReturnPlayerLock(mctx);
    23442385
     
    23642405    if (handled)
    23652406        return;
    23662407
    2367     if (timer_id == pipChangeTimerId)
    2368     {
    2369         if (needToMakePIPChange > kPIPOff)
    2370         {
    2371             PlayerContext *mctx = GetPlayerWriteLock(0, __FILE__, __LINE__);
    2372             ClearOSD(mctx);
    2373 
    2374             switch (needToMakePIPChange)
    2375             {
    2376                 case (kPIPSwap):
    2377                     SwapPIP(mctx, 1, false);
    2378                     break;
    2379                 case (kPBPSwap):
    2380                     SwapPIP(mctx, 1, true);
    2381                     break;
    2382                 case (kPIPToPBP):
    2383                     TogglePIPState(mctx, needToMakePIPChange);
    2384                     break;
    2385                 case (kPBPToPIP):
    2386                     TogglePIPState(mctx, needToMakePIPChange);
    2387                     break;
    2388                 case (kPBPToggle):
    2389                 {
    2390                     if (mctx && (player.size() > 1))
    2391                         TeardownPlayer(mctx, GetPlayer(mctx, 1));
    2392                     else
    2393                         TogglePIPView(mctx, true);
    2394                     break;
    2395                 }
    2396             }
    2397             ReturnPlayerLock(mctx);
    2398 
    2399             needToMakePIPChange = kPIPOff;
    2400         }
    2401 
    2402         QMutexLocker locker(&timerIdLock);
    2403         if (pipChangeTimerId)
    2404             KillTimer(pipChangeTimerId);
    2405         pipChangeTimerId = 0;
    2406         handled = true;
    2407     }
    2408 
    2409     if (handled)
    2410         return;
    2411 
    24122408    if (timer_id == udpNotifyTimerId)
    24132409    {
    24142410        while (HasUDPNotifyEvent())
     
    25552551    {
    25562552        PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
    25572553        OSD *osd = GetOSDLock(actx);
    2558         OSDSet *oset = osd->GetSet("status");
     2554        OSDSet *oset = NULL;
     2555        if (osd)
     2556            oset = osd->GetSet("status");
    25592557        if (osd && oset && oset->Displaying() &&
    25602558            (StateIsLiveTV(actx->GetState()) ||
    25612559             StateIsPlaying(actx->GetState())))
     
    25802578            (mctx->nvp && mctx->nvp->IsErrored()) || mctx->IsErrored())
    25812579        {
    25822580            SetExitPlayer(true, false);
    2583             ForceNextStateNone(mctx);
     2581            ForceNextStateNone(mctx, __LINE__);
    25842582            error = true;
    25852583        }
    25862584
     
    25882586        {
    25892587            PlayerContext *ctx = GetPlayer(mctx, i);
    25902588            if (error || ctx->IsErrored())
    2591                 ForceNextStateNone(ctx);
     2589                ForceNextStateNone(ctx, __LINE__);
    25922590        }
    25932591        ReturnPlayerLock(mctx);
    25942592
     
    26002598    }
    26012599}
    26022600
     2601bool TV::HandlePxPTimerEvent(void)
     2602{
     2603    QString cmd = QString::null;
     2604
     2605    {
     2606        QMutexLocker locker(&timerIdLock);
     2607        if (changePxP.empty())
     2608        {
     2609            if (pipChangeTimerId)
     2610                KillTimer(pipChangeTimerId);
     2611            pipChangeTimerId = 0;
     2612            return true;
     2613        }
     2614        cmd = changePxP.dequeue();
     2615    }
     2616
     2617    PlayerContext *mctx = GetPlayerWriteLock(0, __FILE__, __LINE__);
     2618    PlayerContext *actx = GetPlayer(mctx, -1);
     2619
     2620    if (cmd == "TOGGLEPIPMODE")
     2621        PxPToggleView(actx, false);
     2622    else if (cmd == "TOGGLEPBPMODE")
     2623        PxPToggleView(actx, true);
     2624    else if (cmd == "CREATEPIPVIEW")
     2625        PxPCreateView(actx, false);
     2626    else if (cmd == "CREATEPBPVIEW")
     2627        PxPCreateView(actx, true);
     2628    else if (cmd == "SWAPPIP")
     2629        PxPSwap(mctx);
     2630    else if (cmd == "TOGGLEPIPSTATE")
     2631        PxPToggleType(mctx, !mctx->isPBP());
     2632
     2633    ReturnPlayerLock(mctx);
     2634
     2635    QMutexLocker locker(&timerIdLock);
     2636
     2637    if (pipChangeTimerId)
     2638        KillTimer(pipChangeTimerId);
     2639
     2640    if (changePxP.empty())
     2641        pipChangeTimerId = 0;
     2642    else
     2643        pipChangeTimerId = StartTimer(20, __LINE__);
     2644
     2645    return true;
     2646}
     2647
    26032648bool TV::HandleLCDTimerEvent(void)
    26042649{
    26052650    PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
     
    26542699    QObject::killTimer(id);
    26552700}
    26562701
    2657 void TV::ForceNextStateNone(PlayerContext *ctx)
     2702void TV::ForceNextStateNone(PlayerContext *ctx, int line)
    26582703{
    2659     ctx->ForceNextStateNone();
     2704    ctx->ForceNextStateNone(line);
    26602705    ScheduleStateChange(ctx);
    26612706}
    26622707
     
    26742719    errorRecoveryTimerId = StartTimer(1, __LINE__);
    26752720}
    26762721
    2677 void TV::SetExitPlayer(bool set_it, bool wants_to) const
     2722void TV::SetExitPlayerReal(bool set_it, bool wants_to, int line) const
    26782723{
     2724    VERBOSE(VB_IMPORTANT,
     2725            LOC + "SetExitPlayer("<<set_it<<","<<wants_to<<","<<line<<")");
     2726
    26792727    QMutexLocker locker(&timerIdLock);
    26802728    if (set_it)
    26812729    {
     
    27312779            continue;
    27322780        }
    27332781
    2734         ForceNextStateNone(ctx);
     2782        ForceNextStateNone(ctx, __LINE__);
    27352783        if (mctx == ctx)
    27362784        {
    27372785            endOfRecording = true;
     
    30653113
    30663114void TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e)
    30673115{
     3116    bool ignoreKeys = actx->IsNVPChangingBuffers();
    30683117#if DEBUG_ACTIONS
    3069     VERBOSE(VB_IMPORTANT, LOC + "ProcessKeypress() ignoreKeys: "<<ignoreKeys);
     3118    VERBOSE(VB_IMPORTANT, LOC + QString("ProcessKeypress() ignoreKeys: %1")
     3119            .arg(ignoreKeys));
    30703120#endif // DEBUG_ACTIONS
    30713121
    30723122    if (db_idle_timeout > 0)
     
    31913241    handled = handled || DVDMenuHandleAction(actx, actions, isDVD, isDVDStill);
    31923242    handled = handled || ActiveHandleAction(actx, actions, isDVD, isDVDStill);
    31933243    handled = handled || ToggleHandleAction(actx, actions, isDVD);
    3194     handled = handled || PIPHandleAction(actx, actions);
     3244    handled = handled || PxPHandleAction(actx, actions);
    31953245    handled = handled || FFRewHandleAction(actx, actions);
    31963246    handled = handled || ActivePostQHandleAction(actx, actions, isDVD);
    31973247
     
    32233273
    32243274bool TV::BrowseHandleAction(PlayerContext *ctx, const QStringList &actions)
    32253275{
    3226     bool handled = false;
    32273276    if (!browsemode)
    3228         return handled;
     3277        return false;
    32293278
    3230     handled = true;
     3279    bool handled = true;
    32313280
    32323281    if (has_action("UP", actions) || has_action("CHANNELUP", actions))
    32333282        BrowseDispInfo(ctx, BROWSE_UP);
     
    32543303        ToggleRecord(ctx);
    32553304    else
    32563305    {
     3306        handled = false;
    32573307        QStringList::const_iterator it = actions.begin();
    32583308        for (; it != actions.end(); ++it)
    32593309        {
     
    32623312            {
    32633313                CommitQueuedInput(ctx);
    32643314                BrowseEnd(ctx, false);
     3315                handled = true;
    32653316            }
    32663317        }
    32673318    }
    32683319
    32693320    // only pass-through actions listed below
    3270     return !(has_action("VOLUMEDOWN",      actions) ||
    3271              has_action("VOLUMEUP",        actions) ||
    3272              has_action("STRETCHINC",      actions) ||
    3273              has_action("STRETCHDEC",      actions) ||
    3274              has_action("MUTE",            actions) ||
    3275              has_action("TOGGLEASPECT",    actions) ||
    3276              has_action("TOGGLEPIPWINDOW", actions) ||
    3277              has_action("TOGGLEPIPMODE",   actions) ||
    3278              has_action("TOGGLEPIPSTATE",  actions) ||
    3279              has_action("SWAPPIP",         actions));
    3280 
    3281     // TODO FIXME this is never reached.. must be a logic error
    3282     return handled;
     3321    return handled ||
     3322        !(has_action("VOLUMEDOWN",      actions) ||
     3323          has_action("VOLUMEUP",        actions) ||
     3324          has_action("STRETCHINC",      actions) ||
     3325          has_action("STRETCHDEC",      actions) ||
     3326          has_action("MUTE",            actions) ||
     3327          has_action("TOGGLEASPECT",    actions) ||
     3328          has_action("TOGGLEPIPMODE",   actions) ||
     3329          has_action("TOGGLEPIPSTATE",  actions) ||
     3330          has_action("NEXTPIPWINDOW",   actions) ||
     3331          has_action("CREATEPIPVIEW",   actions) ||
     3332          has_action("CREATEPBPVIEW",   actions) ||
     3333          has_action("SWAPPIP",         actions));
    32833334}
    32843335
    32853336bool TV::ManualZoomHandleAction(PlayerContext *actx, const QStringList &actions)
     
    34673518        }
    34683519        else if (dialogname == "allowrecordingbox")
    34693520        {
    3470             int result = osd->GetDialogResponse(dialogname);
    3471 
    3472             if (result == 1)
    3473                 actx->recorder->CancelNextRecording(false);
    3474             else if (result == 2)
    3475                 StopLiveTV();
    3476             else if (result == 3)
    3477                 actx->recorder->CancelNextRecording(true);
     3521            HandleOSDAskAllowResponse();
    34783522        }
    34793523        else if (dialogname == "idletimeout")
    34803524        {
     
    38313875    {
    38323876        ClearOSD(ctx);
    38333877    }
    3834     else if (has_action("JUMPPREV", actions))
    3835     {
    3836         if (PromptRecGroupPassword(ctx))
    3837         {
    3838             ctx->nvp->SetBookmark();
    3839             SetExitPlayer(true, true);
    3840         }
    3841     }
    38423878    else if (has_action("VIEWSCHEDULED", actions))
    38433879        EditSchedule(ctx, kViewSchedule);
    3844     else if (has_action("JUMPREC", actions))
     3880    else if (HandleJumpToProgramAction(ctx, actions))
    38453881    {
    3846         if (gContext->GetNumSetting("JumpToProgramOSD", 1) &&
    3847             StateIsPlaying(ctx->GetState()))
    3848         {
    3849             if (jumpMenuTimerId)
    3850                 KillTimer(jumpMenuTimerId);
    3851             jumpMenuTimerId = StartTimer(1, __LINE__);
    3852         }
    3853         else if (RunPlaybackBoxPtr)
    3854             EditSchedule(ctx, kPlaybackBox);
    38553882    }
    38563883    else if (has_action("SIGNALMON", actions))
    38573884    {
     
    38983925    }
    38993926    else if (has_action("ESCAPE", actions))
    39003927    {
    3901         SetActive(ctx, 0);
     3928        VERBOSE(VB_IMPORTANT, LOC + "ESCAPE");
     3929
    39023930        osd = GetOSDLock(ctx);
    39033931        if (StateIsLiveTV(ctx->GetState()) &&
    39043932            (ctx->lastSignalMsgTime.elapsed() <
     
    39173945
    39183946        StopFFRew(ctx);
    39193947
     3948        bool do_exit = false;
     3949
    39203950        if (StateIsLiveTV(GetState(ctx)))
    39213951        {
    39223952            if (ctx->nvp &&
    3923                 (12 & gContext->GetNumSetting("PlaybackExitPrompt")))
     3953                (12 & db_playback_exit_prompt))
    39243954            {
    39253955                PromptStopWatchingRecording(ctx);
    39263956            }
    39273957            else
    39283958            {
    3929                 SetExitPlayer(true, true);
     3959                do_exit = true;
    39303960            }
    39313961        }
    39323962        else
    39333963        {
    39343964            if (ctx->nvp &&
    3935                 (5 & gContext->GetNumSetting("PlaybackExitPrompt")) &&
     3965                (5 & db_playback_exit_prompt) &&
    39363966                !underNetworkControl && !isDVDStill)
    39373967            {
    39383968                PromptStopWatchingRecording(ctx);
    39393969                return handled;
    39403970            }
    39413971            else if (ctx->nvp &&
    3942                      (gContext->GetNumSetting("PlaybackExitPrompt") == 2))
     3972                     (db_playback_exit_prompt == 2))
    39433973            {
    39443974                ctx->nvp->SetBookmark();
    39453975            }
    39463976            if (ctx->nvp &&
    3947                 gContext->GetNumSetting("AutomaticSetWatched", 0))
     3977                db_auto_set_watched)
    39483978            {
    39493979                ctx->nvp->SetWatched();
    39503980            }
    39513981            requestDelete = false;
    3952             SetExitPlayer(true, true);
     3982            do_exit = true;
    39533983        }
     3984
     3985        if (do_exit)
     3986        {
     3987            PlayerContext *mctx = GetPlayer(ctx, 0);
     3988            if (mctx != ctx)
     3989            { // A PIP is active, just tear it down..
     3990                PxPTeardownView(ctx);
     3991                return handled;
     3992            }
     3993            else
     3994            {
     3995                SetExitPlayer(true, true);
     3996            }
     3997        }
     3998
     3999        SetActive(ctx, 0, false);
    39544000    }
    39554001    else if (has_action("VOLUMEDOWN", actions))
    39564002        ChangeVolume(ctx, false);
     
    40754121    return handled;
    40764122}
    40774123
    4078 bool TV::PIPHandleAction(PlayerContext *ctx, const QStringList &actions)
     4124bool TV::PxPHandleAction(PlayerContext *ctx, const QStringList &actions)
    40794125{
    4080     bool handled = true;
    4081 
    40824126    if (!IsPIPSupported(ctx))
    4083         return handled;
     4127        return false;
    40844128
    4085     if (has_action("TOGGLEPIPMODE", actions))
    4086         TogglePIPView(ctx);
    4087     else if (has_action("TOGGLEPBPMODE", actions))
     4129    bool handled = true;
    40884130    {
    4089         needToMakePIPChange = kPBPToggle;
    4090         if (pipChangeTimerId)
    4091             KillTimer(pipChangeTimerId);
    4092         pipChangeTimerId = StartTimer(1, __LINE__);
     4131        QMutexLocker locker(&timerIdLock);
     4132
     4133        if (has_action("TOGGLEPIPMODE", actions))
     4134            changePxP.enqueue("TOGGLEPIPMODE");
     4135        else if (has_action("TOGGLEPBPMODE", actions))
     4136            changePxP.enqueue("TOGGLEPBPMODE");
     4137        else if (has_action("CREATEPIPVIEW", actions))
     4138            changePxP.enqueue("CREATEPIPVIEW");
     4139        else if (has_action("CREATEPBPVIEW", actions))
     4140            changePxP.enqueue("CREATEPBPVIEW");
     4141        else if (has_action("SWAPPIP", actions))
     4142            changePxP.enqueue("SWAPPIP");
     4143        else if (has_action("TOGGLEPIPSTATE", actions))
     4144            changePxP.enqueue("TOGGLEPIPSTATE");
     4145        else
     4146            handled = false;
     4147       
     4148        if (!changePxP.empty() && !pipChangeTimerId)
     4149            pipChangeTimerId = StartTimer(1, __LINE__);
    40934150    }
    4094     else if (has_action("SWAPPIP", actions))
     4151
     4152    if (has_action("NEXTPIPWINDOW", actions))
    40954153    {
    4096         needToMakePIPChange = (ctx->isPBP()) ? kPBPSwap : kPIPSwap;
    4097         if (pipChangeTimerId)
    4098             KillTimer(pipChangeTimerId);
    4099         pipChangeTimerId = StartTimer(1, __LINE__);
     4154        SetActive(ctx, -1, true);
     4155        handled = true;
    41004156    }
    4101     else if (has_action("TOGGLEPIPSTATE", actions))
    4102     {
    4103         if (ctx && player.size() > 1)
    4104         {
    4105             needToMakePIPChange = (ctx->isPBP()) ? kPBPToPIP : kPIPToPBP;
    4106             if (pipChangeTimerId)
    4107                 KillTimer(pipChangeTimerId);
    4108             pipChangeTimerId = StartTimer(1, __LINE__);
    4109         }
    4110     }
    4111     else if (has_action("TOGGLEPIPWINDOW", actions))
    4112         ToggleActiveWindow(ctx, 1);
    4113     else
    4114         handled = false;
    41154157
    41164158    return handled;
    41174159}
     
    41314173            if (isDVD && ctx->buffer && ctx->buffer->DVD())
    41324174                ctx->buffer->DVD()->JumpToTitle(false);
    41334175
    4134             int clearpos = gContext->GetNumSetting("AltClearSavedPosition", 1);
    41354176            ctx->LockDeleteNVP(__FILE__, __LINE__);
    41364177            if (ctx->nvp)
    41374178            {
    4138                 if (clearpos && ctx->nvp->GetBookmark())
     4179                if (db_toggle_bookmark && ctx->nvp->GetBookmark())
    41394180                    ctx->nvp->ClearBookmark();
    41404181                else
    41414182                    ctx->nvp->SetBookmark();
     
    42004241void TV::ProcessNetworkControlCommand(PlayerContext *ctx,
    42014242                                      const QString &command)
    42024243{
     4244    bool ignoreKeys = ctx->IsNVPChangingBuffers();
    42034245#ifdef DEBUG_ACTIONS
    42044246    VERBOSE(VB_IMPORTANT, LOC + "ProcessNetworkControlCommand(" +
    4205             QString("%1)").arg(command));
     4247            QString("%1) ignoreKeys: %2").arg(command).arg(ignoreKeys));
    42064248#endif
    42074249
     4250    if (ignoreKeys)
     4251    {
     4252        VERBOSE(VB_IMPORTANT, LOC_WARN +
     4253                "Ignoring network control command"
     4254                "\n\t\t\tbecause ignoreKeys is set");
     4255        return;
     4256    }
     4257
    42084258    QStringList tokens = command.split(" ", QString::SkipEmptyParts);
    42094259    if (tokens.size() < 2)
    42104260    {
     
    43654415    {
    43664416        if (ctx->nvp)
    43674417            ctx->nvp->SetBookmark();
    4368         if (ctx->nvp && gContext->GetNumSetting("AutomaticSetWatched", 0))
     4418        if (ctx->nvp && db_auto_set_watched)
    43694419            ctx->nvp->SetWatched();
    43704420        SetExitPlayer(true, true);
    43714421    }
     
    45494599        swap(player[0],player[1]);
    45504600        player[0]->SetPIPState(kPIPOff);
    45514601        // End the old main context..
    4552         ForceNextStateNone(mctx);
     4602        ForceNextStateNone(mctx, __LINE__);
    45534603    }
    45544604
    45554605    VERBOSE(VB_PLAYBACK, LOC + "CreatePBP() -- end : "<<ok);
     
    45574607}
    45584608
    45594609/**
    4560  * \brief tear down remaining PBP video and restore
    4561  * fullscreen display
     4610 * \brief create PIP.
     4611 * \param info programinfo for PIP to create. is NULL for LiveTV PIP
    45624612 */
    4563 void TV::TeardownPBP(PlayerContext *ctx)
     4613bool TV::CreatePIP(PlayerContext *ctx, const ProgramInfo *info)
    45644614{
    45654615    PlayerContext *mctx = GetPlayer(ctx, 0);
     4616    if (!mctx)
     4617        return false;
    45664618
    4567     if (!mctx->nvp || !mctx->nvp->IsPlaying() ||
    4568         mctx->GetPIPState() < kPBPLeft || exitPlayerTimerId)
    4569     {
    4570         return;
    4571     }
     4619    VERBOSE(VB_PLAYBACK, LOC + "CreatePIP -- begin");
    45724620
    4573     VERBOSE(VB_PLAYBACK, LOC  + "TeardownPBP -- begin");
    4574 
    4575     long long mctx_frame = mctx->nvp->GetFramesPlayed();
    4576     mctx->PIPTeardown();
    4577     mctx->SetPIPState(kPIPOff);
    4578     mctx->buffer->Seek(0, SEEK_SET);
    4579     if (mctx->CreateNVP(this, myWindow, mctx->GetState(),
    4580                         embedWinID, &embedBounds))
     4621    if (mctx->isPBP())
    45814622    {
    4582         ScheduleStateChange(mctx);
    4583         mctx->StartOSD(this);
    4584         mctx->nvp->JumpToFrame(mctx_frame);
    4585 
    4586         //TODO find out where wantsToQuit is been enabled
    4587         wantsToQuit = false;
    4588 
    4589         SetSpeedChangeTimer(25, __LINE__);
     4623        VERBOSE(VB_IMPORTANT, LOC_ERR +
     4624                "CreatePIP called, but we're in PBP mode already, ignoring.");
     4625        return false;
    45904626    }
    4591     else
    4592         ForceNextStateNone(mctx);
    45934627
    4594     VERBOSE(VB_PLAYBACK, LOC + "TeardownPBP -- end");
    4595 }
     4628    bool has_vo = false, using_xvmc = false;
    45964629
    4597 /**
    4598  * \brief create PIP.
    4599  * \param info programinfo for PIP to create. is NULL for LiveTV PIP
    4600  */
    4601 bool TV::CreatePIP(PlayerContext *ctx, const ProgramInfo *info)
    4602 {
    4603     bool ret = false;
    4604     PlayerContext *mctx = GetPlayer(ctx, 0);
    4605 
    4606     VERBOSE(VB_PLAYBACK, LOC + "Starting CreatePIP -- begin");
    4607     bool using_xvmc = false;
    4608     QRect rect;
    4609     PIPLocation pip_location;
    4610     if (mctx && mctx->nvp && mctx->nvp->getVideoOutput())
     4630    mctx->LockDeleteNVP(__FILE__, __LINE__);
     4631    if (mctx->nvp && mctx->nvp->getVideoOutput())
    46114632    {
    4612         pip_location = (PIPLocation)gContext->GetNumSetting("PIPLocation", 0);
    4613         rect = mctx->nvp->getVideoOutput()->GetPIPRect(pip_location, NULL);
     4633        has_vo = true;
    46144634        using_xvmc = mctx->nvp->getVideoOutput()->hasMCAcceleration();
    46154635    }
    4616     else
    4617     {
    4618         return ret;
    4619     }
     4636    mctx->UnlockDeleteNVP(__FILE__, __LINE__);
    46204637
     4638    if (!has_vo)
     4639        return false;
     4640
    46214641    /* TODO implement PIP solution for Xvmc playback */
    46224642    if (using_xvmc)
    46234643    {
    46244644        VERBOSE(VB_IMPORTANT, LOC + "PiP is not supported for XvMC");
    4625         return ret;
     4645        return false;
    46264646    }
    46274647
    46284648    PlayerContext *pipctx = new PlayerContext();
     
    46434663    else
    46444664    {
    46454665        delete pipctx;
    4646         return ret;
     4666        return false;
    46474667    }
    46484668
    46494669    // this is safe because we are already holding lock for ctx
    46504670    player.push_back(pipctx);
    46514671
    4652     ret = true;
    4653     return ret;
     4672    return true;
    46544673}
    46554674
    46564675int TV::find_player_index(const PlayerContext *ctx) const
     
    46624681}
    46634682
    46644683bool TV::StartPlayer(PlayerContext *mctx, PlayerContext *ctx,
    4665                      TVState desiredState, bool ispip)
     4684                     TVState desiredState)
    46664685{
    4667     if (ispip)
    4668         return StartPIPPlayer(mctx, ctx, desiredState);
     4686    bool wantPiP = ctx->isPIP();
    46694687
    4670     VERBOSE(VB_IMPORTANT, LOC + QString("StartPlayer(%1, %2, not pip) -- begin")
    4671             .arg(find_player_index(ctx)).arg(StateToString(desiredState)));
     4688    VERBOSE(VB_IMPORTANT, LOC + QString("StartPlayer(%1, %2, %3) -- begin")
     4689            .arg(find_player_index(ctx)).arg(StateToString(desiredState))
     4690            .arg((wantPiP) ? "PiP" : "main"));
    46724691
     4692    if (wantPiP)
     4693    {
     4694        if (mctx->nvp && ctx->StartPIPPlayer(this, desiredState) &&
     4695            ctx->nvp && PIPAddPlayer(mctx, ctx))
     4696        {
     4697            ScheduleStateChange(ctx);
     4698            VERBOSE(VB_IMPORTANT, "StartPlayer PiP -- end : ok");
     4699            return true;
     4700        }
     4701
     4702        ForceNextStateNone(ctx, __LINE__);
     4703        VERBOSE(VB_IMPORTANT, "StartPlayer PiP -- end : !ok");
     4704        return false;
     4705    }
     4706
    46734707    InitUDPNotifyEvent();
    46744708    bool ok = false;
    46754709    if (ctx->UseNullVideo())
     
    46774711        ok = ctx->CreateNVP(this, NULL, desiredState, 0, NULL);
    46784712        ScheduleStateChange(ctx);
    46794713        if (ok)
    4680             SetPIPPlayer(mctx, ctx);
     4714            ok = PIPAddPlayer(mctx, ctx);
    46814715    }
    46824716    else
    46834717    {
     
    46924726        SetSpeedChangeTimer(25, __LINE__);
    46934727    }
    46944728
    4695     VERBOSE(VB_IMPORTANT, LOC +
    4696             QString("StartPlayer(%1, %2, not pip) -- end %3")
     4729    VERBOSE(VB_IMPORTANT, LOC + QString("StartPlayer(%1, %2, %3) -- end %4")
    46974730            .arg(find_player_index(ctx)).arg(StateToString(desiredState))
    4698             .arg((ok) ? "ok" : "error"));
     4731            .arg((wantPiP) ? "PiP" : "main").arg((ok) ? "ok" : "error"));
    46994732
    47004733    return ok;
    47014734}
    47024735
    4703 bool TV::StartPIPPlayer(PlayerContext *mctx, PlayerContext *ctx,
    4704                         TVState desiredState)
     4736/// \brief Maps NVP of software scaled PIP to the NVP of the main player
     4737bool TV::PIPAddPlayer(PlayerContext *mctx, PlayerContext *pipctx)
    47054738{
    4706     if (mctx->nvp && ctx->StartPIPPlayer(this, desiredState) && ctx->nvp)
     4739    if (!mctx || !pipctx)
     4740        return false;
     4741
     4742    if (!mctx->nvp || !mctx->nvp->IsPlaying())
     4743        return false;
     4744
     4745    bool ok = false, addCondition = false;
     4746    if (pipctx->nvp)
    47074747    {
    4708         SetPIPPlayer(mctx, ctx);
    4709         ScheduleStateChange(ctx);
    4710         return true;
     4748        if (pipctx->nvp->UsingNullVideo())
     4749        {
     4750            addCondition = true;
     4751            PIPLocation loc = mctx->nvp->GetNextPIPLocation();
     4752            ok = mctx->nvp->AddPIPPlayer(pipctx->nvp, loc, 4000);
     4753        }
     4754        else if (pipctx->isPIP())
     4755        {
     4756            ok = ResizePIPWindow(pipctx);
     4757        }
    47114758    }
    4712     return false;
     4759
     4760    VERBOSE(VB_IMPORTANT,
     4761            QString("AddPIPPlayer null: %1 isPIP: %2 addCond: %3 ok: %4")
     4762            .arg(pipctx->nvp->UsingNullVideo())
     4763            .arg(pipctx->isPIP()).arg(addCondition).arg(ok));
     4764
     4765    return ok;
    47134766}
    47144767
    4715 /**
    4716  * \brief maps NVP of software scaled PIP  to the NVP of player
    4717  * context 0.
    4718  */
    4719 void TV::SetPIPPlayer(PlayerContext *mctx, PlayerContext *pipctx)
     4768/// \brief Unmaps NVP of software scaled PIP from the NVP of the main player
     4769bool TV::PIPRemovePlayer(PlayerContext *mctx, PlayerContext *pipctx)
    47204770{
    47214771    if (!mctx || !pipctx)
     4772        return false;
     4773
     4774    bool ok = false;
     4775    if (mctx->nvp && pipctx->nvp)
     4776        ok = mctx->nvp->RemovePIPPlayer(pipctx->nvp, 4000);
     4777
     4778    VERBOSE(VB_IMPORTANT, QString("PIPRemovePlayer ok: %1").arg(ok));
     4779
     4780    return ok;
     4781}
     4782
     4783/// \brief start/stop PIP/PBP
     4784void TV::PxPToggleView(PlayerContext *actx, bool wantPBP)
     4785{
     4786    if (player.size() <= 1)
     4787        PxPCreateView(actx, wantPBP);
     4788    else
     4789        PxPTeardownView(actx);
     4790}
     4791
     4792/// \brief start PIP/PBP
     4793void TV::PxPCreateView(PlayerContext *actx, bool wantPBP)
     4794{
     4795    if (!actx)
    47224796        return;
    47234797
    4724     bool setCondition = false;
    4725     if (mctx->nvp && mctx->nvp->IsPlaying())
     4798    QString err_msg = QString::null;
     4799    if ((player.size() > kMaxPBPCount) && (wantPBP || actx->isPBP()))
    47264800    {
    4727         if (pipctx->nvp)
    4728         {
    4729             if (pipctx->nvp->UsingNullVideo())
    4730                 setCondition = true;
    4731             else if (playerActive == 0 && pipctx->isPIP())
    4732                 ResizePIPWindow(pipctx);
    4733         }
    4734         else
    4735             setCondition = true;
     4801        err_msg = tr("Sorry, PBP only supports %1 video streams")
     4802            .arg(kMaxPBPCount);
     4803    }
    47364804
    4737         if (setCondition)
    4738         {
    4739             QMutex piplock;
    4740             piplock.lock();
    4741             mctx->nvp->SetPIPPlayer(pipctx->nvp);
    4742             pipPlayerSetCondition.wait(&piplock);
    4743             piplock.unlock();
    4744         }
     4805    if ((player.size() > kMaxPIPCount) &&
     4806        (!wantPBP || GetPlayer(actx,1)->isPIP()))
     4807    {
     4808        err_msg = tr("Sorry, PIP only supports %1 video streams")
     4809            .arg(kMaxPIPCount);
    47454810    }
     4811
     4812    if ((player.size() > 1) && (wantPBP ^ actx->isPBP()))
     4813        err_msg = tr("Sorry, can not mix PBP and PIP views");
     4814
     4815    if (!err_msg.isEmpty())
     4816    {
     4817        VERBOSE(VB_IMPORTANT, LOC_ERR + err_msg);
     4818        OSD *osd = GetOSDLock(actx);
     4819        if (osd)
     4820            osd->SetSettingsText(err_msg, 3);
     4821        ReturnOSDLock(actx, osd);
     4822        return;
     4823    }
     4824
     4825    bool ok = false;
     4826    if (wantPBP)
     4827        ok = CreatePBP(actx, NULL);
     4828    else
     4829        ok = CreatePIP(actx, NULL);
     4830    actx = GetPlayer(actx, -1); // CreatePBP/PIP mess with ctx's
     4831
     4832    QString msg = (ok) ?
     4833        ((wantPBP) ? tr("Creating PBP")      : tr("Creating PIP")) :
     4834        ((wantPBP) ? tr("Cannot create PBP") : tr("Cannot create PIP"));
     4835   
     4836    OSD *osd = GetOSDLock(actx);
     4837    if (osd)
     4838        osd->SetSettingsText(msg, 3);
     4839    ReturnOSDLock(actx, osd);
    47464840}
    47474841
    4748 /**
    4749  * \brief start/stop PIP/PBP
    4750  *
    4751  */
    4752 void TV::TogglePIPView(PlayerContext *lctx, bool ispbp)
     4842/// \brief stop PIP/PBP
     4843void TV::PxPTeardownView(PlayerContext *actx)
    47534844{
     4845    VERBOSE(VB_IMPORTANT, "PxPTeardownView()");
     4846
    47544847    QString msg;
     4848    PlayerContext *mctx = GetPlayer(actx, 0);
     4849    PlayerContext *dctx = NULL;
     4850    dctx = (mctx != actx)       ? actx               : dctx;
     4851    dctx = (2 == player.size()) ? GetPlayer(actx, 1) : dctx;
    47554852
    4756     if (player.size() > 1)
     4853    SetActive(actx, 0, false);
     4854
     4855    PlayerContext *ctx1 = GetPlayer(actx, 1);
     4856    msg = (ctx1->isPIP()) ? tr("Stopping PIP") : tr("Stopping PBP");
     4857    if (dctx)
    47574858    {
    4758         PlayerContext *ctx = player.back();
    4759         msg = "Stopping ";
    4760         msg += (ctx->isPIP()) ? "PIP" : "PBP";
    4761         if (playerActive != 0)
    4762             ToggleActiveWindow(lctx, 1);
    4763         ForceNextStateNone(ctx);
     4859        ForceNextStateNone(dctx, __LINE__);
    47644860    }
    47654861    else
    47664862    {
    4767         bool ok = false;
    4768         if (ispbp)
    4769             ok = CreatePBP(lctx, NULL);
    4770         else
    4771             ok = CreatePIP(lctx, NULL);
    4772         lctx = GetPlayer(lctx, -1); // CreatePBP/PIP mess with ctx's
    4773 
    4774         if (!ok)
     4863        if (player.size() > 2)
    47754864        {
    4776             msg = "Cannot Start ";
    4777             msg += (ispbp) ? "PBP" : "PIP";
     4865            msg = (ctx1->isPIP()) ?
     4866                tr("Stopping all PIPs") : tr("Stopping all PBPs");
    47784867        }
     4868
     4869        for (uint i = player.size() - 1; i > 0; i--)
     4870            ForceNextStateNone(GetPlayer(actx,i), __LINE__);
    47794871    }
    47804872
    4781     OSD *osd = GetOSDLock(lctx);
     4873    OSD *osd = GetOSDLock(mctx);
    47824874    if (osd)
    47834875        osd->SetSettingsText(msg, 3);
    4784     ReturnOSDLock(lctx, osd);
     4876    ReturnOSDLock(mctx, osd);
    47854877}
    47864878
    47874879/**
    47884880* \brief Change PIP View from PIP to PBP and visa versa
    47894881*/
    4790 void TV::TogglePIPState(PlayerContext *mctx, PIPState changeTo)
     4882void TV::PxPToggleType(PlayerContext *mctx, bool wantPBP)
    47914883{
    4792     QString before = (changeTo == kPIPToPBP) ? "PIP" : "PBP";
    4793     QString after  = (changeTo == kPIPToPBP) ? "PBP" : "PIP";
     4884    const QString before = (mctx->isPBP()) ? "PBP" : "PIP";
     4885    const QString after  = (wantPBP)       ? "PBP" : "PIP";
     4886
    47944887    VERBOSE(VB_PLAYBACK, LOC +
    4795             QString("TogglePIPState() converting from %1 to %2 -- begin")
    4796                     .arg(before).arg(after));
     4888            QString("PxPToggleType() converting from %1 to %2 -- begin")
     4889            .arg(before).arg(after));
    47974890
    4798     if (2 != player.size())
     4891    if (mctx->isPBP() == wantPBP)
    47994892    {
    4800         VERBOSE(VB_IMPORTANT, LOC_ERR +
    4801                 QString("TogglePIPState() -- end: # player contexts must be 2, "
    4802                         "but it is currently %1").arg(player.size()));
     4893        VERBOSE(VB_IMPORTANT, LOC_WARN +
     4894                "PxPToggleType() -- end: already in desired mode");
    48034895        return;
    48044896    }
    48054897
    4806     SetActive(mctx, 0);
     4898    uint max_cnt = min(kMaxPBPCount, kMaxPIPCount);
     4899    if (player.size() > max_cnt)
     4900    {
     4901        VERBOSE(VB_IMPORTANT, LOC_ERR +
     4902                QString("PxPToggleType() -- end: "
     4903                        "# player contexts must be %1 or less, "
     4904                        "but it is currently %1")
     4905                .arg(max_cnt).arg(player.size()));
    48074906
    4808     PlayerContext *pipctx = GetPlayer(mctx, 1);
     4907        QString err_msg = tr("Too many views to switch");
    48094908
    4810     if (!mctx->nvp || !pipctx->nvp || !pipctx->nvp->IsPlaying())
     4909        PlayerContext *actx = GetPlayer(mctx, -1);
     4910        OSD *osd = GetOSDLock(actx);
     4911        if (osd)
     4912            osd->SetSettingsText(err_msg, 3);
     4913        ReturnOSDLock(actx, osd);
    48114914        return;
     4915    }
    48124916
     4917    for (uint i = 0; i < player.size(); i++)
     4918    {
     4919        PlayerContext *ctx = GetPlayer(mctx, i);
     4920        ctx->LockDeleteNVP(__FILE__, __LINE__);
     4921        if (!ctx->nvp || !ctx->nvp->IsPlaying())
     4922        {
     4923            ctx->UnlockDeleteNVP(__FILE__, __LINE__);
     4924            VERBOSE(VB_IMPORTANT, LOC_ERR + "PxPToggleType() -- end: " +
     4925                    QString("nvp #%1 is not active, exiting without "
     4926                            "doing anything to avoid danger").arg(i));
     4927            return;
     4928        }
     4929        ctx->UnlockDeleteNVP(__FILE__, __LINE__);
     4930    }
     4931
    48134932    MuteState mctx_mute = mctx->nvp->GetMuteState();
    48144933
    4815     vector<long long> pos;
    4816     pos.push_back(mctx->nvp->GetFramesPlayed());
    4817     pos.push_back(pipctx->nvp->GetFramesPlayed());
     4934    vector<long long> pos = TeardownAllNVPs(mctx);
    48184935
    4819     TeardownPIPWindows(mctx);
    4820 
    4821     if (changeTo == kPIPToPBP)
     4936    if (wantPBP)
    48224937    {
    4823         mctx->SetPIPState(kPBPLeft);
    4824         pipctx->SetPIPState(kPBPRight);
     4938        GetPlayer(mctx, 0)->SetPIPState(kPBPLeft);
     4939        GetPlayer(mctx, 1)->SetPIPState(kPBPRight);
    48254940    }
    48264941    else
    48274942    {
    4828         mctx->SetPIPState(kPIPOff);
    4829         pipctx->SetPIPState(kPIPonTV);
    4830         pipctx->SetNullVideo(true);
     4943        GetPlayer(mctx, 0)->SetPIPState(kPIPOff);
     4944        for (uint i = 1; i < player.size(); i++)
     4945        {
     4946            GetPlayer(mctx, i)->SetPIPState(kPIPonTV);
     4947            GetPlayer(mctx, i)->SetNullVideo(true);
     4948        }
    48314949    }
    48324950
    4833     RestartPIPWindows(mctx, pos, mctx_mute);
     4951    RestartAllNVPs(mctx, pos, mctx_mute);
    48344952
    48354953    VERBOSE(VB_PLAYBACK, LOC +
    4836             QString("TogglePIPState() converting from %1 to %2 -- end")
     4954            QString("PxPToggleType() converting from %1 to %2 -- end")
    48374955            .arg(before).arg(after));
    48384956}
    48394957
    4840 void TV::ToggleActiveWindow(PlayerContext *lctx, int ctx_index)
    4841 {
    4842     if (player.size() == 1 || ctx_index < 1)
    4843         return;
    4844 
    4845     ClearOSD(lctx);
    4846 
    4847     PlayerContext *ctx = GetPlayer(lctx, ctx_index);
    4848     if (ctx->nvp)
    4849     {
    4850         int new_index = 0;
    4851         lockTimerOn = false;
    4852 
    4853         if (playerActive != ctx_index)
    4854             new_index = ctx_index;
    4855 
    4856         SetActive(lctx, new_index);
    4857     }
    4858 
    4859     PlayerContext *actx = GetPlayer(lctx, -1);
    4860     OSD *osd = GetOSDLock(actx);
    4861     if (osd)
    4862         osd->SetSettingsText(tr("Changed Active Window"), 3);
    4863     ReturnOSDLock(actx, osd);
    4864 }
    4865 
    48664958/**
    4867  * \brief wake TV thread when pip player var is set in the main NVP
    4868  */
    4869 void TV::WakeSetPIPPlayerCond(void)
    4870 {
    4871     pipPlayerSetCondition.wakeAll();
    4872 }
    4873 
    4874 /**
    48754959 * \brief resize PIP Window. done when changing channels or swapping PIP
    48764960 */
    4877 void TV::ResizePIPWindow(PlayerContext *ctx)
     4961bool TV::ResizePIPWindow(PlayerContext *ctx)
    48784962{
    4879     PlayerContext *actx = GetPlayer(ctx, -1);
    4880     PIPLocation loc = (PIPLocation) gContext->GetNumSetting("PIPLocation", 0);
    4881     if (actx->nvp && ctx->nvp)
     4963    VERBOSE(VB_PLAYBACK, LOC + "ResizePIPWindow -- begin");
     4964    PlayerContext *mctx = GetPlayer(ctx, 0);
     4965    if (mctx->nvp && ctx->nvp)
    48824966    {
    4883         QRect rect =
    4884             actx->nvp->getVideoOutput()->GetPIPRect(loc, ctx->nvp, false);
    4885         ctx->ResizePIPWindow(rect);
     4967        QRect rect;
     4968
     4969        mctx->LockDeleteNVP(__FILE__, __LINE__);
     4970        if (mctx->nvp)
     4971        {
     4972            PIPLocation loc = mctx->nvp->GetNextPIPLocation();
     4973            VERBOSE(VB_PLAYBACK, LOC + "ResizePIPWindow -- loc "<<loc);
     4974            if (loc != kPIP_END)
     4975            {
     4976                rect = mctx->nvp->getVideoOutput()->GetPIPRect(
     4977                    loc, ctx->nvp, false);
     4978            }
     4979        }
     4980        mctx->UnlockDeleteNVP(__FILE__, __LINE__);
     4981
     4982        if (rect.isValid())
     4983        {
     4984            ctx->ResizePIPWindow(rect);
     4985            VERBOSE(VB_PLAYBACK, LOC + "ResizePIPWindow -- end : ok");
     4986            return true;
     4987        }
    48864988    }
     4989    VERBOSE(VB_PLAYBACK, LOC + "ResizePIPWindow -- end : !ok");
     4990    return false;
    48874991}
    48884992
    48894993bool TV::IsPIPSupported(const PlayerContext *ctx) const
     
    49025006    return yes;
    49035007}
    49045008
    4905 /**
    4906 * \brief Teardown all NVP's in preparation for PIP Swap or change from PIP <-> PBP
    4907 */
    4908 void TV::TeardownPIPWindows(PlayerContext *lctx)
     5009/** \brief Teardown all NVP's in preparation for PxP Swap or
     5010 *         change from PIP -> PBP or PBP -> PIP
     5011 */
     5012vector<long long> TV::TeardownAllNVPs(PlayerContext *lctx)
    49095013{
    4910     SetActive(lctx, 0);
     5014    vector<long long> pos;
     5015    for (uint i = 0; i < player.size(); i++)
     5016    {
     5017        const PlayerContext *ctx = GetPlayer(lctx, i);
     5018        ctx->LockDeleteNVP(__FILE__, __LINE__);
     5019        pos.push_back((ctx->nvp) ? ctx->nvp->GetFramesPlayed() : 0);
     5020        ctx->UnlockDeleteNVP(__FILE__, __LINE__);
     5021    }
    49115022
    49125023    for (uint i = 0; i < player.size(); i++)
    49135024    {
    49145025        PlayerContext *ctx = GetPlayer(lctx, i);
    49155026        ctx->PIPTeardown();
    49165027    }
     5028
     5029    return pos;
    49175030}
    49185031
    49195032/**
     5033 * \brief tear down remaining PBP video and restore
     5034 * fullscreen display
     5035 */
     5036void TV::PBPRestartMainNVP(PlayerContext *mctx)
     5037{
     5038    VERBOSE(VB_PLAYBACK, LOC  + "PBPRestartMainNVP -- begin");
     5039
     5040    mctx->LockDeleteNVP(__FILE__, __LINE__);
     5041    if (!mctx->nvp || !mctx->nvp->IsPlaying() ||
     5042        mctx->GetPIPState() != kPBPLeft || exitPlayerTimerId)
     5043    {
     5044        mctx->UnlockDeleteNVP(__FILE__, __LINE__);
     5045        VERBOSE(VB_PLAYBACK, LOC  + "PBPRestartMainNVP -- end !ok !valid");
     5046        return;
     5047    }
     5048
     5049    long long mctx_frame = mctx->nvp->GetFramesPlayed();
     5050    mctx->UnlockDeleteNVP(__FILE__, __LINE__);
     5051
     5052    mctx->PIPTeardown();
     5053    mctx->SetPIPState(kPIPOff);
     5054    mctx->buffer->Seek(0, SEEK_SET);
     5055
     5056    if (mctx->CreateNVP(this, myWindow, mctx->GetState(),
     5057                        embedWinID, &embedBounds))
     5058    {
     5059        ScheduleStateChange(mctx);
     5060        mctx->StartOSD(this);
     5061        mctx->nvp->JumpToFrame(mctx_frame);
     5062        SetSpeedChangeTimer(25, __LINE__);
     5063        VERBOSE(VB_PLAYBACK, LOC + "PBPRestartMainNVP -- end ok");
     5064        return;
     5065    }
     5066
     5067    ForceNextStateNone(mctx, __LINE__);
     5068    VERBOSE(VB_PLAYBACK, LOC +
     5069            "PBPRestartMainNVP -- end !ok NVP did not restart");
     5070}
     5071
     5072/**
    49205073* \brief Recreate Main and PIP windows. Could be either PIP or PBP views.
    49215074*/
    4922 void TV::RestartPIPWindows(PlayerContext *lctx,
    4923                            const vector<long long> &pos,
    4924                            MuteState mctx_mute)
     5075void TV::RestartAllNVPs(PlayerContext *lctx,
     5076                        const vector<long long> &pos,
     5077                        MuteState mctx_mute)
    49255078{
    4926     QString loc = LOC + QString("RestartPIPWindows()");
     5079    QString loc = LOC + QString("RestartAllNVPs(): ");
    49275080
    49285081    PlayerContext *mctx = GetPlayer(lctx, 0);
    49295082
     
    49355088    if (StateIsLiveTV(mctx->GetState()))
    49365089        mctx->buffer->Unpause();
    49375090
    4938     bool ok = StartPlayer(mctx, mctx, mctx->GetState(), false);
     5091    bool ok = StartPlayer(mctx, mctx, mctx->GetState());
    49395092
    49405093    if (ok)
    49415094    {
     
    49455098    {
    49465099        VERBOSE(VB_IMPORTANT, loc +
    49475100                "Failed to restart new main context (was pip context)");
    4948         ForceNextStateNone(mctx);
     5101        ForceNextStateNone(mctx, __LINE__);
    49495102        return;
    49505103    }
    49515104
     
    49585111        if (StateIsLiveTV(pipctx->GetState()))
    49595112            pipctx->buffer->Unpause();
    49605113
    4961         ok = StartPlayer(mctx, pipctx, pipctx->GetState(), pipctx->isPIP());
     5114        ok = StartPlayer(mctx, pipctx, pipctx->GetState());
    49625115
    49635116        if (ok)
    49645117        {
     
    49705123        { // TODO print OSD informing user of Swap failure ?
    49715124            VERBOSE(VB_IMPORTANT, loc +
    49725125                    "Failed to restart new pip context (was main context)");
    4973             ForceNextStateNone(pipctx);
     5126            ForceNextStateNone(pipctx, __LINE__);
    49745127        }
    49755128    }
    49765129
     
    49805133        mctx->nvp->SetMuteState(mctx_mute);
    49815134}
    49825135
    4983 void TV::SwapPIP(PlayerContext *lctx, int ctx_index, bool ispbp)
     5136void TV::PxPSwap(PlayerContext *mctx, int ctx_index)
    49845137{
    49855138    lockTimerOn = false;
    49865139
    4987     PlayerContext *mctx = GetPlayer(lctx, 0);
    4988     if (player.size() == 1)
     5140    if (player.size() <= 1 || !mctx)
    49895141        return;
    49905142
    4991     PlayerContext *pipctx = GetPlayer(lctx, ctx_index);
     5143    PlayerContext *pipctx = GetPlayer(mctx, ctx_index);
     5144    ctx_index = find_player_index(pipctx);
     5145    if (ctx_index < 0 || !pipctx)
     5146        return;
    49925147
    49935148    if (!mctx->nvp || !pipctx->nvp || !pipctx->nvp->IsPlaying())
    49945149        return;
     
    49965151    VERBOSE(VB_PLAYBACK, LOC + "SwapPIP -- begin");
    49975152
    49985153    MuteState mctx_mute = mctx->nvp->GetMuteState();
    4999 
    5000     vector<long long> pos;
    5001     for (uint i = 0; i < player.size(); i++)
    5002     {
    5003         const PlayerContext *ctx = GetPlayer(lctx, i);
    5004         ctx->LockDeleteNVP(__FILE__, __LINE__);
    5005         pos.push_back((ctx->nvp) ? ctx->nvp->GetFramesPlayed() : 0);
    5006         ctx->UnlockDeleteNVP(__FILE__, __LINE__);
    5007     }
    5008 
    50095154    bool useNullVideo = pipctx->UseNullVideo();
    50105155
    5011     TeardownPIPWindows(lctx);
     5156    vector<long long> pos = TeardownAllNVPs(mctx);
    50125157
    5013     swap(player[0], player[ctx_index]);
    5014     swap(pos[0],    pos[ctx_index]);
    5015 
    5016     pipctx->SetPIPState((ispbp) ? kPBPLeft: kPIPOff);
    5017 
     5158    swap(player[0],           player[ctx_index]);
     5159    swap(pos[0],              pos[ctx_index]);
     5160    swap(player[0]->pipState, player[ctx_index]->pipState);
     5161    playerActive = (ctx_index == playerActive) ?
     5162        0 : ((ctx_index == 0) ? ctx_index : playerActive);
     5163   
    50185164    if (useNullVideo)
    50195165        mctx->SetNullVideo(true);
    50205166
    5021     mctx->SetPIPState((ispbp) ? kPBPRight: kPIPonTV);
    5022 
    5023     RestartPIPWindows(mctx, pos, mctx_mute);
     5167    RestartAllNVPs(mctx, pos, mctx_mute);
    50245168}
    50255169
    50265170void TV::DoPlay(PlayerContext *ctx)
     
    53795523            ctx->playingInfo->ApplyTranscoderProfileChange(profile);
    53805524            QString jobHost = "";
    53815525
    5382             if (gContext->GetNumSetting("JobsRunOnRecordHost", 0))
     5526            if (db_run_jobs_on_remote)
    53835527                jobHost = ctx->playingInfo->hostname;
    53845528
    53855529            OSD *osd = GetOSDLock(ctx);
     
    55905734
    55915735    if (testrec && testrec->IsValidRecorder())
    55925736    {
    5593         // pause the decoder first, so we're not reading to close to the end.
     5737        PlayerContext *mctx = GetPlayer(ctx, 0);
     5738        if (mctx != ctx)
     5739            PIPRemovePlayer(mctx, ctx);
    55945740
     5741        // pause the decoder first, so we're not reading too close to the end.
    55955742        ctx->buffer->IgnoreLiveEOF(true);
    55965743        ctx->buffer->StopReads();
    55975744        ctx->nvp->PauseDecoder();
     
    56015748        ctx->buffer->WaitForPause();
    56025749        ctx->nvp->StopPlaying();
    56035750        ctx->recorder->StopLiveTV();
    5604         {
    5605             ctx->SetNVP(NULL);
    5606             PlayerContext *mctx = GetPlayer(ctx, 0);
    5607             if (ctx != mctx)
    5608                 SetPIPPlayer(mctx, ctx);
    5609         }
     5751        ctx->SetNVP(NULL);
    56105752
    56115753        // now restart stuff
    56125754        ctx->lastSignalUIInfo.clear();
     
    56505792                ok = true;
    56515793                ctx->StartOSD(this);
    56525794                ctx->PushPreviousChannel();
    5653                 if (ctx && (player.size() > 1))
    5654                 {
    5655                     if (0 == playerActive)
    5656                     {
    5657                         PlayerContext *ctx1 = GetPlayer(ctx, 1);
    5658                         SetPIPPlayer(mctx, ctx1);
    5659                     }
    5660                     else
    5661                     {
    5662                         PlayerContext *mctx = GetPlayer(ctx, 0);
    5663                         SetPIPPlayer(mctx, ctx);
    5664                     }
    5665                 }
     5795                for (uint i = 1; i < player.size(); i++)
     5796                    PIPAddPlayer(mctx, GetPlayer(ctx, i));
    56665797
    56675798                SetSpeedChangeTimer(25, __LINE__);
    56685799            }
     
    69497080    margin = margin * 5;
    69507081    QDomElement xmldata;
    69517082    XMLParse *theme = new XMLParse();
    6952     if (!allowembed ||
    6953         !theme->LoadTheme(xmldata, str) ||
    6954         !gContext->GetNumSetting("ContinueEmbeddedTVPlay", 0) ||
    6955         ctx->nvp->IsNearEnd(margin) ||
    6956         ctx->paused)
     7083    if (!allowembed || !theme->LoadTheme(xmldata, str) ||
     7084        !db_continue_embedded || ctx->nvp->IsNearEnd(margin) || ctx->paused)
    69577085    {
    69587086        if (!stayPaused)
    69597087            DoPause(ctx, false);
     
    69687096
    69697097void TV::DoEditSchedule(int editType)
    69707098{
    6971     PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
    6972     mctx->LockPlayingInfo(__FILE__, __LINE__);
    6973     if (!mctx->playingInfo)
     7099    PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
     7100
     7101    actx->LockPlayingInfo(__FILE__, __LINE__);
     7102    if (!actx->playingInfo)
    69747103    {
    69757104        VERBOSE(VB_IMPORTANT,
    6976                 LOC_ERR + "doEditSchedule(): no active mctx->playingInfo.");
    6977         mctx->UnlockPlayingInfo(__FILE__, __LINE__);
    6978         ReturnPlayerLock(mctx);
     7105                LOC_ERR + "doEditSchedule(): no active ctx playingInfo.");
     7106        actx->UnlockPlayingInfo(__FILE__, __LINE__);
     7107        ReturnPlayerLock(actx);
    69797108        return;
    69807109    }
    69817110
    69827111    // Collect channel info
    6983     uint    chanid  = mctx->playingInfo->chanid.toUInt();
    6984     QString channum = mctx->playingInfo->chanstr;
    6985     mctx->UnlockPlayingInfo(__FILE__, __LINE__);
     7112    uint    chanid  = actx->playingInfo->chanid.toUInt();
     7113    QString channum = actx->playingInfo->chanstr; channum.detach();
     7114    actx->UnlockPlayingInfo(__FILE__, __LINE__);
    69867115
    6987     ClearOSD(mctx);
     7116    ClearOSD(actx);
    69887117
    69897118    // Resize window to the MythTV GUI size
    6990 
     7119    PlayerContext *mctx = GetPlayer(actx, 0);
    69917120    if (mctx->nvp && mctx->nvp->getVideoOutput())
    69927121        mctx->nvp->getVideoOutput()->ResizeForGui();
    69937122    MythMainWindow *mwnd = gContext->GetMainWindow();
    6994     bool using_gui_size_for_tv = gContext->GetNumSetting("GuiSizeForTV", 0);
    6995     if (!using_gui_size_for_tv)
     7123
     7124    if (!db_use_gui_size_for_tv)
    69967125    {
    69977126        mwnd->setGeometry(saved_gui_bounds.left(), saved_gui_bounds.top(),
    69987127                          saved_gui_bounds.width(), saved_gui_bounds.height());
     
    70187147                player = this;
    70197148            if (StateIsLiveTV(GetState(mctx)))
    70207149            {
    7021                 ReturnPlayerLock(mctx);
     7150                ReturnPlayerLock(actx);
    70227151                changeChannel = GuideGrid::Run(chanid, channum, false,
    70237152                                               player, allowsecondary);
    7024                 mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
     7153                actx = GetPlayerReadLock(0, __FILE__, __LINE__);
    70257154            }
    70267155            else
    70277156            {
    7028                 ReturnPlayerLock(mctx);
     7157                ReturnPlayerLock(actx);
    70297158                GuideGrid::Run(chanid, channum, false, player);
    7030                 mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
     7159                actx = GetPlayerReadLock(0, __FILE__, __LINE__);
    70317160            }
    70327161            break;
    70337162        }
    70347163        case kScheduleProgramFinder:
    70357164            if (!mctx->paused)
    70367165                DoPause(mctx, false);
     7166            ReturnPlayerLock(actx);
    70377167            RunProgramFind(false, false);
     7168            actx = GetPlayerReadLock(0, __FILE__, __LINE__);
    70387169            break;
    70397170        case kScheduledRecording:
    70407171        {
    70417172            if (mctx->paused)
    70427173                DoPause(mctx, false);
     7174
    70437175            mctx->LockPlayingInfo(__FILE__, __LINE__);
     7176            const ProgramInfo pginfo(*mctx->playingInfo);
     7177            mctx->UnlockPlayingInfo(__FILE__, __LINE__);
     7178            ReturnPlayerLock(actx);
     7179
    70447180            ScheduledRecording *record = new ScheduledRecording();
    7045             record->loadByProgram(mctx->playingInfo);
     7181            record->loadByProgram(&pginfo);
    70467182            record->exec();
    70477183            record->deleteLater();
    7048             mctx->UnlockPlayingInfo(__FILE__, __LINE__);
     7184
     7185            actx = GetPlayerReadLock(0, __FILE__, __LINE__);
    70497186            break;
    70507187        }
    70517188        case kViewSchedule:
    70527189        {
    70537190            showvideo = VideoThemeCheck(mctx, "conflict-video", stayPaused);
     7191            ReturnPlayerLock(actx);
    70547192            RunViewScheduledPtr((void *)this, showvideo);
     7193            actx = GetPlayerReadLock(0, __FILE__, __LINE__);
    70557194            break;
    70567195        }
    70577196        case kPlaybackBox:
    70587197        {
    70597198            showvideo = VideoThemeCheck(mctx, "playback-video", stayPaused);
     7199            ReturnPlayerLock(actx);
    70607200            nextProgram = RunPlaybackBoxPtr((void *)this, showvideo);
     7201            actx = GetPlayerReadLock(0, __FILE__, __LINE__);
    70617202        }
    70627203    }
    70637204
     
    70727213        embedCheckTimerId = 0;
    70737214    }
    70747215
    7075     if (StateIsPlaying(GetState(mctx)) &&
     7216    VERBOSE(VB_PLAYBACK, LOC
     7217            <<"state: "<<StateToString(GetState(mctx))
     7218            <<"!stayPaused: "<<stayPaused<<" mctx->paused: "<<mctx->paused);
     7219    if ((StateIsPlaying(GetState(mctx)) || StateIsLiveTV(GetState(mctx))) &&
    70767220        !stayPaused && mctx->paused)
    70777221    {
     7222        VERBOSE(VB_PLAYBACK, LOC + "Unpausing video");
     7223        // this should unpause video..
    70787224        DoPause(mctx, true);
    70797225    }
    7080     ReturnPlayerLock(mctx);
     7226    ReturnPlayerLock(actx);
    70817227
    70827228    mctx = GetPlayerWriteLock(0, __FILE__, __LINE__);
    70837229    if (nextProgram)
    70847230    {
    7085         if (nextProgPIPState == kPIPonTV)
     7231        if (jumpToProgramPIPState == kPIPonTV)
    70867232            CreatePIP(mctx, nextProgram);
    7087         else if (nextProgPIPState == kPBPLeft)
     7233        else if (jumpToProgramPIPState == kPBPLeft)
    70887234            CreatePBP(mctx, nextProgram);
    70897235        else
    70907236        {
    7091             setLastProgram(nextProgram);
     7237            SetLastProgram(nextProgram);
    70927238            jumpToProgram = true;
    70937239            SetExitPlayer(true, true);
    70947240        }
    70957241        mctx = GetPlayer(mctx, 0); // CreatePBP/PIP mess with ctx's
    70967242
    7097         nextProgPIPState = kPIPOff;
     7243        jumpToProgramPIPState = kPIPOff;
    70987244        delete nextProgram;
    70997245        nextProgram = NULL;
    71007246    }
     
    71027248
    71037249    mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
    71047250    // Resize the window back to the MythTV Player size
    7105     if (!using_gui_size_for_tv)
     7251    if (!db_use_gui_size_for_tv)
    71067252    {
    71077253        mwnd->setGeometry(player_bounds.left(), player_bounds.top(),
    71087254                          player_bounds.width(), player_bounds.height());
     
    71247270
    71257271void TV::EditSchedule(const PlayerContext *ctx, int editType)
    71267272{
    7127     if (ctx && (player.size() > 1))
    7128         return;
    7129 
    71307273    // post the request to the main UI thread
    71317274    // it will be caught in eventFilter and processed as CustomEvent
    71327275    // this will create the program guide window (widget)
     
    74867629                {
    74877630                    ctx->nvp->SetWatchingRecording(false);
    74887631                    ctx->nvp->SetLength(filelen);
    7489                     ctx->ChangeState(kState_WatchingPreRecorded);
     7632                    ctx->ChangeState(kState_WatchingPreRecorded, __LINE__);
    74907633                    ScheduleStateChange(ctx);
    74917634                }
    74927635            }
     
    76367779
    76377780    if (message.left(12) == "EXIT_TO_MENU")
    76387781    {
    7639         int exitprompt = gContext->GetNumSetting("PlaybackExitPrompt");
    7640 
    76417782        PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
    76427783        for (uint i = 0; mctx && (i < player.size()); i++)
    76437784        {
    76447785            PlayerContext *ctx = GetPlayer(mctx, i);
    76457786            if (!ctx->nvp)
    76467787                continue;
    7647             if (exitprompt == 1 || exitprompt == 2)
     7788            if (db_playback_exit_prompt == 1 || db_playback_exit_prompt == 2)
    76487789                ctx->nvp->SetBookmark();
    7649             if (ctx->nvp && gContext->GetNumSetting("AutomaticSetWatched", 0))
     7790            if (ctx->nvp && db_auto_set_watched)
    76507791                ctx->nvp->SetWatched();
    76517792        }
    76527793
     
    77257866        }
    77267867    }
    77277868
    7728     int player_cnt = 0;
     7869    if (message.left(9) == "START_EPG")
    77297870    {
    7730         PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
    7731         player_cnt = player.size();
    7732         ReturnPlayerLock(mctx);
    7733     }
    7734 
    7735     if (message.left(9) == "START_EPG" && (1 == player_cnt))
    7736     {
    77377871        int editType = tokens[1].toInt();
    77387872        DoEditSchedule(editType);
    77397873    }
     
    78367970        browsechannum   = ctx->playingInfo->chanstr;
    78377971        browsechanid    = ctx->playingInfo->chanid;
    78387972        browsestarttime = ctx->playingInfo->startts.toString();
     7973        ctx->UnlockPlayingInfo(__FILE__, __LINE__);
    78397974
    78407975        BrowseDispInfo(ctx, BROWSE_SAME);
    78417976
     
    78447979            KillTimer(browseTimerId);
    78457980        browseTimerId = StartTimer(kBrowseTimeout, __LINE__);
    78467981    }
    7847     ctx->UnlockPlayingInfo(__FILE__, __LINE__);
     7982    else
     7983    {
     7984        ctx->UnlockPlayingInfo(__FILE__, __LINE__);
     7985    }
    78487986}
    78497987
    78507988/** \fn TV::BrowseEnd(PlayerContext*, bool)
     
    79788116    QString cmdmsg("");
    79798117    if (ctx->playingInfo->GetAutoExpireFromRecorded() == kLiveTVAutoExpire)
    79808118    {
    7981         int autoexpiredef = gContext->GetNumSetting("AutoExpireDefault", 0);
    7982         ctx->playingInfo->SetAutoExpire(autoexpiredef);
     8119        ctx->playingInfo->SetAutoExpire(db_autoexpire_default);
    79838120        ctx->playingInfo->ApplyRecordRecGroupChange("Default");
    79848121        cmdmsg = tr("Record");
    79858122        ctx->SetPseudoLiveTV(ctx->playingInfo, kPseudoRecording);
     
    81478284    SetUpdateOSDPosition(false);
    81488285}
    81498286
    8150 void TV::SetActive(PlayerContext *ctx, int index)
     8287#include <cassert>
     8288void TV::SetActive(PlayerContext *lctx, int index, bool osd_msg)
    81518289{
    8152     VERBOSE(VB_PLAYBACK, "TV::SetActive -- begin");
     8290    assert(lctx);
     8291    if (!lctx)
     8292        return;
    81538293
     8294    QString loc = LOC + QString("SetActive(%1,%2) was %3")
     8295        .arg(index).arg((osd_msg) ? "with OSD" : "w/o OSD").arg(playerActive);
     8296
     8297    VERBOSE(VB_PLAYBACK, loc + " -- begin");
     8298
    81548299    if (playerActive == index)
    81558300    {
    8156         VERBOSE(VB_PLAYBACK, "TV::SetActive -- index == active context");
     8301        VERBOSE(VB_PLAYBACK, loc + " -- end (index == playerActive)");
    81578302        return;
    81588303    }
    81598304
    8160     VERBOSE(VB_PLAYBACK, "TV::SetActive -- lock");
     8305    index = (index < 0) ? (playerActive+1) % player.size() : index;
     8306    index = (index >= (int)player.size()) ? 0 : index;
    81618307
    8162     PlayerContext *actx = NULL;
     8308    playerActive = index;
    81638309
    8164     if (playerActive > -1 && !ctx)
     8310    for (int i = 0; i < (int)player.size(); i++)
    81658311    {
    8166         actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
    8167         ReturnPlayerLock(actx);
     8312        PlayerContext *ctx = GetPlayer(lctx, i);
     8313        ctx->LockDeleteNVP(__FILE__, __LINE__);
     8314        if (ctx->nvp)
     8315            ctx->nvp->SetPIPActive(i == playerActive);
     8316        ctx->UnlockDeleteNVP(__FILE__, __LINE__);
    81688317    }
    81698318
    8170     VERBOSE(VB_PLAYBACK,
    8171             QString("TV::SetActive changing playerActive "
    8172                     "from %1 to %2")
    8173                     .arg(playerActive).arg(index));
    8174 
    8175     playerActive = index;
    8176 
    8177     if (!ctx)
     8319#if 0
     8320    // seems reduntant, except maybe for main player? -dtk
     8321    if (osd_msg)
    81788322    {
    8179         actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
    8180         ReturnPlayerLock(actx);
     8323        PlayerContext *actx = GetPlayer(lctx, -1);
     8324        OSD *osd = GetOSDLock(actx);
     8325        if (osd)
     8326            osd->SetSettingsText(tr("Active").arg(playerActive), 3);
     8327        ReturnOSDLock(actx, osd);
    81818328    }
     8329#endif
     8330
     8331    VERBOSE(VB_PLAYBACK, loc + " -- end");
    81828332}
    81838333
    81848334void TV::TreeMenuEntered(OSDListTreeType *tree, OSDGenericTree *item)
     
    87258875        EditSchedule(actx, kScheduleProgramFinder);
    87268876    else if (action == "SCHEDULE")
    87278877        EditSchedule(actx, kScheduledRecording);
    8728     else if ((action == "JUMPPREV") ||
    8729              ((action == "PREVCHAN") && (!StateIsLiveTV(GetState(actx)))))
    8730     {
    8731         if (PromptRecGroupPassword(actx))
    8732         {
    8733             actx->nvp->SetBookmark();
    8734             jumpToProgram = true;
    8735             SetExitPlayer(true, true);
    8736         }
    8737     }
    87388878    else if (action == "VIEWSCHEDULED")
    87398879        EditSchedule(actx, kViewSchedule);
    8740     else if (action == "JUMPREC")
     8880    else if (HandleJumpToProgramAction(actx, QStringList(action)))
    87418881    {
    8742         if (gContext->GetNumSetting("JumpToProgramOSD", 1) &&
    8743             StateIsPlaying(actx->GetState()))
    8744         {
    8745             if (jumpMenuTimerId)
    8746                 KillTimer(jumpMenuTimerId);
    8747             jumpMenuTimerId = StartTimer(1, __LINE__);
    8748         }
    8749         else if (RunPlaybackBoxPtr)
    8750             EditSchedule(actx, kPlaybackBox);
    87518882    }
    8752     else if (action == "TOGGLEPIPMODE")
     8883    else if (PxPHandleAction(actx, QStringList(action)))
    87538884    {
    8754         ReturnPlayerLock(actx);
     8885        // Hide the tree on this old active context..
     8886        tree->SetVisible(false);
     8887        tree->disconnect();
     8888        OSD *osd = GetOSDLock(actx);
     8889        if (osd)
     8890            osd->HideTreeMenu();
     8891        ReturnOSDLock(actx, osd);
     8892        ClearOSD(actx);
    87558893
    8756         actx = GetPlayerWriteLock(-1, __FILE__, __LINE__);
    8757         TogglePIPView(actx);
    8758         ReturnPlayerLock(actx);
     8894        hidetree = true;
    87598895
    8760         actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
     8896        actx = GetPlayer(actx,-1); // "NEXTPIPWINDOW" changes active context..
    87618897    }
    8762     else if (action == "TOGGLEPBPMODE")
    8763     {
    8764         needToMakePIPChange = kPBPToggle;
    8765         if (pipChangeTimerId)
    8766             KillTimer(pipChangeTimerId);
    8767         pipChangeTimerId = StartTimer(1, __LINE__);
    8768     }
    8769     else if (action == "TOGGLEPIPWINDOW")
    8770         ToggleActiveWindow(actx, 1);
    8771     else if (action == "TOGGLEPIPSTATE")
    8772     {
    8773         needToMakePIPChange = (actx->isPBP()) ? kPBPToPIP : kPIPToPBP;
    8774         if (pipChangeTimerId)
    8775             KillTimer(pipChangeTimerId);
    8776         pipChangeTimerId = StartTimer(1, __LINE__);
    8777     }
    8778     else if (action == "SWAPPIP")
    8779     {
    8780         needToMakePIPChange = (actx->isPBP()) ? kPBPSwap : kPIPSwap;
    8781         if (pipChangeTimerId)
    8782             KillTimer(pipChangeTimerId);
    8783         pipChangeTimerId = StartTimer(1, __LINE__);
    8784     }
    87858898    else if (StateIsLiveTV(GetState(actx)))
    87868899    {
    87878900        if (action == "TOGGLEBROWSE")
     
    88258938            DoQueueTranscode(actx, "Medium Quality");
    88268939        else if (action == "QUEUETRANSCODE_LOW")
    88278940            DoQueueTranscode(actx, "Low Quality");
    8828         else if (action.left(8) == "JUMPPROG")
    8829         {
    8830             SetJumpToProgram(action.section(" ",1,-2),
    8831                              action.section(" ",-1,-1).toInt());
    8832             actx->nvp->SetBookmark();
    8833             jumpToProgram = true;
    8834             SetExitPlayer(true, true);
    8835         }
    88368941        else
    88378942        {
    88388943            VERBOSE(VB_IMPORTANT, LOC_ERR +
     
    88938998        delete treeMenu;
    88948999
    88959000    treeMenu = new OSDGenericTree(NULL, "treeMenu");
    8896     OSDGenericTree *item, *subitem;
    88979001
    88989002    bool freeRecorders = RemoteGetFreeRecorderCount();
    88999003
    8900     if (IsPIPSupported(ctx) && ((ctx && (player.size() > 1)) || freeRecorders))
    8901     {
    8902         // Picture-in-Picture
    8903         item = new OSDGenericTree(treeMenu, tr("Picture-in-Picture"));
    8904         if (ctx && (player.size() == 1))
    8905         {
    8906             subitem = new OSDGenericTree(item, tr("Enable PIP"),
    8907                                          "TOGGLEPIPMODE");
    8908             subitem = new OSDGenericTree(item, tr("Enable PBP"),
    8909                                          "TOGGLEPBPMODE");
    8910         }
    8911         else
    8912         {
    8913             QString option = "Disable ";
    8914             QString switchTo = "Switch to ";
    8915             QString pipType = (ctx->isPBP()) ? "PBP" : "PIP";
    8916             QString changeTo = (ctx->isPBP()) ? "PIP" : "PBP";
    8917             option += pipType;
    8918             switchTo += changeTo;
    8919             QString toggleMode = QString("TOGGLE%1MODE").arg(pipType);
    8920             subitem = new OSDGenericTree(item, option, toggleMode);
    8921             subitem = new OSDGenericTree(item, tr("Swap Windows"),
    8922                                          "SWAPPIP");
    8923             subitem = new OSDGenericTree(item, tr("Change Active Window"),
    8924                                          "TOGGLEPIPWINDOW");
    8925             subitem = new OSDGenericTree(item, switchTo, "TOGGLEPIPSTATE");
    8926         }
    8927     }
    8928 
    89299004    if (freeRecorders && ctx->recorder)
    89309005    {
    89319006        // Input switching
     
    90039078        }
    90049079    }
    90059080
     9081    FillMenuPxP(ctx, treeMenu, freeRecorders);
     9082
    90069083    if ((ctx != GetPlayer(ctx, 0)))
    90079084        return;
    90089085
     
    91539230    new OSDGenericTree(s_item, "120 " + tr("minutes"), "TOGGLESLEEP120");
    91549231}
    91559232
    9156 void TV::FillMenuLiveTV(OSDGenericTree *treeMenu)
     9233/// \brief Constructs Picture-X-Picture portion of menu
     9234void TV::FillMenuPxP(
     9235    const PlayerContext *ctx, OSDGenericTree *treeMenu, bool freeRecorders)
    91579236{
    9158     new OSDGenericTree(treeMenu, tr("Program Guide"), "GUIDE");
     9237    if (!ctx || !IsPIPSupported(ctx))
     9238        return;
    91599239
    9160     if (!gContext->GetNumSetting("JumpToProgramOSD", 1))
     9240    if ((player.size() <= 1) && !freeRecorders)
     9241        return;
     9242
     9243    OSDGenericTree *item =
     9244        new OSDGenericTree(treeMenu, tr("Picture-in-Picture"));
     9245
     9246    if (freeRecorders)
    91619247    {
    9162         OSDGenericTree *jtpo_item =
    9163             new OSDGenericTree(treeMenu, tr("Jump to Program"));
    9164         new OSDGenericTree(jtpo_item, tr("Recorded Program"), "JUMPREC");
    9165         if (lastProgram != NULL)
    9166             new OSDGenericTree(jtpo_item, lastProgram->title, "JUMPPREV");
     9248        if (player.size() <= kMaxPIPCount)
     9249            new OSDGenericTree(item, tr("Open Live TV PIP"), "CREATEPIPVIEW");
     9250        if (player.size() <= kMaxPBPCount)
     9251            new OSDGenericTree(item, tr("Open Live TV PBP"), "CREATEPBPVIEW");
    91679252    }
    91689253
     9254    if (player.size() <= kMaxPIPCount)
     9255        new OSDGenericTree(item, tr("Open Recording PIP"), "JUMPRECPIP");
     9256    if (player.size() <= kMaxPBPCount)
     9257        new OSDGenericTree(item, tr("Open Recording PBP"), "JUMPRECPBP");
     9258
     9259    if (player.size() <= 1)
     9260        return;
     9261
     9262    new OSDGenericTree(item, tr("Change Active Window"), "NEXTPIPWINDOW");
     9263
     9264    QString pipType  = (ctx->isPBP()) ? "PBP" : "PIP";
     9265    QString toggleMode = QString("TOGGLE%1MODE").arg(pipType);
     9266
     9267    bool isPBP = ctx->isPBP();
     9268    const PlayerContext *mctx = GetPlayer(ctx, 0);
     9269    QString pipClose = (isPBP) ? tr("Close PBP") : tr("Close PIP");
     9270    if (mctx == ctx)
     9271    {
     9272        if (player.size() > 2)
     9273            pipClose = (isPBP) ? tr("Close PBPs") : tr("Close PIPs");
     9274
     9275        new OSDGenericTree(item, pipClose, toggleMode);
     9276
     9277        if (player.size() == 2)
     9278            new OSDGenericTree(item, tr("Swap Windows"), "SWAPPIP");
     9279    }
     9280    else
     9281    {
     9282        new OSDGenericTree(item, pipClose, toggleMode);
     9283        new OSDGenericTree(item, tr("Swap Windows"), "SWAPPIP");
     9284    }
     9285
     9286    uint max_cnt = min(kMaxPBPCount, kMaxPIPCount);
     9287    if (player.size() <= max_cnt)
     9288    {
     9289        QString switchTo = (isPBP) ? tr("Switch to PIP") : tr("Switch to PBP");
     9290        new OSDGenericTree(item, switchTo, "TOGGLEPIPSTATE");
     9291    }
     9292}
     9293
     9294
     9295void TV::FillMenuLiveTV(OSDGenericTree *treeMenu)
     9296{
     9297    new OSDGenericTree(treeMenu, tr("Program Guide"), "GUIDE");
     9298
     9299    OSDGenericTree *jtpo_item =
     9300        new OSDGenericTree(treeMenu, tr("Jump to Program"));
     9301    new OSDGenericTree(jtpo_item, tr("Recorded Program"), "JUMPREC");
     9302    if (lastProgram != NULL)
     9303        new OSDGenericTree(jtpo_item, lastProgram->title, "JUMPPREV");
     9304
    91699305    if (!persistentbrowsemode)
    91709306        new OSDGenericTree(treeMenu, tr("Enable Browse Mode"), "TOGGLEBROWSE");
    91719307
     
    93959531    ReturnOSDLock(ctx, osd);
    93969532}
    93979533
    9398 void TV::SetJumpToProgram(QString progKey, int progIndex)
     9534bool TV::HandleJumpToProgramAction(
     9535    PlayerContext *ctx, const QStringList &actions)
    93999536{
    9400     QMap<QString,ProgramList>::Iterator Iprog;
    9401     Iprog = progLists.find(progKey);
    9402     ProgramList plist = *Iprog;
    9403     ProgramInfo *p = plist.at(progIndex);
    9404     VERBOSE(VB_IMPORTANT, QString("Switching to program: %1: %2")
    9405             .arg(p->title).arg(p->subtitle));
    9406     setLastProgram(p);
     9537    const PlayerContext *mctx = GetPlayer(ctx, 0);
     9538    TVState s = ctx->GetState();
     9539    if (has_action("JUMPPREV", actions) ||
     9540        (has_action("PREVCHAN", actions) && !StateIsLiveTV(s)))
     9541    {
     9542        if (PromptRecGroupPassword(ctx))
     9543        {
     9544            if (mctx == ctx)
     9545            {
     9546                ctx->LockDeleteNVP(__FILE__, __LINE__);
     9547                ctx->nvp->SetBookmark();
     9548                ctx->UnlockDeleteNVP(__FILE__, __LINE__);
     9549                jumpToProgram = true;
     9550                SetExitPlayer(true, true);
     9551            }
     9552            else
     9553            {
     9554                // TODO
     9555            }
     9556        }
     9557        return true;
     9558    }
     9559
     9560    QStringList::const_iterator it = actions.begin();
     9561    for (; it != actions.end(); ++it)
     9562    {
     9563        if ((*it).left(8) != "JUMPPROG")
     9564            continue;
     9565
     9566        const QString &action = *it;
     9567
     9568        bool ok;
     9569        QString progKey   = action.section(" ",1,-2);
     9570        uint    progIndex = action.section(" ",-1,-1).toUInt(&ok);
     9571        ProgramInfo *p = NULL;
     9572
     9573        if (!ok)
     9574        {
     9575            QMutexLocker locker(&progListsLock);
     9576            QMap<QString,ProgramList>::const_iterator it =
     9577                progLists.find(progKey);
     9578            if (it != progLists.end())
     9579            {
     9580                const ProgramInfo *tmp = (*it)[progIndex];
     9581                if (tmp)
     9582                    p = new ProgramInfo(*tmp);
     9583            }
     9584        }
     9585
     9586        if (!p)
     9587        {
     9588            VERBOSE(VB_IMPORTANT, LOC_ERR +
     9589                    QString("Failed to locate jump to program '%1' @ %2")
     9590                    .arg(progKey).arg(action.section(" ",-1,-1).toUInt(&ok)));
     9591            return true;
     9592        }
     9593
     9594        PIPState state = kPIPOff;
     9595        {
     9596            QMutexLocker locker(&timerIdLock);
     9597            state = jumpToProgramPIPState;
     9598        }
     9599
     9600        if (kPIPOff == state)
     9601        {
     9602            if (mctx == ctx)
     9603            {
     9604                VERBOSE(VB_GENERAL, LOC +
     9605                        QString("Switching to program: %1: %2")
     9606                        .arg(p->title).arg(p->subtitle));
     9607           
     9608                SetLastProgram(p);
     9609                ctx->LockDeleteNVP(__FILE__, __LINE__);
     9610                ctx->nvp->SetBookmark();
     9611                ctx->UnlockDeleteNVP(__FILE__, __LINE__);
     9612                jumpToProgram = true;
     9613                SetExitPlayer(true, true);
     9614            }
     9615            else
     9616            {
     9617                // TODO
     9618            }
     9619        }
     9620        else
     9621        {
     9622            QString type = (kPIPonTV == jumpToProgramPIPState) ? "PIP" : "PBP";
     9623            VERBOSE(VB_GENERAL, LOC + QString("Creating %1 with program: %2: %3")
     9624                    .arg(type).arg(p->title).arg(p->subtitle));
     9625
     9626            if (jumpToProgramPIPState == kPIPonTV)
     9627                CreatePIP(ctx, p);
     9628            else if (jumpToProgramPIPState == kPBPLeft)
     9629                CreatePBP(ctx, p);
     9630        }
     9631
     9632        delete p;
     9633
     9634        return true;
     9635    }
     9636
     9637    bool wants_jump = has_action("JUMPREC", actions);
     9638    bool wants_pip = !wants_jump && has_action("JUMPRECPIP", actions);
     9639    bool wants_pbp = !wants_jump && !wants_pip &&
     9640        has_action("JUMPRECPBP", actions);
     9641
     9642    if (!wants_jump && !wants_pip && !wants_pbp)
     9643        return false;
     9644
     9645    {
     9646        QMutexLocker locker(&timerIdLock);
     9647        jumpToProgramPIPState = wants_pip ? kPIPonTV :
     9648            (wants_pbp ? kPBPLeft : kPIPOff);
     9649    }
     9650
     9651    if (db_jump_prefer_osd && (StateIsPlaying(s) || StateIsLiveTV(s)))
     9652    {
     9653        QMutexLocker locker(&timerIdLock);
     9654        if (jumpMenuTimerId)
     9655            KillTimer(jumpMenuTimerId);
     9656        jumpMenuTimerId = StartTimer(1, __LINE__);
     9657    }
     9658    else if (RunPlaybackBoxPtr)
     9659        EditSchedule(ctx, kPlaybackBox);
     9660    else
     9661        VERBOSE(VB_IMPORTANT, "Failed to open jump to program GUI");
     9662
     9663    return true;
    94079664}
    94089665
    94099666void TV::ToggleSleepTimer(const PlayerContext *ctx, const QString &time)
     
    976110018
    976210019    ctx->UnlockPlayingInfo(__FILE__, __LINE__);
    976310020
    9764     if (isDVD && (!gContext->GetNumSetting("EnableDVDBookmark", 0) ||
    9765                   ctx->buffer->DVD()->GetTotalTimeOfTitle() < 120))
    9766     {
    9767         return false;
    9768     }
    9769 
    9770     return true;
     10021    return (!isDVD || (db_use_dvd_bookmark &&
     10022                       ctx->buffer->DVD()->GetTotalTimeOfTitle() >= 120));
    977110023}
    977210024
    977310025/* \fn TV::IsDeleteAllowed(const PlayerContext*) const
     
    990910161            dialogname == "exitplayoptions");
    991010162}
    991110163
    9912 void TV::setLastProgram(ProgramInfo *rcinfo)
     10164void TV::SetLastProgram(ProgramInfo *rcinfo)
    991310165{
     10166    QMutexLocker locker(&lastProgramLock);
     10167
    991410168    if (lastProgram)
    991510169        delete lastProgram;
    991610170
     
    992010174        lastProgram = NULL;
    992110175}
    992210176
     10177ProgramInfo *TV::GetLastProgram(void) const
     10178{
     10179    QMutexLocker locker(&lastProgramLock);
     10180    if (lastProgram)
     10181        return new ProgramInfo(*lastProgram);
     10182    return NULL;
     10183}
     10184
    992310185QString TV::GetRecordingGroup(int player_idx) const
    992410186{
    992510187    QString ret = QString::null;
     
    1001410276    treeMenu = new OSDGenericTree(NULL, "treeMenu");
    1001510277
    1001610278    // Build jumpMenu of recorded program titles
    10017     ProgramInfo *p;
     10279    QMutexLocker locker(&progListsLock);
    1001810280    progLists.clear();
    10019     vector<ProgramInfo *> *infoList;
    10020     infoList = RemoteGetRecordedList(false);
     10281    vector<ProgramInfo *> *infoList = RemoteGetRecordedList(false);
    1002110282
    1002210283    //bool LiveTVInAllPrograms = gContext->GetNumSetting("LiveTVInAllPrograms",0);
    1002310284    if (infoList)
    1002410285    {
    1002510286        PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
    1002610287        actx->LockPlayingInfo(__FILE__, __LINE__);
    10027         vector<ProgramInfo *>::iterator i = infoList->begin();
    10028         for ( ; i != infoList->end(); i++)
     10288        vector<ProgramInfo *>::const_iterator it = infoList->begin();
     10289        for ( ; it != infoList->end(); it++)
    1002910290        {
    10030             p = *i;
    1003110291            //if (p->recgroup != "LiveTV" || LiveTVInAllPrograms)
    10032             if (p->recgroup == actx->playingInfo->recgroup)
    10033                 progLists[p->title].prepend(p);
     10292            if ((*it)->recgroup == actx->playingInfo->recgroup)
     10293                progLists[(*it)->title].push_front(new ProgramInfo(*(*it)));
    1003410294        }
    1003510295        actx->UnlockPlayingInfo(__FILE__, __LINE__);
    1003610296        ReturnPlayerLock(actx);
    1003710297
    10038         QMap<QString,ProgramList>::Iterator Iprog;
     10298        QMap<QString,ProgramList>::const_iterator Iprog;
    1003910299        for (Iprog = progLists.begin(); Iprog != progLists.end(); Iprog++)
    1004010300        {
    10041             ProgramList plist = *Iprog;
     10301            const ProgramList &plist = *Iprog;
    1004210302            int progIndex = plist.count();
    1004310303            if (progIndex == 1)
    1004410304            {
     
    1005310313
    1005410314                for (int i = 0; i < progIndex; i++)
    1005510315                {
    10056                     p = plist.at(i);
    10057                     if (p->subtitle.size())
     10316                    const ProgramInfo *p = plist[i];
     10317                    if (!p->subtitle.isEmpty())
    1005810318                        new OSDGenericTree(j_item, p->subtitle,
    1005910319                            QString("JUMPPROG %1 %2").arg(Iprog.key()).arg(i));
    1006010320                    else
     
    1006410324            }
    1006510325        }
    1006610326    }
     10327    while (!infoList->empty())
     10328    {
     10329        delete infoList->back();
     10330        infoList->pop_back();
     10331    }
     10332    delete infoList;
    1006710333
    1006810334    PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
    1006910335    OSD *osd = GetOSDLock(actx);
     
    1008510351        }
    1008610352    }
    1008710353    ReturnOSDLock(actx, osd);
    10088 
     10354    ReturnPlayerLock(actx);
    1008910355}
    1009010356
    1009110357void TV::RestoreScreenSaver(const PlayerContext *ctx)
     
    1009610362
    1009710363void TV::InitUDPNotifyEvent(void)
    1009810364{
    10099     uint udp_port = gContext->GetNumSetting("UDPNotifyPort");
    10100     if (udp_port && !udpnotify)
     10365    if (db_udpnotify_port && !udpnotify)
    1010110366    {
    10102         udpnotify = new UDPNotify(udp_port);
     10367        udpnotify = new UDPNotify(db_udpnotify_port);
    1010310368        connect(udpnotify,
    1010410369                SIGNAL(AddUDPNotifyEvent(
    1010510370                        const QString&,const UDPNotifyOSDSet*)),
     
    1023410499{
    1023510500    playerLock.lockForWrite();
    1023610501
     10502    //VERBOSE(VB_IMPORTANT, LOC_WARN +
     10503    //        QString("GetPlayerWriteLock(%1,%2,%3) size(%4)")
     10504    //        .arg(which).arg(file).arg(location).arg(player.size()));
     10505
    1023710506    if ((which >= (int)player.size()))
    1023810507    {
    1023910508        VERBOSE(VB_IMPORTANT, LOC_WARN +
     
    1024910518{
    1025010519    playerLock.lockForRead();
    1025110520
     10521    //VERBOSE(VB_IMPORTANT, LOC_WARN +
     10522    //        QString("GetPlayerReadLock(%1,%2,%3) size(%4)")
     10523    //        .arg(which).arg(file).arg(location).arg(player.size()));
     10524
    1025210525    if ((which >= (int)player.size()))
    1025310526    {
    1025410527        VERBOSE(VB_IMPORTANT, LOC_WARN +
     
    1026510538{
    1026610539    playerLock.lockForRead();
    1026710540
     10541    //VERBOSE(VB_IMPORTANT, LOC_WARN +
     10542    //        QString("GetPlayerReadLock(%1,%2,%3) const size(%4)")
     10543    //        .arg(which).arg(file).arg(location).arg(player.size()));
     10544
    1026810545    if ((which >= (int)player.size()))
    1026910546    {
    1027010547        VERBOSE(VB_IMPORTANT, LOC_WARN +
     
    1031210589
    1031310590void TV::ReturnPlayerLock(PlayerContext *&ctx)
    1031410591{
     10592    //VERBOSE(VB_IMPORTANT, LOC_WARN +
     10593    //        QString("ReturnPlayerLock() size(%4)")
     10594    //        .arg(player.size()));
     10595
    1031510596    playerLock.unlock();
    1031610597    ctx = NULL;
    1031710598}
    1031810599
    1031910600void TV::ReturnPlayerLock(const PlayerContext *&ctx) const
    1032010601{
     10602    //VERBOSE(VB_IMPORTANT, LOC_WARN +
     10603    //        QString("ReturnPlayerLock() const size(%4)")
     10604    //        .arg(player.size()));
     10605
    1032110606    playerLock.unlock();
    1032210607    ctx = NULL;
    1032310608}
  • libs/libmythtv/videoouttypes.h

     
    1111    kPIPOff = 0,
    1212    kPIPonTV,
    1313    kPIPStandAlone,
    14     kPIPSwap,
    1514    kPBPLeft,
    1615    kPBPRight,
    17     kPBPSwap,
    18     kPIPToPBP,
    19     kPBPToPIP,
    20     kPBPToggle
    2116} PIPState;
    2217
    2318typedef enum PIPLocation
  • libs/libmythtv/videooutbase.cpp

     
    887887
    888888    int pipw, piph;
    889889    VideoFrame *pipimage       = pipplayer->GetCurrentFrame(pipw, piph);
    890     const bool  pipActive      = false;
     890    const bool  pipActive      = pipplayer->IsPIPActive();
    891891    const float pipVideoAspect = pipplayer->GetVideoAspect();
    892892    const QSize pipVideoDim    = pipplayer->GetVideoBufferSize();
    893893    const uint  pipVideoWidth  = pipVideoDim.width();
  • libs/libmythtv/playercontext.h

     
    6262    bool StartPIPPlayer(TV *tv, TVState desiredState);
    6363    void PIPTeardown(void);
    6464    bool isPIP(void) const
    65         { return (pipState > kPIPOff && pipState < kPBPLeft); }
     65        { return (kPIPonTV == pipState) || (kPIPStandAlone == pipState); }
    6666    bool isPBP(void) const
    67         { return (pipState > kPIPStandAlone && pipState < kPBPSwap); }
     67        { return (kPBPLeft == pipState) || (kPBPRight      == pipState); }
    6868    bool isMainVideo(void) const
    69         { return (pipState == kPIPOff || pipState == kPBPLeft); }
     69        { return (kPIPOff  == pipState) || (kPBPLeft       == pipState); }
    7070    void DrawARGBFrame(QPainter *p);
    7171    QRect GetStandAlonePIPRect(void);
    72     QString PIPLocationToString(void);
    73     QString PIPStateToString(void);
    7472    PIPState GetPIPState(void) const { return pipState; }
    7573    void SetNullVideo(bool setting) { useNullVideo = setting; }
    7674    bool UseNullVideo(void) const { return useNullVideo; }
     75    bool IsNVPChangingBuffers(void) const { return nvpUnsafe; }
    7776
    78 
    7977    void    PushPreviousChannel(void);
    8078    QString PopPreviousChannel(void);
    8179    QString GetPreviousChannel(void) const;
     
    9088    void LockState(void) const;
    9189    void UnlockState(void) const;
    9290    bool InStateChange(void) const;
    93     void ChangeState(TVState newState);
    94     void ForceNextStateNone(void);
     91    void ChangeState(TVState newState, int line);
     92    void ForceNextStateNone(int line);
    9593    TVState DequeueNextState(void);
    9694    TVState GetState(void) const;
    9795    /// This is set if the player encountered some irrecoverable error.
     
    113111    void SetPlayGroup(const QString &group);
    114112    void SetPseudoLiveTV(const ProgramInfo *pi, PseudoState new_state);
    115113    void SetPIPState(PIPState change) { pipState = change; }
     114    void SetNVPChangingBuffers(bool val) { nvpUnsafe = val; }
    116115    void ResizePIPWindow(void);
    117116
    118117
    119118  public:
    120119    NuppelVideoPlayer  *nvp;
     120    volatile bool       nvpUnsafe;
    121121    RemoteEncoder      *recorder;
    122122    LiveTVChain        *tvchain;
    123123    RingBuffer         *buffer;
  • libs/libmythtv/videoout_xv.cpp

     
    36063606
    36073607    int pipw, piph;
    36083608    VideoFrame *pipimage       = pipplayer->GetCurrentFrame(pipw, piph);
    3609     const bool  pipActive      = false;
     3609    const bool  pipActive      = pipplayer->IsPIPActive();
    36103610    const float pipVideoAspect = pipplayer->GetVideoAspect();
    36113611    const QSize pipVideoDim    = pipplayer->GetVideoBufferSize();
    36123612    const uint  pipVideoWidth  = pipVideoDim.width();