diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp
index 5b80c452a3b..db5bb6f06eb 100644
a
|
b
|
MythPlayer::MythPlayer(PlayerFlags flags) |
223 | 223 | rtcbase(0), |
224 | 224 | maxtcval(0), maxtcframes(0), |
225 | 225 | numdroppedframes(0), |
| 226 | prior_audiotimecode(0), |
| 227 | prior_videotimecode(0), |
226 | 228 | // LiveTVChain stuff |
227 | 229 | m_tv(nullptr), isDummy(false), |
228 | 230 | // Counter for buffering messages |
… |
… |
bool MythPlayer::Play(float speed, bool normal, bool unpauseaudio) |
415 | 417 | return false; |
416 | 418 | } |
417 | 419 | rtcbase = 0; |
| 420 | prior_audiotimecode = 0; |
| 421 | prior_videotimecode = 0; |
418 | 422 | SetEof(kEofStateNone); |
419 | 423 | UnpauseBuffer(); |
420 | 424 | UnpauseDecoder(); |
… |
… |
void MythPlayer::ResetAVSync(void) |
1806 | 1810 | prevtc = 0; |
1807 | 1811 | avsync_next = avsync_interval; // Frames till next sync check |
1808 | 1812 | rtcbase = 0; |
| 1813 | prior_audiotimecode = 0; |
| 1814 | prior_videotimecode = 0; |
1809 | 1815 | LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + "A/V sync reset"); |
1810 | 1816 | } |
1811 | 1817 | |
… |
… |
void MythPlayer::InitAVSync(void) |
1822 | 1828 | // Number of frames over which to average time divergence |
1823 | 1829 | avsync_averaging=4; |
1824 | 1830 | rtcbase = 0; |
| 1831 | prior_audiotimecode = 0; |
| 1832 | prior_videotimecode = 0; |
1825 | 1833 | |
1826 | 1834 | // Special averaging default of 60 for OpenMAX passthru |
1827 | 1835 | QString device = gCoreContext->GetSetting("AudioOutputDevice",""); |
… |
… |
void wait_for_time(int64_t framedue) |
2170 | 2178 | QThread::usleep(delay); |
2171 | 2179 | } |
2172 | 2180 | |
2173 | | #define AVSYNC_MAX_LATE 1000000 |
| 2181 | #define AVSYNC_MAX_LATE 10000000 |
2174 | 2182 | void MythPlayer::AVSync2(VideoFrame *buffer) |
2175 | 2183 | { |
2176 | 2184 | if (videoOutput->IsErrored()) |
… |
… |
void MythPlayer::AVSync2(VideoFrame *buffer) |
2180 | 2188 | SetErrored(tr("Failed to initialize A/V Sync")); |
2181 | 2189 | return; |
2182 | 2190 | } |
2183 | | int64_t audiotimecode = audio.GetAudioTime(); |
2184 | 2191 | int64_t videotimecode = 0; |
2185 | 2192 | |
2186 | 2193 | bool dropframe = false; |
… |
… |
void MythPlayer::AVSync2(VideoFrame *buffer) |
2191 | 2198 | int64_t unow = 0; |
2192 | 2199 | int64_t lateness = 0; |
2193 | 2200 | int64_t playspeed1000 = (float)1000 / play_speed; |
| 2201 | bool reset = false; |
2194 | 2202 | |
2195 | 2203 | while (framedue == 0) |
2196 | 2204 | { |
2197 | | bool reset = false; |
2198 | 2205 | if (buffer) |
2199 | 2206 | { |
2200 | 2207 | videotimecode = buffer->timecode & 0x0000ffffffffffff; |
… |
… |
void MythPlayer::AVSync2(VideoFrame *buffer) |
2210 | 2217 | { |
2211 | 2218 | // cater for DVB radio |
2212 | 2219 | if (videotimecode == 0) |
2213 | | videotimecode = audiotimecode; |
| 2220 | videotimecode = audio.GetAudioTime();; |
2214 | 2221 | // On first frame we get nothing, so exit out. |
2215 | 2222 | if (videotimecode == 0) |
2216 | 2223 | return; |
… |
… |
void MythPlayer::AVSync2(VideoFrame *buffer) |
2244 | 2251 | if (lateness > 30000) |
2245 | 2252 | dropframe = !FlagIsSet(kMusicChoice) && numdroppedframes < 10; |
2246 | 2253 | |
2247 | | if (lateness <= 30000 && audiotimecode > 0 && normal_speed && !FlagIsSet(kMusicChoice)) |
| 2254 | if (lateness <= 30000 && prior_audiotimecode > 0 |
| 2255 | && prior_videotimecode > 0 && normal_speed && !FlagIsSet(kMusicChoice)) |
2248 | 2256 | { |
2249 | 2257 | // Get video in sync with audio |
2250 | | audio_adjustment = audiotimecode - videotimecode; |
2251 | | int sign = audio_adjustment < 0 ? -1 : 1; |
2252 | | if (audio_adjustment * sign > 40) |
| 2258 | audio_adjustment = prior_audiotimecode - prior_videotimecode; |
| 2259 | // If there is excess audio - throw it away. |
| 2260 | if (audio_adjustment < -200) |
2253 | 2261 | { |
2254 | | // adjust by AVSyncIncrementMS milliseconds at a time (range 1-40) |
2255 | | rtcbase -= (int64_t)1000000 * avsync2adjustms * sign / playspeed1000; |
| 2262 | audio.Reset(); |
| 2263 | audio_adjustment = 0; |
| 2264 | } |
| 2265 | int sign = audio_adjustment < 0 ? -1 : 1; |
| 2266 | int64_t fix_amount = audio_adjustment * sign; |
| 2267 | if (fix_amount > avsync2adjustms) |
| 2268 | fix_amount = avsync2adjustms; |
| 2269 | // Faster catch-up when off by more than 200 ms |
| 2270 | if (audio_adjustment * sign > 200) |
| 2271 | // fix the sync within 15 - 20 frames |
| 2272 | fix_amount = audio_adjustment * sign / 15; |
| 2273 | int64_t speedup1000 = (float)1000 * play_speed; |
| 2274 | rtcbase -= (int64_t)1000000 * fix_amount * sign / speedup1000; |
| 2275 | if (audio_adjustment * sign > 20 || (framesPlayed % 400 == 0)) |
2256 | 2276 | LOG(VB_PLAYBACK, LOG_INFO, LOC + |
2257 | 2277 | QString("AV Sync, audio ahead by %1 ms").arg(audio_adjustment)); |
2258 | | } |
2259 | | if (audio_adjustment > 1000) |
| 2278 | if (audio_adjustment > 200) |
2260 | 2279 | pause_audio = true; |
2261 | 2280 | } |
2262 | 2281 | // sanity check - reset rtcbase if time codes have gone crazy. |
… |
… |
void MythPlayer::AVSync2(VideoFrame *buffer) |
2277 | 2296 | reset = true; |
2278 | 2297 | } |
2279 | 2298 | } |
2280 | | |
| 2299 | prior_videotimecode = videotimecode; |
2281 | 2300 | disp_timecode = videotimecode; |
2282 | 2301 | |
2283 | 2302 | output_jmeter && output_jmeter->RecordCycleTime(); |
… |
… |
void MythPlayer::AVSync2(VideoFrame *buffer) |
2307 | 2326 | audio.Pause(true); |
2308 | 2327 | } |
2309 | 2328 | |
2310 | | LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + |
2311 | | QString("A/V timecodes audio=%1 video=%2 frameinterval=%3 " |
2312 | | "audioadj=%4 tcoffset=%5 unow=%6 udue=%7") |
2313 | | .arg(audiotimecode) |
2314 | | .arg(videotimecode) |
2315 | | .arg(frame_interval) |
2316 | | .arg(audio_adjustment) |
2317 | | .arg(tc_wrap[TC_AUDIO]) |
2318 | | .arg(unow) |
2319 | | .arg(framedue) |
2320 | | ); |
2321 | 2329 | |
2322 | 2330 | if (dropframe) |
2323 | 2331 | numdroppedframes++; |
… |
… |
void MythPlayer::AVSync2(VideoFrame *buffer) |
2338 | 2346 | // the primary PBP will become out of sync |
2339 | 2347 | if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP()) |
2340 | 2348 | wait_for_time(framedue); |
| 2349 | // get time codes for calculating difference next time |
| 2350 | prior_audiotimecode = audio.GetAudioTime(); |
2341 | 2351 | videoOutput->Show(ps); |
2342 | 2352 | if (videoOutput->IsErrored()) |
2343 | 2353 | { |
… |
… |
void MythPlayer::AVSync2(VideoFrame *buffer) |
2373 | 2383 | } |
2374 | 2384 | else |
2375 | 2385 | wait_for_time(framedue); |
| 2386 | |
| 2387 | LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + |
| 2388 | QString("A/V timecodes audio=%1 video=%2 frameinterval=%3 " |
| 2389 | "audioadj=%4 tcoffset=%5 unow=%6 udue=%7") |
| 2390 | .arg(prior_audiotimecode) |
| 2391 | .arg(prior_videotimecode) |
| 2392 | .arg(frame_interval) |
| 2393 | .arg(audio_adjustment) |
| 2394 | .arg(tc_wrap[TC_AUDIO]) |
| 2395 | .arg(unow) |
| 2396 | .arg(framedue) |
| 2397 | ); |
| 2398 | |
2376 | 2399 | } |
2377 | 2400 | |
2378 | 2401 | void MythPlayer::RefreshPauseFrame(void) |
… |
… |
bool MythPlayer::PrebufferEnoughFrames(int min_buffers) |
2451 | 2474 | if (!videoOutput) |
2452 | 2475 | return false; |
2453 | 2476 | |
2454 | | bool paused_now = false; |
2455 | 2477 | if (!(min_buffers ? (videoOutput->ValidVideoFrames() >= min_buffers) : |
2456 | 2478 | (GetEof() != kEofStateNone) || |
2457 | 2479 | (videoOutput->hasHWAcceleration() ? |
… |
… |
bool MythPlayer::PrebufferEnoughFrames(int min_buffers) |
2467 | 2489 | // for the jerking is detected. |
2468 | 2490 | |
2469 | 2491 | bool watchingTV = IsWatchingInprogress(); |
2470 | | if (!paused_now && (livetv || watchingTV) && !FlagIsSet(kMusicChoice)) |
| 2492 | if ( (livetv || watchingTV) && !FlagIsSet(kMusicChoice)) |
2471 | 2493 | { |
2472 | 2494 | uint64_t frameCount = GetCurrentFrameCount(); |
2473 | 2495 | uint64_t framesLeft = frameCount - framesPlayed; |
2474 | 2496 | uint64_t margin = (uint64_t) (video_frame_rate * 3); |
2475 | 2497 | if (framesLeft < margin) |
2476 | 2498 | { |
2477 | | LOG(VB_PLAYBACK, LOG_NOTICE, LOC + |
2478 | | QString("Pause to allow live tv catch up. Position in sec. Current: %2, Total: %3") |
2479 | | .arg(framesPlayed).arg(frameCount)); |
| 2499 | if (rtcbase) |
| 2500 | LOG(VB_PLAYBACK, LOG_NOTICE, LOC + |
| 2501 | QString("Pause to allow live tv catch up. Position in sec. Current: %2, Total: %3") |
| 2502 | .arg(framesPlayed).arg(frameCount)); |
2480 | 2503 | audio.Pause(true); |
2481 | 2504 | avsync_audiopaused = true; |
2482 | | paused_now = true; |
| 2505 | rtcbase = 0; |
2483 | 2506 | } |
2484 | 2507 | } |
2485 | 2508 | usleep(frame_interval >> 3); |
diff --git a/mythtv/libs/libmythtv/mythplayer.h b/mythtv/libs/libmythtv/mythplayer.h
index 7358d617a9d..4b295af28fe 100644
a
|
b
|
class MTV_PUBLIC MythPlayer |
856 | 856 | int maxtcframes; // number of frames seen since max to date tc |
857 | 857 | int64_t avsync2adjustms; // number of milliseconds to adjust for av sync errors |
858 | 858 | int numdroppedframes; // number of consecutive dropped frames. |
| 859 | int64_t prior_audiotimecode; // time code from prior frame |
| 860 | int64_t prior_videotimecode; // time code from prior frame |
859 | 861 | |
860 | 862 | // LiveTV |
861 | 863 | TV *m_tv; |
diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp
index ea0aa806d59..37894f75431 100644
a
|
b
|
bool TV::StartTV(ProgramInfo *tvrec, uint flags, |
409 | 409 | startSysEventSent = true; |
410 | 410 | startLivetvEventSent = true; |
411 | 411 | gCoreContext->SendSystemEvent("LIVETV_STARTED"); |
| 412 | usleep(gCoreContext->GetNumSetting("LiveTVWaitMS", 0)*1000); |
412 | 413 | } |
413 | 414 | |
414 | 415 | LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "tv->LiveTV() -- end"); |
diff --git a/mythtv/programs/mythfrontend/globalsettings.cpp b/mythtv/programs/mythfrontend/globalsettings.cpp
index e0551ef2840..4118c585ad5 100644
a
|
b
|
static HostSpinBoxSetting *LiveTVIdleTimeout() |
1810 | 1810 | return gs; |
1811 | 1811 | } |
1812 | 1812 | |
| 1813 | static HostSpinBoxSetting *LiveTVWaitMS() |
| 1814 | { |
| 1815 | HostSpinBoxSetting *gs = new HostSpinBoxSetting("LiveTVWaitMS", 0, 30000, 100); |
| 1816 | |
| 1817 | gs->setLabel(PlaybackSettings::tr("Live TV wait (milliseconds)")); |
| 1818 | |
| 1819 | gs->setValue(0); |
| 1820 | |
| 1821 | gs->setHelpText(PlaybackSettings::tr("Wait the specified number of milliseconds " |
| 1822 | "before beginning playback of Live TV " |
| 1823 | "to avoid stuttering during the first minute")); |
| 1824 | return gs; |
| 1825 | } |
| 1826 | |
| 1827 | |
1813 | 1828 | // static HostCheckBoxSetting *PlaybackPreview() |
1814 | 1829 | // { |
1815 | 1830 | // HostCheckBoxSetting *gc = new HostCheckBoxSetting("PlaybackPreview"); |
… |
… |
void PlaybackSettings::Load(void) |
4224 | 4239 | general->addChild(UseProgStartMark()); |
4225 | 4240 | general->addChild(AutomaticSetWatched()); |
4226 | 4241 | general->addChild(ContinueEmbeddedTVPlay()); |
| 4242 | general->addChild(LiveTVWaitMS()); |
4227 | 4243 | general->addChild(LiveTVIdleTimeout()); |
4228 | 4244 | |
4229 | 4245 | #if CONFIG_DEBUGTYPE |