play media to end when eof detected
From: <mspieth@digivation.com.au>
---
mythtv/libs/libmyth/audiooutput.h | 1
mythtv/libs/libmyth/audiooutputbase.cpp | 29 ++++++++++++
mythtv/libs/libmyth/audiooutputbase.h | 2 +
mythtv/libs/libmythtv/NuppelVideoPlayer.cpp | 63 +++++++++++++++++----------
mythtv/libs/libmythtv/NuppelVideoPlayer.h | 14 +++++-
mythtv/programs/mythtranscode/transcode.cpp | 1
6 files changed, 83 insertions(+), 27 deletions(-)
diff --git a/mythtv/libs/libmyth/audiooutput.h b/mythtv/libs/libmyth/audiooutput.h
index 943bd77..f410032 100644
a
|
b
|
class MPUBLIC AudioOutput : public VolumeBase, public OutputListeners |
33 | 33 | |
34 | 34 | // do AddSamples calls block? |
35 | 35 | virtual void SetBlocking(bool blocking) = 0; |
| 36 | virtual void SetTimedBlocking(bool blocking) = 0; |
36 | 37 | |
37 | 38 | // dsprate is in 100 * samples/second |
38 | 39 | virtual void SetEffDsp(int dsprate) = 0; |
diff --git a/mythtv/libs/libmyth/audiooutputbase.cpp b/mythtv/libs/libmyth/audiooutputbase.cpp
index ef2f3d5..5a1b687 100644
a
|
b
|
AudioOutputBase::AudioOutputBase(const AudioSettings &settings) : |
53 | 53 | surround_mode(FreeSurround::SurroundModePassive), |
54 | 54 | |
55 | 55 | blocking(false), |
| 56 | timed_blocking(false), |
56 | 57 | |
57 | 58 | lastaudiolen(0), samples_buffered(0), |
58 | 59 | |
… |
… |
void AudioOutputBase::SetBlocking(bool blocking) |
511 | 512 | this->blocking = blocking; |
512 | 513 | } |
513 | 514 | |
| 515 | void AudioOutputBase::SetTimedBlocking(bool blocking) |
| 516 | { |
| 517 | VERBOSE(VB_AUDIO, LOC + QString("Timed blocking: %1").arg(blocking)); |
| 518 | this->timed_blocking = blocking; |
| 519 | } |
| 520 | |
514 | 521 | int AudioOutputBase::audiolen(bool use_lock) |
515 | 522 | { |
516 | 523 | /* Thread safe, returns the number of valid bytes in the audio buffer */ |
… |
… |
bool AudioOutputBase::AddSamples(char *buffers[], int samples, |
671 | 678 | len += (pSoundStretch->numUnprocessedSamples() + |
672 | 679 | (int)(pSoundStretch->numSamples()/audio_stretchfactor))*abps; |
673 | 680 | |
| 681 | if ((len > afree) && !blocking && timed_blocking && !pauseaudio) |
| 682 | { |
| 683 | int time_to_sleep = ((len - afree) * 1000000LL) / (abps * audio_samplerate); |
| 684 | VERBOSE(VB_AUDIO, LOC + QString("timed block %1 %2").arg(len - afree).arg(time_to_sleep)); |
| 685 | if (time_to_sleep > 0) |
| 686 | { |
| 687 | usleep(time_to_sleep + 50000); |
| 688 | afree = audiofree(true); |
| 689 | } |
| 690 | } |
| 691 | |
674 | 692 | if ((len > afree) && !blocking) |
675 | 693 | { |
676 | 694 | VERBOSE(VB_AUDIO+VB_TIMESTAMP, LOC + QString( |
… |
… |
bool AudioOutputBase::AddSamples(char *buffer, int samples, long long timecode) |
740 | 758 | (int)(pSoundStretch->numSamples()/audio_stretchfactor))*abps; |
741 | 759 | } |
742 | 760 | |
| 761 | if ((len > afree) && !blocking && timed_blocking && !pauseaudio) |
| 762 | { |
| 763 | int time_to_sleep = ((len - afree) * 1000000LL) / (abps * audio_samplerate); |
| 764 | VERBOSE(VB_AUDIO, LOC + QString("timed block %1 %2").arg(len - afree).arg(time_to_sleep)); |
| 765 | if (time_to_sleep > 0) |
| 766 | { |
| 767 | usleep(time_to_sleep + 50000); |
| 768 | afree = audiofree(true); |
| 769 | } |
| 770 | } |
| 771 | |
743 | 772 | if ((len > afree) && !blocking) |
744 | 773 | { |
745 | 774 | VERBOSE(VB_AUDIO+VB_TIMESTAMP, LOC + QString( |
diff --git a/mythtv/libs/libmyth/audiooutputbase.h b/mythtv/libs/libmyth/audiooutputbase.h
index 1f636e2..4575e91 100644
a
|
b
|
class AudioOutputBase : public AudioOutput, public QThread |
37 | 37 | |
38 | 38 | // do AddSamples calls block? |
39 | 39 | virtual void SetBlocking(bool blocking); |
| 40 | virtual void SetTimedBlocking(bool blocking); |
40 | 41 | |
41 | 42 | // dsprate is in 100 * samples/second |
42 | 43 | virtual void SetEffDsp(int dsprate); |
… |
… |
class AudioOutputBase : public AudioOutput, public QThread |
161 | 162 | int surround_mode; |
162 | 163 | |
163 | 164 | bool blocking; // do AddSamples calls block? |
| 165 | bool timed_blocking; // do AddSamples calls block? |
164 | 166 | |
165 | 167 | int lastaudiolen; |
166 | 168 | long long samples_buffered; |
diff --git a/mythtv/libs/libmythtv/NuppelVideoPlayer.cpp b/mythtv/libs/libmythtv/NuppelVideoPlayer.cpp
index 84e013d..36dc49d 100644
a
|
b
|
NuppelVideoPlayer::NuppelVideoPlayer() |
162 | 162 | parentWidget(NULL), embedid(0), |
163 | 163 | embx(-1), emby(-1), embw(-1), embh(-1), |
164 | 164 | // State |
165 | | eof(false), |
| 165 | eof(kEofStateNone), |
166 | 166 | m_double_framerate(false), m_double_process(false), |
167 | 167 | m_can_double(false), m_deint_possible(true), |
168 | 168 | paused(false), |
… |
… |
void NuppelVideoPlayer::PauseDecoder(void) |
491 | 491 | QMutex mutex; |
492 | 492 | mutex.lock(); |
493 | 493 | |
494 | | while (!eof && !actuallypaused && |
495 | | !decoderThreadPaused.wait(&mutex, 100) && |
496 | | !eof && !actuallypaused) |
| 494 | while ((eof == kEofStateNone) && !actuallypaused && |
| 495 | !decoderThreadPaused.wait(&mutex, 100)) |
497 | 496 | { |
498 | 497 | VERBOSE(VB_IMPORTANT, LOC_WARN + |
499 | 498 | "Waited too long for decoder to pause"); |
… |
… |
void NuppelVideoPlayer::PauseVideo(bool wait) |
590 | 589 | { |
591 | 590 | videoThreadPaused.wait(&pauseUnpauseLock, 250); |
592 | 591 | |
593 | | if (video_actually_paused || eof) |
| 592 | if (video_actually_paused || (eof != kEofStateNone)) |
594 | 593 | break; |
595 | 594 | |
596 | 595 | if ((i % 10) == 9) |
… |
… |
void NuppelVideoPlayer::UnpauseVideo(bool wait) |
607 | 606 | { |
608 | 607 | videoThreadUnpaused.wait(&pauseUnpauseLock, 250); |
609 | 608 | |
610 | | if (!video_actually_paused || eof) |
| 609 | if (!video_actually_paused || (eof != kEofStateNone)) |
611 | 610 | break; |
612 | 611 | |
613 | 612 | if ((i % 10) == 9) |
… |
… |
QString NuppelVideoPlayer::ReinitAudio(void) |
928 | 927 | if (!audioOutput) |
929 | 928 | errMsg = QObject::tr("Unable to create AudioOutput."); |
930 | 929 | else |
| 930 | { |
931 | 931 | errMsg = audioOutput->GetError(); |
| 932 | audioOutput->SetTimedBlocking(true); |
| 933 | } |
932 | 934 | |
933 | 935 | if (!errMsg.isEmpty()) |
934 | 936 | { |
… |
… |
int NuppelVideoPlayer::OpenFile(bool skipDsp, uint retries, |
1296 | 1298 | GetDecoder()->setTranscoding(transcoding); |
1297 | 1299 | CheckExtraAudioDecode(); |
1298 | 1300 | |
1299 | | eof = false; |
| 1301 | eof = kEofStateNone; |
1300 | 1302 | |
1301 | 1303 | // Set 'no_video_decode' to true for audio only decodeing |
1302 | 1304 | bool no_video_decode = false; |
… |
… |
void NuppelVideoPlayer::SwitchToProgram(void) |
3273 | 3275 | OpenDummy(); |
3274 | 3276 | ResetPlaying(); |
3275 | 3277 | DoPause(); |
3276 | | eof = false; |
| 3278 | eof = kEofStateNone; |
3277 | 3279 | delete pginfo; |
3278 | 3280 | return; |
3279 | 3281 | } |
… |
… |
void NuppelVideoPlayer::SwitchToProgram(void) |
3284 | 3286 | if (!player_ctx->buffer->IsOpen()) |
3285 | 3287 | { |
3286 | 3288 | VERBOSE(VB_IMPORTANT, LOC_ERR + "SwitchToProgram's OpenFile failed."); |
3287 | | eof = true; |
| 3289 | eof = kEofStateImmediate; |
3288 | 3290 | SetErrored(QObject::tr("Error opening switch program buffer")); |
3289 | 3291 | delete pginfo; |
3290 | 3292 | return; |
3291 | 3293 | } |
3292 | 3294 | |
3293 | | if (eof) |
| 3295 | if (eof != kEofStateNone) |
3294 | 3296 | { |
3295 | 3297 | discontinuity = true; |
3296 | 3298 | ClearSubtitles(); |
… |
… |
void NuppelVideoPlayer::SwitchToProgram(void) |
3326 | 3328 | if (IsErrored()) |
3327 | 3329 | { |
3328 | 3330 | VERBOSE(VB_IMPORTANT, LOC_ERR + "SwitchToProgram failed."); |
3329 | | eof = true; |
| 3331 | eof = kEofStateDelayed; |
3330 | 3332 | return; |
3331 | 3333 | } |
3332 | 3334 | |
… |
… |
void NuppelVideoPlayer::SwitchToProgram(void) |
3341 | 3343 | GetDecoder()->SyncPositionMap(); |
3342 | 3344 | } |
3343 | 3345 | |
3344 | | eof = false; |
| 3346 | eof = kEofStateNone; |
3345 | 3347 | } |
3346 | 3348 | |
3347 | 3349 | void NuppelVideoPlayer::FileChangedCallback(void) |
… |
… |
void NuppelVideoPlayer::FileChangedCallback(void) |
3358 | 3360 | |
3359 | 3361 | player_ctx->buffer->Unpause(); |
3360 | 3362 | |
3361 | | eof = false; |
| 3363 | eof = kEofStateNone; |
3362 | 3364 | |
3363 | 3365 | player_ctx->SetNVPChangingBuffers(false); |
3364 | 3366 | |
… |
… |
void NuppelVideoPlayer::JumpToProgram(void) |
3400 | 3402 | OpenDummy(); |
3401 | 3403 | ResetPlaying(); |
3402 | 3404 | DoPause(); |
3403 | | eof = false; |
| 3405 | eof = kEofStateNone; |
3404 | 3406 | delete pginfo; |
3405 | 3407 | return; |
3406 | 3408 | } |
… |
… |
void NuppelVideoPlayer::JumpToProgram(void) |
3409 | 3411 | if (!player_ctx->buffer->IsOpen()) |
3410 | 3412 | { |
3411 | 3413 | VERBOSE(VB_IMPORTANT, LOC_ERR + "JumpToProgram's OpenFile failed."); |
3412 | | eof = true; |
| 3414 | eof = kEofStateImmediate; |
3413 | 3415 | SetErrored(QObject::tr("Error opening jump program file buffer")); |
3414 | 3416 | delete pginfo; |
3415 | 3417 | return; |
… |
… |
void NuppelVideoPlayer::JumpToProgram(void) |
3463 | 3465 | GetDecoder()->setExactSeeks(seeks); |
3464 | 3466 | } |
3465 | 3467 | |
3466 | | eof = false; |
| 3468 | eof = kEofStateNone; |
3467 | 3469 | player_ctx->SetNVPChangingBuffers(false); |
3468 | 3470 | } |
3469 | 3471 | |
… |
… |
bool NuppelVideoPlayer::StartPlaying(bool openfile) |
3645 | 3647 | player_ctx->tvchain->JumpToNext(true, 1); |
3646 | 3648 | JumpToProgram(); |
3647 | 3649 | } |
3648 | | else if ((!paused || eof) && player_ctx->tvchain && |
| 3650 | else if ((!paused || (eof != kEofStateNone)) && player_ctx->tvchain && |
3649 | 3651 | !GetDecoder()->GetWaitForChange()) |
3650 | 3652 | { |
3651 | 3653 | if (player_ctx->tvchain->NeedsToSwitch()) |
… |
… |
bool NuppelVideoPlayer::StartPlaying(bool openfile) |
3697 | 3699 | continue; |
3698 | 3700 | } |
3699 | 3701 | |
3700 | | if (eof) |
| 3702 | if (eof != kEofStateNone) |
3701 | 3703 | { |
3702 | 3704 | if (player_ctx->tvchain) |
3703 | 3705 | { |
… |
… |
bool NuppelVideoPlayer::StartPlaying(bool openfile) |
3711 | 3713 | VERBOSE(VB_PLAYBACK, "Ignoring livetv eof in decoder loop"); |
3712 | 3714 | usleep(50000); |
3713 | 3715 | } |
3714 | | else |
| 3716 | else if (eof == kEofStateImmediate) |
| 3717 | { |
3715 | 3718 | break; |
| 3719 | } |
| 3720 | else |
| 3721 | { |
| 3722 | VERBOSE(VB_PLAYBACK, "waiting for no video frames " << videoOutput->ValidVideoFrames()); |
| 3723 | if (videoOutput && videoOutput->ValidVideoFrames() < 3) |
| 3724 | { |
| 3725 | eof = kEofStateImmediate; |
| 3726 | break; |
| 3727 | } |
| 3728 | else |
| 3729 | usleep(50000); |
| 3730 | } |
3716 | 3731 | } |
3717 | 3732 | |
3718 | 3733 | if (isDummy) |
… |
… |
bool NuppelVideoPlayer::StartPlaying(bool openfile) |
3799 | 3814 | if (fftime >= 5) |
3800 | 3815 | DoFastForward(); |
3801 | 3816 | |
3802 | | if (eof) |
| 3817 | if (eof != kEofStateNone) |
3803 | 3818 | continue; |
3804 | 3819 | |
3805 | 3820 | UnpauseVideo(true); |
… |
… |
bool NuppelVideoPlayer::StartPlaying(bool openfile) |
3854 | 3869 | && !player_ctx->IsPIP() && |
3855 | 3870 | player_ctx->GetState() == kState_WatchingPreRecorded)) |
3856 | 3871 | { |
3857 | | eof = true; |
| 3872 | eof = kEofStateImmediate; |
3858 | 3873 | } |
3859 | 3874 | } |
3860 | 3875 | else |
… |
… |
bool NuppelVideoPlayer::TranscodeGetNextFrame(QMap<long long, int>::Iterator &dm |
6150 | 6165 | |
6151 | 6166 | if (!GetDecoder()->GetFrame(0)) |
6152 | 6167 | return false; |
6153 | | if (eof) |
| 6168 | if (eof != kEofStateNone) |
6154 | 6169 | return false; |
6155 | 6170 | |
6156 | 6171 | if (honorCutList && (!deleteMap.isEmpty())) |
… |
… |
bool NuppelVideoPlayer::TranscodeGetNextFrame(QMap<long long, int>::Iterator &dm |
6184 | 6199 | } |
6185 | 6200 | } |
6186 | 6201 | } |
6187 | | if (eof) |
| 6202 | if (eof != kEofStateNone) |
6188 | 6203 | return false; |
6189 | 6204 | *is_key = GetDecoder()->isLastFrameKey(); |
6190 | 6205 | return true; |
… |
… |
bool NuppelVideoPlayer::RebuildSeekTable(bool showPercentage, StatusCallback cb, |
6276 | 6291 | fflush( stdout ); |
6277 | 6292 | } |
6278 | 6293 | |
6279 | | while (!eof) |
| 6294 | while (eof == kEofStateNone) |
6280 | 6295 | { |
6281 | 6296 | looped = true; |
6282 | 6297 | myFramesPlayed++; |
diff --git a/mythtv/libs/libmythtv/NuppelVideoPlayer.h b/mythtv/libs/libmythtv/NuppelVideoPlayer.h
index f930f02..f5e3907 100644
a
|
b
|
enum |
99 | 99 | kDisplayTeletextMenu = 0x40, |
100 | 100 | }; |
101 | 101 | |
| 102 | // Eof States |
| 103 | typedef enum |
| 104 | { |
| 105 | kEofStateNone, |
| 106 | kEofStateDelayed, |
| 107 | kEofStateImmediate |
| 108 | } EofState; |
| 109 | |
102 | 110 | class MPUBLIC NuppelVideoPlayer : public CC608Reader, public CC708Reader |
103 | 111 | { |
104 | 112 | friend class PlayerContext; |
… |
… |
class MPUBLIC NuppelVideoPlayer : public CC608Reader, public CC708Reader |
146 | 154 | void SetLength(int len) { totalLength = len; } |
147 | 155 | void SetVideoFilters(const QString &override); |
148 | 156 | void SetFramesPlayed(long long played) { framesPlayed = played; } |
149 | | void SetEof(void) { eof = true; } |
| 157 | void SetEof(void) { eof = kEofStateDelayed; } |
150 | 158 | void SetPIPActive(bool is_active) { pip_active = is_active; } |
151 | 159 | void SetPIPVisible(bool is_visible) { pip_visible = is_visible; } |
152 | 160 | bool AddPIPPlayer(NuppelVideoPlayer *pip, PIPLocation loc, uint timeout); |
… |
… |
class MPUBLIC NuppelVideoPlayer : public CC608Reader, public CC708Reader |
214 | 222 | // Bool Gets |
215 | 223 | bool GetRawAudioState(void) const; |
216 | 224 | bool GetLimitKeyRepeat(void) const { return limitKeyRepeat; } |
217 | | bool GetEof(void) const { return eof; } |
| 225 | bool GetEof(void) const { return eof != kEofStateNone; } |
218 | 226 | bool IsErrored(void) const; |
219 | 227 | bool IsPlaying(uint wait_ms = 0, bool wait_for = true) const; |
220 | 228 | bool AtNormalSpeed(void) const { return next_normal_speed; } |
… |
… |
class MPUBLIC NuppelVideoPlayer : public CC608Reader, public CC708Reader |
569 | 577 | mutable QMutex pauseUnpauseLock; |
570 | 578 | mutable QMutex internalPauseLock; |
571 | 579 | mutable QMutex playingLock; |
572 | | bool eof; ///< At end of file/ringbuffer |
| 580 | EofState eof; ///< At end of file/ringbuffer |
573 | 581 | bool m_double_framerate;///< Output fps is double Video (input) rate |
574 | 582 | bool m_double_process;///< Output filter must processed at double rate |
575 | 583 | bool m_can_double; ///< VideoOutput capable of doubling frame rate |
diff --git a/mythtv/programs/mythtranscode/transcode.cpp b/mythtv/programs/mythtranscode/transcode.cpp
index ff8eb54..ed909c8 100644
a
|
b
|
class AudioReencodeBuffer : public AudioOutput |
83 | 83 | } |
84 | 84 | |
85 | 85 | virtual void SetBlocking(bool block) { (void)block; } |
| 86 | virtual void SetTimedBlocking(bool block) { (void)block; } |
86 | 87 | virtual void Reset(void) |
87 | 88 | { |
88 | 89 | audiobuffer_len = 0; |