commit 3b0dd2efb281b59143405d23ccd32cde42fa78c2
Author: Mark Spieth <mspieth@digivation.com.au>
Date: Tue Apr 27 07:51:51 2010 +1000
smoother vsync with predictive frame skipping
diff --git a/mythtv/libs/libmyth/audiooutput.h b/mythtv/libs/libmyth/audiooutput.h
index 2b22a1e..0b28cfb 100644
a
|
b
|
class MPUBLIC AudioOutput : public VolumeBase, public OutputListeners |
44 | 44 | |
45 | 45 | // timecode is in milliseconds. |
46 | 46 | // Return true if all samples were written, false if none. |
47 | | virtual bool AddSamples(char *buffer, int samples, long long timecode) = 0; |
48 | | virtual bool AddSamples(char *buffers[], int samples, long long timecode) = 0; |
| 47 | virtual bool AddSamples(char *buffer, int samples, int64_t timecode) = 0; |
| 48 | virtual bool AddSamples(char *buffers[], int samples, int64_t timecode) = 0; |
49 | 49 | |
50 | | virtual void SetTimecode(long long timecode) = 0; |
| 50 | virtual void SetTimecode(int64_t timecode) = 0; |
51 | 51 | virtual bool IsPaused(void) const = 0; |
52 | 52 | virtual void Pause(bool paused) = 0; |
53 | 53 | |
54 | 54 | // Wait for all data to finish playing |
55 | 55 | virtual void Drain(void) = 0; |
56 | 56 | |
57 | | virtual int GetAudiotime(void) = 0; |
| 57 | virtual int64_t GetAudiotime(void) = 0; |
58 | 58 | |
59 | 59 | /// report amount of audio buffered in milliseconds. |
60 | | virtual int GetAudioBufferedTime(void) { return 0; } |
| 60 | virtual int64_t GetAudioBufferedTime(void) { return 0; } |
61 | 61 | |
62 | 62 | virtual void SetSourceBitrate(int ) { } |
63 | 63 | |
diff --git a/mythtv/libs/libmyth/audiooutputbase.cpp b/mythtv/libs/libmyth/audiooutputbase.cpp
index 0b9f2ef..8ea2b24 100644
a
|
b
|
|
19 | 19 | #define LOC QString("AO: ") |
20 | 20 | #define LOC_ERR QString("AO, ERROR: ") |
21 | 21 | |
| 22 | #define EFF_FACTOR_F 100000.0 |
| 23 | #define EFF_FACTOR_I 100000 |
| 24 | #define EFF_FACTOR_LL 100000LL |
| 25 | |
22 | 26 | AudioOutputBase::AudioOutputBase(const AudioSettings &settings) : |
23 | 27 | // protected |
24 | 28 | effdsp(0), effdspstretched(0), |
… |
… |
AudioOutputBase::AudioOutputBase(const AudioSettings &settings) : |
31 | 35 | audio_passthru_device(settings.GetPassthruDevice()), |
32 | 36 | audio_passthru(false), audio_enc(false), |
33 | 37 | audio_reenc(false), audio_stretchfactor(1.0f), |
| 38 | eff_audio_stretchfactor(10000), |
34 | 39 | |
35 | 40 | source(settings.source), killaudio(false), |
36 | 41 | |
… |
… |
void AudioOutputBase::SetStretchFactorLocked(float laudio_stretchfactor) |
143 | 148 | if ((audio_stretchfactor != laudio_stretchfactor) || !pSoundStretch) |
144 | 149 | { |
145 | 150 | audio_stretchfactor = laudio_stretchfactor; |
| 151 | eff_audio_stretchfactor = (int)(EFF_FACTOR_F * laudio_stretchfactor); |
146 | 152 | if (pSoundStretch) |
147 | 153 | { |
148 | 154 | VERBOSE(VB_GENERAL, LOC + QString("Changing time stretch to %1") |
… |
… |
void AudioOutputBase::Reset() |
528 | 534 | gettimeofday(&audiotime_updated, NULL); |
529 | 535 | } |
530 | 536 | |
531 | | void AudioOutputBase::SetTimecode(long long timecode) |
| 537 | void AudioOutputBase::SetTimecode(int64_t timecode) |
532 | 538 | { |
533 | 539 | QMutexLocker locker(&audio_buflock); |
534 | 540 | audbuf_timecode = timecode; |
535 | | samples_buffered = (long long)((timecode * effdsp) / 100000.0); |
| 541 | samples_buffered = (int64_t)((timecode * effdsp) / EFF_FACTOR_I); |
536 | 542 | } |
537 | 543 | |
538 | 544 | void AudioOutputBase::SetEffDsp(int dsprate) |
… |
… |
int AudioOutputBase::audiofree(bool use_lock) |
580 | 586 | be is kAudioRingBufferSize - 1. */ |
581 | 587 | } |
582 | 588 | |
583 | | int AudioOutputBase::GetAudiotime(void) |
| 589 | int64_t AudioOutputBase::GetAudiotime(void) |
584 | 590 | { |
585 | 591 | /* Returns the current timecode of audio leaving the soundcard, based |
586 | 592 | on the 'audiotime' computed earlier, and the delay since it was computed. |
… |
… |
int AudioOutputBase::GetAudiotime(void) |
590 | 596 | The reason is that computing 'audiotime' requires acquiring the audio |
591 | 597 | lock, which the video thread should not do. So, we call 'SetAudioTime()' |
592 | 598 | from the audio thread, and then call this from the video thread. */ |
593 | | long long ret; |
| 599 | int64_t ret; |
594 | 600 | struct timeval now; |
595 | 601 | |
596 | 602 | if (audiotime == 0) |
… |
… |
int AudioOutputBase::GetAudiotime(void) |
602 | 608 | |
603 | 609 | ret = (now.tv_sec - audiotime_updated.tv_sec) * 1000; |
604 | 610 | ret += (now.tv_usec - audiotime_updated.tv_usec) / 1000; |
605 | | ret = (long long)(ret * audio_stretchfactor); |
| 611 | ret = (int64_t)(ret * audio_stretchfactor); |
606 | 612 | |
607 | 613 | #if 1 |
608 | 614 | VERBOSE(VB_AUDIO+VB_TIMESTAMP, |
… |
… |
int AudioOutputBase::GetAudiotime(void) |
617 | 623 | |
618 | 624 | ret += audiotime; |
619 | 625 | |
620 | | return (int)ret; |
| 626 | return ret; |
621 | 627 | } |
622 | 628 | |
623 | 629 | void AudioOutputBase::SetAudiotime(void) |
… |
… |
void AudioOutputBase::SetAudiotime(void) |
625 | 631 | if (audbuf_timecode == 0) |
626 | 632 | return; |
627 | 633 | |
628 | | int soundcard_buffer = 0; |
629 | | int totalbuffer; |
| 634 | int64_t soundcard_buffer = 0; |
| 635 | int64_t totalsamples_stretched; |
| 636 | int64_t totalsamples_unstretched = 0; |
630 | 637 | |
631 | 638 | /* We want to calculate 'audiotime', which is the timestamp of the audio |
632 | 639 | which is leaving the sound card at this instant. |
… |
… |
void AudioOutputBase::SetAudiotime(void) |
649 | 656 | QMutexLocker lock2(&avsync_lock); |
650 | 657 | |
651 | 658 | soundcard_buffer = GetBufferedOnSoundcard(); // bytes |
652 | | totalbuffer = audiolen(false) + soundcard_buffer; |
653 | | |
654 | | // include algorithmic latencies |
655 | | if (pSoundStretch) |
656 | | totalbuffer += (int)((pSoundStretch->numUnprocessedSamples() * |
657 | | audio_bytes_per_sample) / audio_stretchfactor); |
| 659 | // major post-stretched buffer contents |
| 660 | totalsamples_stretched = (audiolen(false) + soundcard_buffer) / audio_bytes_per_sample; |
658 | 661 | |
| 662 | // include algorithmic pre-stretch latencies |
659 | 663 | if (upmixer && needs_upmix) |
660 | | totalbuffer += upmixer->sampleLatency() * audio_bytes_per_sample; |
| 664 | totalsamples_unstretched += upmixer->sampleLatency(); |
661 | 665 | |
| 666 | if (pSoundStretch) |
| 667 | totalsamples_unstretched += pSoundStretch->numUnprocessedSamples(); |
| 668 | |
| 669 | // include algorithmic post-stretch latencies |
662 | 670 | if (encoder) |
663 | | totalbuffer += encoder->Buffered(); |
| 671 | // the input buffered data is still in audio_bytes_per_sample format |
| 672 | totalsamples_stretched += encoder->Buffered() / audio_bytes_per_sample; |
664 | 673 | |
665 | | audiotime = audbuf_timecode - (int)(totalbuffer * 100000.0 / |
666 | | (audio_bytes_per_sample * effdspstretched)); |
| 674 | // timecode is the stretch adjusted version |
| 675 | audiotime = audbuf_timecode - (int64_t)((totalsamples_unstretched * EFF_FACTOR_I + |
| 676 | totalsamples_stretched * eff_audio_stretchfactor ) / effdsp ); |
667 | 677 | |
668 | 678 | gettimeofday(&audiotime_updated, NULL); |
669 | 679 | #if 1 |
670 | 680 | VERBOSE(VB_AUDIO+VB_TIMESTAMP, |
671 | 681 | QString("SetAudiotime set=%1.%2, audt=%3 atc=%4 " |
672 | | "tb=%5 sb=%6 eds=%7 abps=%8 sf=%9") |
| 682 | "tss=%5 tsu=%6 sb=%7 eds=%8 abps=%9 sf=%10") |
673 | 683 | .arg(audiotime_updated.tv_sec).arg(audiotime_updated.tv_usec) |
674 | 684 | .arg(audiotime) |
675 | 685 | .arg(audbuf_timecode) |
676 | | .arg(totalbuffer) |
| 686 | .arg(totalsamples_stretched) |
| 687 | .arg(totalsamples_unstretched) |
677 | 688 | .arg(soundcard_buffer) |
678 | 689 | .arg(effdspstretched) |
679 | 690 | .arg(audio_bytes_per_sample) |
… |
… |
void AudioOutputBase::SetAudiotime(void) |
681 | 692 | #endif |
682 | 693 | } |
683 | 694 | |
684 | | int AudioOutputBase::GetAudioBufferedTime(void) |
| 695 | int64_t AudioOutputBase::GetAudioBufferedTime(void) |
685 | 696 | { |
686 | 697 | return audbuf_timecode - GetAudiotime(); |
687 | 698 | } |
… |
… |
void AudioOutputBase::_AdjustVolume(AudioDataType *buffer, int len, bool music) |
744 | 755 | } |
745 | 756 | |
746 | 757 | bool AudioOutputBase::AddSamples(char *buffers[], int samples, |
747 | | long long timecode) |
| 758 | int64_t timecode) |
748 | 759 | { |
749 | 760 | // NOTE: This function is not threadsafe |
750 | 761 | int afree = audiofree(true); |
… |
… |
bool AudioOutputBase::AddSamples(char *buffers[], int samples, |
823 | 834 | return true; |
824 | 835 | } |
825 | 836 | |
826 | | bool AudioOutputBase::AddSamples(char *buffer, int samples, long long timecode) |
| 837 | bool AudioOutputBase::AddSamples(char *buffer, int samples, int64_t timecode) |
827 | 838 | { |
828 | 839 | // NOTE: This function is not threadsafe |
829 | 840 | |
… |
… |
void *AudioOutputBase::_MonoToStereo(AudioDataType *s1, AudioDataType *s2, int s |
965 | 976 | return s2; |
966 | 977 | } |
967 | 978 | |
968 | | void AudioOutputBase::_AddSamples(void *buffer, bool interleaved, int samples, |
969 | | long long timecode) |
| 979 | void AudioOutputBase::_AddSamples(void *buffer, bool interleaved, int in_samples, |
| 980 | int64_t timecode) |
970 | 981 | { |
971 | 982 | int len; // = samples * audio_bytes_per_sample; |
972 | 983 | int audio_bytes = audio_bits / 8; |
973 | 984 | int org_waud = waud; |
| 985 | int samples = in_samples; |
974 | 986 | |
975 | 987 | int afree = audiofree(false); |
976 | 988 | |
… |
… |
void AudioOutputBase::_AddSamples(void *buffer, bool interleaved, int samples, |
1009 | 1021 | int out_samples = 0; |
1010 | 1022 | org_waud = waud; |
1011 | 1023 | int step = (interleaved)?source_audio_channels:1; |
1012 | | |
| 1024 | |
1013 | 1025 | for (int itemp = 0; itemp < samples; ) |
1014 | 1026 | { |
1015 | 1027 | if (audio_bytes == 2) |
… |
… |
void AudioOutputBase::_AddSamples(void *buffer, bool interleaved, int samples, |
1094 | 1106 | } |
1095 | 1107 | } |
1096 | 1108 | |
1097 | | if (samples <= 0) |
1098 | | return; |
1099 | | |
1100 | | if (pSoundStretch) |
| 1109 | if (samples > 0) |
1101 | 1110 | { |
1102 | | // does not change the timecode, only the number of samples |
1103 | | // back to orig pos |
1104 | | org_waud = waud; |
1105 | | int bdiff = kAudioRingBufferSize - org_waud; |
1106 | | int nSamplesToEnd = bdiff/abps; |
1107 | | if (bdiff < len) |
1108 | | { |
1109 | | pSoundStretch->putSamples((soundtouch::SAMPLETYPE*) |
1110 | | (audiobuffer + |
1111 | | org_waud), nSamplesToEnd); |
1112 | | pSoundStretch->putSamples((soundtouch::SAMPLETYPE*)audiobuffer, |
1113 | | (len - bdiff) / abps); |
1114 | | } |
1115 | | else |
1116 | | { |
1117 | | pSoundStretch->putSamples((soundtouch::SAMPLETYPE*) |
1118 | | (audiobuffer + org_waud), |
1119 | | len / abps); |
1120 | | } |
1121 | 1111 | |
1122 | | int nSamples = pSoundStretch->numSamples(); |
1123 | | len = WaitForFreeSpace(nSamples); |
1124 | | |
1125 | | while ((nSamples = pSoundStretch->numSamples())) |
| 1112 | if (pSoundStretch) |
1126 | 1113 | { |
1127 | | if (nSamples > nSamplesToEnd) |
1128 | | nSamples = nSamplesToEnd; |
1129 | | |
1130 | | nSamples = pSoundStretch->receiveSamples( |
1131 | | (soundtouch::SAMPLETYPE*) |
1132 | | (audiobuffer + org_waud), nSamples |
1133 | | ); |
1134 | | |
1135 | | if (nSamples == nSamplesToEnd) { |
1136 | | org_waud = 0; |
1137 | | nSamplesToEnd = kAudioRingBufferSize/abps; |
| 1114 | // does not change the timecode, only the number of samples |
| 1115 | // back to orig pos |
| 1116 | org_waud = waud; |
| 1117 | int bdiff = kAudioRingBufferSize - org_waud; |
| 1118 | int nSamplesToEnd = bdiff/abps; |
| 1119 | if (bdiff < len) |
| 1120 | { |
| 1121 | pSoundStretch->putSamples((soundtouch::SAMPLETYPE*) |
| 1122 | (audiobuffer + |
| 1123 | org_waud), nSamplesToEnd); |
| 1124 | pSoundStretch->putSamples((soundtouch::SAMPLETYPE*)audiobuffer, |
| 1125 | (len - bdiff) / abps); |
1138 | 1126 | } |
1139 | | else { |
1140 | | org_waud += nSamples * abps; |
1141 | | nSamplesToEnd -= nSamples; |
| 1127 | else |
| 1128 | { |
| 1129 | pSoundStretch->putSamples((soundtouch::SAMPLETYPE*) |
| 1130 | (audiobuffer + org_waud), |
| 1131 | len / abps); |
1142 | 1132 | } |
1143 | | } |
1144 | | } |
1145 | 1133 | |
1146 | | if (internal_vol && SWVolume()) |
1147 | | { |
1148 | | int bdiff = kAudioRingBufferSize - waud; |
1149 | | bool music = (timecode < 1); |
| 1134 | int nSamples = pSoundStretch->numSamples(); |
| 1135 | len = WaitForFreeSpace(nSamples); |
1150 | 1136 | |
1151 | | if (bdiff < len) |
1152 | | { |
1153 | | AdjustVolume(audiobuffer + waud, bdiff, music); |
1154 | | AdjustVolume(audiobuffer, len - bdiff, music); |
1155 | | } |
1156 | | else |
1157 | | AdjustVolume(audiobuffer + waud, len, music); |
1158 | | } |
| 1137 | while ((nSamples = pSoundStretch->numSamples())) |
| 1138 | { |
| 1139 | if (nSamples > nSamplesToEnd) |
| 1140 | nSamples = nSamplesToEnd; |
1159 | 1141 | |
1160 | | // Encode to AC-3? |
1161 | | if (encoder) |
1162 | | { |
1163 | | org_waud = waud; |
1164 | | int bdiff = kAudioRingBufferSize - org_waud; |
1165 | | int to_get = 0; |
| 1142 | nSamples = pSoundStretch->receiveSamples( |
| 1143 | (soundtouch::SAMPLETYPE*) |
| 1144 | (audiobuffer + org_waud), nSamples |
| 1145 | ); |
1166 | 1146 | |
1167 | | if (bdiff < len) |
| 1147 | if (nSamples == nSamplesToEnd) { |
| 1148 | org_waud = 0; |
| 1149 | nSamplesToEnd = kAudioRingBufferSize/abps; |
| 1150 | } |
| 1151 | else { |
| 1152 | org_waud += nSamples * abps; |
| 1153 | nSamplesToEnd -= nSamples; |
| 1154 | } |
| 1155 | } |
| 1156 | } |
| 1157 | |
| 1158 | if (internal_vol && SWVolume()) |
1168 | 1159 | { |
1169 | | encoder->Encode(audiobuffer + org_waud, bdiff); |
1170 | | to_get = encoder->Encode(audiobuffer, len - bdiff); |
| 1160 | int bdiff = kAudioRingBufferSize - waud; |
| 1161 | bool music = (timecode < 1); |
| 1162 | |
| 1163 | if (bdiff < len) |
| 1164 | { |
| 1165 | AdjustVolume(audiobuffer + waud, bdiff, music); |
| 1166 | AdjustVolume(audiobuffer, len - bdiff, music); |
| 1167 | } |
| 1168 | else |
| 1169 | AdjustVolume(audiobuffer + waud, len, music); |
1171 | 1170 | } |
1172 | | else |
1173 | | to_get = encoder->Encode(audiobuffer + org_waud, len); |
1174 | 1171 | |
1175 | | if (to_get > 0) |
| 1172 | // Encode to AC-3? |
| 1173 | if (encoder) |
1176 | 1174 | { |
1177 | | if (to_get >= bdiff) |
| 1175 | org_waud = waud; |
| 1176 | int bdiff = kAudioRingBufferSize - org_waud; |
| 1177 | int to_get = 0; |
| 1178 | |
| 1179 | if (bdiff < len) |
1178 | 1180 | { |
1179 | | encoder->GetFrames(audiobuffer + org_waud, bdiff); |
1180 | | to_get -= bdiff; |
1181 | | org_waud = 0; |
| 1181 | encoder->Encode(audiobuffer + org_waud, bdiff); |
| 1182 | to_get = encoder->Encode(audiobuffer, len - bdiff); |
1182 | 1183 | } |
1183 | | if (to_get > 0) |
1184 | | encoder->GetFrames(audiobuffer + org_waud, to_get); |
| 1184 | else |
| 1185 | to_get = encoder->Encode(audiobuffer + org_waud, len); |
| 1186 | |
| 1187 | if (to_get > 0) |
| 1188 | { |
| 1189 | if (to_get >= bdiff) |
| 1190 | { |
| 1191 | encoder->GetFrames(audiobuffer + org_waud, bdiff); |
| 1192 | to_get -= bdiff; |
| 1193 | org_waud = 0; |
| 1194 | } |
| 1195 | if (to_get > 0) |
| 1196 | encoder->GetFrames(audiobuffer + org_waud, to_get); |
1185 | 1197 | |
1186 | | org_waud += to_get; |
| 1198 | org_waud += to_get; |
| 1199 | } |
1187 | 1200 | } |
1188 | | } |
1189 | 1201 | |
1190 | | waud = org_waud; |
1191 | | lastaudiolen = audiolen(false); |
| 1202 | waud = org_waud; |
| 1203 | lastaudiolen = audiolen(false); |
| 1204 | } |
1192 | 1205 | |
1193 | 1206 | if (timecode < 0) |
1194 | 1207 | // mythmusic doesn't give timestamps.. |
1195 | | timecode = (int)((samples_buffered * 100000.0) / effdsp); |
| 1208 | timecode = (int64_t)((samples_buffered * EFF_FACTOR_I) / effdsp); |
1196 | 1209 | |
1197 | | samples_buffered += samples; |
| 1210 | samples_buffered += in_samples; |
1198 | 1211 | |
1199 | 1212 | /* we want the time at the end -- but the file format stores |
1200 | 1213 | time at the start of the chunk. */ |
1201 | 1214 | // even with timestretch, timecode is still calculated from original |
1202 | 1215 | // sample count |
1203 | | audbuf_timecode = timecode + (int)((samples * 100000.0) / effdsp); |
| 1216 | audbuf_timecode = timecode + (int64_t)((in_samples * EFF_FACTOR_I) / effdsp); |
1204 | 1217 | } |
1205 | 1218 | |
1206 | 1219 | void AudioOutputBase::Status() |
… |
… |
void AudioOutputBase::OutputAudioLoop(void) |
1237 | 1250 | unsigned char *zeros = new unsigned char[fragment_size]; |
1238 | 1251 | unsigned char *fragment = new unsigned char[fragment_size]; |
1239 | 1252 | |
| 1253 | // to reduce startup latency, write silence in 8ms chunks |
| 1254 | int zero_fragment_size = (int)(0.008*audio_samplerate/audio_channels); |
| 1255 | zero_fragment_size *= audio_channels * audio_bits / 16; // make sure its a multiple of audio_channels |
| 1256 | if (zero_fragment_size > fragment_size) |
| 1257 | zero_fragment_size = fragment_size; |
| 1258 | |
1240 | 1259 | bzero(zeros, fragment_size); |
1241 | 1260 | last_space_on_soundcard = 0; |
1242 | 1261 | |
… |
… |
void AudioOutputBase::OutputAudioLoop(void) |
1269 | 1288 | |
1270 | 1289 | // only send zeros if card doesn't already have at least one |
1271 | 1290 | // fragment of zeros -dag |
1272 | | if (fragment_size >= soundcard_buffer_size - space_on_soundcard) |
| 1291 | if (zero_fragment_size >= soundcard_buffer_size - space_on_soundcard) |
1273 | 1292 | { |
1274 | | if (fragment_size <= space_on_soundcard) |
| 1293 | if (zero_fragment_size <= space_on_soundcard) |
1275 | 1294 | { |
1276 | | WriteAudio(zeros, fragment_size); |
| 1295 | WriteAudio(zeros, zero_fragment_size); |
1277 | 1296 | } |
1278 | 1297 | else |
1279 | 1298 | { |
… |
… |
void AudioOutputBase::OutputAudioLoop(void) |
1281 | 1300 | VERBOSE(VB_AUDIO+VB_TIMESTAMP, LOC + |
1282 | 1301 | QString("waiting for space on soundcard " |
1283 | 1302 | "to write zeros: have %1 need %2") |
1284 | | .arg(space_on_soundcard).arg(fragment_size)); |
| 1303 | .arg(space_on_soundcard).arg(zero_fragment_size)); |
1285 | 1304 | usleep(5000); |
1286 | 1305 | } |
1287 | 1306 | } |
diff --git a/mythtv/libs/libmyth/audiooutputbase.h b/mythtv/libs/libmyth/audiooutputbase.h
index b962a60..43e18cc 100644
a
|
b
|
class AudioOutputBase : public AudioOutput, public QThread |
52 | 52 | int GetSWVolume(void); |
53 | 53 | |
54 | 54 | // timecode is in milliseconds. |
55 | | virtual bool AddSamples(char *buffer, int samples, long long timecode); |
56 | | virtual bool AddSamples(char *buffers[], int samples, long long timecode); |
| 55 | virtual bool AddSamples(char *buffer, int samples, int64_t timecode); |
| 56 | virtual bool AddSamples(char *buffers[], int samples, int64_t timecode); |
57 | 57 | |
58 | | virtual void SetTimecode(long long timecode); |
| 58 | virtual void SetTimecode(int64_t timecode); |
59 | 59 | virtual bool IsPaused(void) const { return audio_actually_paused; } |
60 | 60 | virtual void Pause(bool paused); |
61 | 61 | |
62 | 62 | // Wait for all data to finish playing |
63 | 63 | virtual void Drain(void); |
64 | 64 | |
65 | | virtual int GetAudiotime(void); |
66 | | virtual int GetAudioBufferedTime(void); |
| 65 | virtual int64_t GetAudiotime(void); |
| 66 | virtual int64_t GetAudioBufferedTime(void); |
67 | 67 | |
68 | 68 | // Send output events showing current progress |
69 | 69 | virtual void Status(void); |
… |
… |
class AudioOutputBase : public AudioOutput, public QThread |
101 | 101 | |
102 | 102 | int GetAudioData(unsigned char *buffer, int buf_size, bool fill_buffer); |
103 | 103 | |
104 | | void _AddSamples(void *buffer, bool interleaved, int samples, long long timecode); |
| 104 | void _AddSamples(void *buffer, bool interleaved, int samples, int64_t timecode); |
105 | 105 | |
106 | 106 | void OutputAudioLoop(void); |
107 | 107 | |
… |
… |
class AudioOutputBase : public AudioOutput, public QThread |
118 | 118 | |
119 | 119 | void SetStretchFactorLocked(float factor); |
120 | 120 | |
121 | | int GetBaseAudioTime() const { return audiotime; } |
122 | | int GetBaseAudBufTimeCode() const { return audbuf_timecode; } |
| 121 | int64_t GetBaseAudioTime() const { return audiotime; } |
| 122 | int64_t GetBaseAudBufTimeCode() const { return audbuf_timecode; } |
123 | 123 | soundtouch::SoundTouch *GetSoundStretch() const { return pSoundStretch; } |
124 | | void SetBaseAudioTime(const int inAudioTime) { audiotime = inAudioTime; } |
| 124 | void SetBaseAudioTime(const int64_t inAudioTime) { audiotime = inAudioTime; } |
125 | 125 | |
126 | 126 | protected: |
127 | 127 | int effdsp; // from the recorded stream |
… |
… |
class AudioOutputBase : public AudioOutput, public QThread |
144 | 144 | bool audio_reenc; |
145 | 145 | |
146 | 146 | float audio_stretchfactor; |
| 147 | int eff_audio_stretchfactor; // scaled to 10000 as effdsp is |
147 | 148 | AudioOutputSource source; |
148 | 149 | |
149 | 150 | bool killaudio; |
… |
… |
class AudioOutputBase : public AudioOutput, public QThread |
188 | 189 | bool timed_blocking; // do AddSamples calls block? |
189 | 190 | |
190 | 191 | int lastaudiolen; |
191 | | long long samples_buffered; |
| 192 | int64_t samples_buffered; |
192 | 193 | |
193 | 194 | bool audio_thread_exists; |
194 | 195 | |
… |
… |
class AudioOutputBase : public AudioOutput, public QThread |
205 | 206 | QMutex avsync_lock; |
206 | 207 | |
207 | 208 | /// timecode of audio leaving the soundcard (same units as timecodes) |
208 | | long long audiotime; |
| 209 | int64_t audiotime; |
209 | 210 | struct timeval audiotime_updated; // ... which was last updated at this time |
210 | 211 | |
211 | 212 | /* Audio circular buffer */ |
212 | 213 | int raud, waud; /* read and write positions */ |
213 | 214 | /// timecode of audio most recently placed into buffer |
214 | | long long audbuf_timecode; |
| 215 | int64_t audbuf_timecode; |
215 | 216 | |
216 | 217 | int numlowbuffer; |
217 | 218 | |
diff --git a/mythtv/libs/libmythtv/NuppelVideoPlayer.cpp b/mythtv/libs/libmythtv/NuppelVideoPlayer.cpp
index 0da4da0..0d53930 100644
a
|
b
|
NuppelVideoPlayer::NuppelVideoPlayer(bool muted) |
206 | 206 | videosync(NULL), delay(0), |
207 | 207 | vsynctol(30/4), avsync_delay(0), |
208 | 208 | avsync_adjustment(0), avsync_avg(0), |
209 | | avsync_oldavg(0), refreshrate(0), |
| 209 | avsync_oldavg(0), |
| 210 | avsync_predictor(0), avsync_predictor_enabled(false), |
| 211 | refreshrate(0), |
210 | 212 | lastsync(false), m_playing_slower(false), |
211 | 213 | m_stored_audio_stretchfactor(1.0), |
212 | 214 | audio_paused(false), |
… |
… |
NuppelVideoPlayer::NuppelVideoPlayer(bool muted) |
238 | 240 | db_prefer708 = gContext->GetNumSetting("Prefer708Captions", 1); |
239 | 241 | autocommercialskip = (CommSkipMode) |
240 | 242 | gContext->GetNumSetting("AutoCommercialSkip", kCommSkipOff); |
| 243 | usesmoothsync = gContext->GetNumSetting("UseSmoothSync", 1) != 0; |
241 | 244 | |
242 | 245 | lastIgnoredManualSkip = QDateTime::currentDateTime().addSecs(-10); |
243 | 246 | |
… |
… |
void NuppelVideoPlayer::SetVideoParams(int width, int height, double fps, |
1120 | 1123 | video_frame_rate = fps; |
1121 | 1124 | float temp_speed = (play_speed == 0.0f) ? |
1122 | 1125 | audio_stretchfactor : play_speed; |
1123 | | frame_interval = (int)(1000000.0f / video_frame_rate / temp_speed); |
| 1126 | SetFrameInterval(kScan_Progressive, 1.0 / (video_frame_rate * temp_speed)); |
1124 | 1127 | } |
1125 | 1128 | |
1126 | 1129 | if (videoOutput) |
… |
… |
float NuppelVideoPlayer::WarpFactor(void) |
2312 | 2315 | return divergence; |
2313 | 2316 | } |
2314 | 2317 | |
| 2318 | void NuppelVideoPlayer::SetFrameInterval(FrameScanType scan, double frame_period) |
| 2319 | { |
| 2320 | frame_interval = (int)(1000000.0f * frame_period + 0.5f); |
| 2321 | avsync_predictor = 0; |
| 2322 | avsync_predictor_enabled = false; |
| 2323 | |
| 2324 | VERBOSE(VB_PLAYBACK, LOC + QString("SetFrameInterval ps:%1 scan:%2 usesmoothsync:%3") |
| 2325 | .arg(play_speed).arg(scan).arg(usesmoothsync) |
| 2326 | ); |
| 2327 | //if (play_speed <= 1 || play_speed > 2 || scan != kScan_Progressive || !usesmoothsync) |
| 2328 | if (play_speed < 1 || play_speed > 2 || refreshrate <= 0 || !usesmoothsync) |
| 2329 | return; |
| 2330 | |
| 2331 | avsync_predictor_enabled = ((frame_interval-(frame_interval/200)) < refreshrate); |
| 2332 | } |
| 2333 | |
| 2334 | void NuppelVideoPlayer::ResetAVSync(void) |
| 2335 | { |
| 2336 | avsync_avg = 0; |
| 2337 | avsync_oldavg = 0; |
| 2338 | avsync_predictor = 0; |
| 2339 | prevtc = 0; |
| 2340 | warpfactor = 1.0f; |
| 2341 | warpfactor_avg = 1.0f; |
| 2342 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + "A/V sync reset"); |
| 2343 | } |
| 2344 | |
2315 | 2345 | void NuppelVideoPlayer::InitAVSync(void) |
2316 | 2346 | { |
2317 | 2347 | videosync->Start(); |
… |
… |
void NuppelVideoPlayer::InitAVSync(void) |
2333 | 2363 | VERBOSE(VB_GENERAL, msg); |
2334 | 2364 | msg = QString("Refresh rate: %1, frame interval: %2") |
2335 | 2365 | .arg(refreshrate).arg(frame_interval); |
2336 | | VERBOSE(VB_PLAYBACK, msg); |
| 2366 | VERBOSE(VB_PLAYBACK, LOC + msg); |
| 2367 | |
| 2368 | SetFrameInterval(m_scan, 1.0 / (video_frame_rate * play_speed)); |
2337 | 2369 | |
2338 | 2370 | // try to get preferential scheduling, but ignore if we fail to. |
2339 | 2371 | myth_nice(-19); |
2340 | 2372 | } |
2341 | 2373 | } |
2342 | 2374 | |
| 2375 | int64_t NuppelVideoPlayer::AVSyncGetAudiotime(void) |
| 2376 | { |
| 2377 | int64_t currentaudiotime = 0; |
| 2378 | audio_lock.lock(); |
| 2379 | if (audioOutput && normal_speed) |
| 2380 | { |
| 2381 | currentaudiotime = audioOutput->GetAudiotime(); |
| 2382 | } |
| 2383 | audio_lock.unlock(); |
| 2384 | return currentaudiotime; |
| 2385 | } |
| 2386 | |
2343 | 2387 | void NuppelVideoPlayer::AVSync(void) |
2344 | 2388 | { |
2345 | 2389 | float diverge = 0.0f; |
| 2390 | int vsync_delay_clock = 0; |
| 2391 | int64_t currentaudiotime = 0; |
| 2392 | |
2346 | 2393 | // attempt to reduce fps for standalone PIP |
2347 | 2394 | if (player_ctx->IsPIP() && framesPlayed % 2) |
2348 | 2395 | { |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2382 | 2429 | ps = kScan_Progressive; |
2383 | 2430 | |
2384 | 2431 | bool dropframe = false; |
| 2432 | QString dbg; |
| 2433 | |
| 2434 | if (avsync_predictor_enabled) |
| 2435 | { |
| 2436 | avsync_predictor += frame_interval; |
| 2437 | if (avsync_predictor >= refreshrate) |
| 2438 | { |
| 2439 | int refreshperiodsinframe = avsync_predictor/refreshrate; |
| 2440 | avsync_predictor -= refreshrate * refreshperiodsinframe; |
| 2441 | } |
| 2442 | else |
| 2443 | { |
| 2444 | dropframe = true; |
| 2445 | dbg = "A/V predict drop frame, "; |
| 2446 | } |
| 2447 | } |
| 2448 | |
2385 | 2449 | if (diverge < -MAXDIVERGE) |
2386 | 2450 | { |
2387 | 2451 | dropframe = true; |
2388 | 2452 | // If video is way behind of audio, adjust for it... |
2389 | | QString dbg = QString("Video is %1 frames behind audio (too slow), ") |
| 2453 | dbg = QString("Video is %1 frames behind audio (too slow), ") |
2390 | 2454 | .arg(-diverge); |
| 2455 | } |
2391 | 2456 | |
| 2457 | if (dropframe) |
| 2458 | { |
2392 | 2459 | // Reset A/V Sync |
2393 | 2460 | lastsync = true; |
2394 | 2461 | |
| 2462 | currentaudiotime = AVSyncGetAudiotime(); |
| 2463 | |
2395 | 2464 | if (buffer && !using_null_videoout && |
2396 | 2465 | videoOutput->hasHWAcceleration() && |
2397 | 2466 | !videoOutput->IsSyncLocked()) |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2416 | 2485 | if (buffer) |
2417 | 2486 | videoOutput->PrepareFrame(buffer, ps); |
2418 | 2487 | |
2419 | | VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, QString("AVSync waitforframe %1 %2") |
| 2488 | VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, LOC + QString("AVSync waitforframe %1 %2") |
2420 | 2489 | .arg(avsync_adjustment).arg(m_double_framerate)); |
2421 | | videosync->WaitForFrame(avsync_adjustment + repeat_delay); |
2422 | | VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, "AVSync show"); |
| 2490 | vsync_delay_clock = videosync->WaitForFrame(avsync_adjustment + repeat_delay); |
| 2491 | currentaudiotime = AVSyncGetAudiotime(); |
| 2492 | VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, LOC + "AVSync show"); |
2423 | 2493 | if (!resetvideo) |
2424 | 2494 | videoOutput->Show(ps); |
2425 | 2495 | |
2426 | 2496 | if (videoOutput->IsErrored()) |
2427 | 2497 | { |
2428 | | VERBOSE(VB_IMPORTANT, "NVP: Error condition detected " |
| 2498 | VERBOSE(VB_IMPORTANT, LOC + "Error condition detected " |
2429 | 2499 | "in videoOutput after Show(), aborting playback."); |
2430 | 2500 | SetErrored(QObject::tr("Serious error detected in Video Output")); |
2431 | 2501 | return; |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2461 | 2531 | // Display the second field |
2462 | 2532 | videosync->AdvanceTrigger(); |
2463 | 2533 | #ifdef NEW_AVSYNC |
2464 | | videosync->WaitForFrame(avsync_adjustment); |
| 2534 | vsync_delay_clock = videosync->WaitForFrame(avsync_adjustment); |
2465 | 2535 | #else |
2466 | | videosync->WaitForFrame(0); |
| 2536 | vsync_delay_clock = videosync->WaitForFrame(0); |
2467 | 2537 | #endif |
2468 | 2538 | if (!resetvideo) |
2469 | 2539 | { |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2474 | 2544 | repeat_delay = frame_interval * buffer->repeat_pict * 0.5; |
2475 | 2545 | |
2476 | 2546 | if (repeat_delay) |
2477 | | VERBOSE(VB_TIMESTAMP, QString("A/V repeat_pict, adding %1 repeat " |
| 2547 | VERBOSE(VB_TIMESTAMP, LOC + QString("A/V repeat_pict, adding %1 repeat " |
2478 | 2548 | "delay").arg(repeat_delay)); |
2479 | 2549 | } |
2480 | 2550 | else |
2481 | 2551 | { |
2482 | | videosync->WaitForFrame(0); |
| 2552 | vsync_delay_clock = videosync->WaitForFrame(0); |
| 2553 | currentaudiotime = AVSyncGetAudiotime(); |
2483 | 2554 | } |
2484 | 2555 | |
2485 | 2556 | if (output_jmeter && output_jmeter->RecordCycleTime()) |
2486 | 2557 | { |
2487 | | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, QString("A/V avsync_delay: %1, " |
| 2558 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString("A/V avsync_delay: %1, " |
2488 | 2559 | "avsync_avg: %2, warpfactor: %3, warpfactor_avg: %4") |
2489 | 2560 | .arg(avsync_delay / 1000).arg(avsync_avg / 1000) |
2490 | 2561 | .arg(warpfactor).arg(warpfactor_avg)); |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2500 | 2571 | // by cutting the frame rate in half for the length of this frame |
2501 | 2572 | |
2502 | 2573 | #ifdef NEW_AVSYNC |
2503 | | avsync_adjustment = refreshrate; |
| 2574 | //avsync_adjustment = refreshrate; |
| 2575 | avsync_adjustment = frame_interval; |
| 2576 | //avsync_adjustment = frame_interval*(((int)MAXDIVERGE)-1); |
2504 | 2577 | #else |
2505 | 2578 | avsync_adjustment = frame_interval; |
2506 | 2579 | #endif |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2510 | 2583 | "\t\t\tdoubling video frame interval to slow down.").arg(diverge)); |
2511 | 2584 | } |
2512 | 2585 | |
2513 | | audio_lock.lock(); |
2514 | 2586 | if (audioOutput && normal_speed) |
2515 | 2587 | { |
2516 | | long long currentaudiotime = audioOutput->GetAudiotime(); |
2517 | | audio_lock.unlock(); |
2518 | 2588 | #if 1 |
2519 | | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, QString( |
| 2589 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString( |
2520 | 2590 | "A/V timecodes audio %1 video %2 frameinterval %3 " |
2521 | | "avdel %4 avg %5 tcoffset %6") |
| 2591 | "avdel %4 avg %5 tcoffset %6" |
| 2592 | " avp %7 avpen %8" |
| 2593 | " avdc %9" |
| 2594 | ) |
2522 | 2595 | .arg(currentaudiotime) |
2523 | 2596 | .arg(buffer->timecode) |
2524 | 2597 | .arg(frame_interval) |
2525 | | .arg(buffer->timecode - currentaudiotime) |
| 2598 | .arg(buffer->timecode - currentaudiotime - (int)(vsync_delay_clock*audio_stretchfactor+500)/1000) |
2526 | 2599 | .arg(avsync_avg) |
2527 | 2600 | .arg(tc_wrap[TC_AUDIO]) |
| 2601 | .arg(avsync_predictor) |
| 2602 | .arg(avsync_predictor_enabled) |
| 2603 | .arg(vsync_delay_clock) |
2528 | 2604 | ); |
2529 | 2605 | #endif |
2530 | 2606 | if (currentaudiotime != 0 && buffer->timecode != 0) |
2531 | 2607 | { // currentaudiotime == 0 after a seek |
2532 | 2608 | // The time at the start of this frame (ie, now) is given by |
2533 | 2609 | // last->timecode |
2534 | | int delta = (int)((buffer->timecode - prevtc)/play_speed) - (frame_interval / 1000); |
2535 | | prevtc = buffer->timecode; |
2536 | | //cerr << delta << " "; |
2537 | | |
2538 | | // If the timecode is off by a frame (dropped frame) wait to sync |
2539 | | if (delta > (int) frame_interval / 1200 && |
2540 | | delta < (int) frame_interval / 1000 * 3 && |
2541 | | prevrp == 0) |
| 2610 | if (prevtc != 0) |
2542 | 2611 | { |
2543 | | //cerr << "+ "; |
2544 | | videosync->AdvanceTrigger(); |
2545 | | if (m_double_framerate) |
| 2612 | int delta = (int)((buffer->timecode - prevtc)/play_speed) - (frame_interval / 1000); |
| 2613 | // If the timecode is off by a frame (dropped frame) wait to sync |
| 2614 | if (delta > (int) frame_interval / 1200 && |
| 2615 | delta < (int) frame_interval / 1000 * 3 && |
| 2616 | prevrp == 0) |
| 2617 | { |
| 2618 | //cerr << "+ "; |
| 2619 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString("A/V delay %1").arg(delta)); |
2546 | 2620 | videosync->AdvanceTrigger(); |
| 2621 | if (m_double_framerate) |
| 2622 | videosync->AdvanceTrigger(); |
| 2623 | } |
2547 | 2624 | } |
| 2625 | prevtc = buffer->timecode; |
2548 | 2626 | prevrp = buffer->repeat_pict; |
2549 | 2627 | |
2550 | | avsync_delay = (buffer->timecode - currentaudiotime) * 1000;//usec |
| 2628 | avsync_delay = (buffer->timecode - currentaudiotime) * 1000 - (int)(vsync_delay_clock*audio_stretchfactor); //usec |
2551 | 2629 | // prevents major jitter when pts resets during dvd title |
2552 | 2630 | if (avsync_delay > 2000000 && player_ctx->buffer->isDVD()) |
2553 | 2631 | avsync_delay = 90000; |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2557 | 2635 | the video by one interlaced field (1/2 frame) */ |
2558 | 2636 | if (!lastsync) |
2559 | 2637 | { |
2560 | | if (avsync_avg > frame_interval * 3 / 2) |
| 2638 | if (avsync_delay > refreshrate) |
2561 | 2639 | { |
2562 | | avsync_adjustment = refreshrate; |
2563 | | lastsync = true; |
| 2640 | avsync_adjustment += refreshrate; |
| 2641 | //lastsync = true; |
| 2642 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + "A/V avg high extend"); |
2564 | 2643 | } |
2565 | | else if (avsync_avg < 0 - frame_interval * 3 / 2) |
| 2644 | else if (avsync_delay < 0 - refreshrate) |
2566 | 2645 | { |
2567 | | avsync_adjustment = -refreshrate; |
2568 | | lastsync = true; |
| 2646 | avsync_adjustment -= refreshrate; |
| 2647 | //lastsync = true; |
| 2648 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + "A/V avg high skip"); |
2569 | 2649 | } |
2570 | 2650 | } |
2571 | 2651 | else |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2573 | 2653 | } |
2574 | 2654 | else |
2575 | 2655 | { |
2576 | | avsync_avg = 0; |
2577 | | avsync_oldavg = 0; |
| 2656 | ResetAVSync(); |
2578 | 2657 | } |
2579 | 2658 | } |
2580 | 2659 | else |
2581 | | audio_lock.unlock(); |
| 2660 | { |
| 2661 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString("A/V no sync proc ns:%1 ao:%2").arg(normal_speed).arg(audioOutput != NULL)); |
| 2662 | } |
2582 | 2663 | } |
2583 | 2664 | |
2584 | 2665 | void NuppelVideoPlayer::DisplayPauseFrame(void) |
… |
… |
void NuppelVideoPlayer::DoPause(void) |
4227 | 4308 | } |
4228 | 4309 | |
4229 | 4310 | float temp_speed = audio_stretchfactor; |
4230 | | frame_interval = (int)(1000000.0 * ffrew_skip / video_frame_rate / temp_speed); |
| 4311 | SetFrameInterval(m_scan, ffrew_skip / (video_frame_rate * temp_speed)); |
4231 | 4312 | VERBOSE(VB_PLAYBACK, QString("rate: %1 speed: %2 skip: %3 = interval %4") |
4232 | 4313 | .arg(video_frame_rate).arg(temp_speed) |
4233 | 4314 | .arg(ffrew_skip).arg(frame_interval)); |
… |
… |
void NuppelVideoPlayer::DoPlay(void) |
4289 | 4370 | ClearAfterSeek(); |
4290 | 4371 | } |
4291 | 4372 | |
4292 | | frame_interval = (int) (1000000.0f * ffrew_skip / video_frame_rate / |
4293 | | play_speed); |
| 4373 | SetFrameInterval(m_scan, ffrew_skip / (video_frame_rate * play_speed)); |
4294 | 4374 | |
4295 | 4375 | VERBOSE(VB_PLAYBACK, LOC + "DoPlay: " + |
4296 | 4376 | QString("rate: %1 speed: %2 skip: %3 => new interval %4") |
… |
… |
void NuppelVideoPlayer::ClearAfterSeek(bool clearvideobuffers) |
4688 | 4768 | savedAudioTimecodeOffset = 0; |
4689 | 4769 | } |
4690 | 4770 | |
| 4771 | ResetAVSync(); |
4691 | 4772 | SetPrebuffering(true); |
4692 | 4773 | audio_lock.lock(); |
4693 | 4774 | if (audioOutput) |
diff --git a/mythtv/libs/libmythtv/NuppelVideoPlayer.h b/mythtv/libs/libmythtv/NuppelVideoPlayer.h
index d19ff73..dcd90f6 100644
a
|
b
|
class MPUBLIC NuppelVideoPlayer : public CC608Reader, public CC708Reader |
519 | 519 | float WarpFactor(void); |
520 | 520 | void WrapTimecode(long long &timecode, TCTypes tc_type); |
521 | 521 | void InitAVSync(void); |
| 522 | void ResetAVSync(void); |
| 523 | int64_t AVSyncGetAudiotime(void); |
| 524 | void SetFrameInterval(FrameScanType scan, double speed); |
522 | 525 | void AVSync(void); |
523 | 526 | void FallbackDeint(void); |
524 | 527 | void CheckExtraAudioDecode(void); |
… |
… |
class MPUBLIC NuppelVideoPlayer : public CC608Reader, public CC708Reader |
805 | 808 | int avsync_adjustment; |
806 | 809 | int avsync_avg; |
807 | 810 | int avsync_oldavg; |
| 811 | bool usesmoothsync; |
| 812 | int avsync_predictor; |
| 813 | bool avsync_predictor_enabled; |
808 | 814 | int refreshrate; |
809 | 815 | bool lastsync; |
810 | 816 | bool m_playing_slower; |
diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp
index e6af2d5..0f39821 100644
a
|
b
|
AvFormatDecoder::AvFormatDecoder(NuppelVideoPlayer *parent, |
481 | 481 | start_code_state(0xffffffff), |
482 | 482 | lastvpts(0), lastapts(0), |
483 | 483 | lastccptsu(0), |
| 484 | firstvpts(0), firstvptsinuse(false), |
484 | 485 | using_null_videoout(use_null_videoout), |
485 | 486 | video_codec_id(kCodec_NONE), |
486 | 487 | no_hardware_decoders(no_hardware_decode), |
… |
… |
void AvFormatDecoder::SeekReset(long long newKey, uint skipFrames, |
929 | 930 | if (decoded_video_frame) |
930 | 931 | GetNVP()->DiscardVideoFrame(decoded_video_frame); |
931 | 932 | } |
| 933 | |
| 934 | if (doflush) |
| 935 | { |
| 936 | firstvpts = 0; |
| 937 | firstvptsinuse = true; |
| 938 | } |
932 | 939 | } |
933 | 940 | |
934 | 941 | void AvFormatDecoder::Reset(bool reset_video_data, bool seek_reset) |
… |
… |
void AvFormatDecoder::MpegPreProcessPkt(AVStream *stream, AVPacket *pkt) |
2929 | 2936 | |
2930 | 2937 | gopset = false; |
2931 | 2938 | prevgoppos = 0; |
| 2939 | firstvpts = |
2932 | 2940 | lastapts = lastvpts = lastccptsu = 0; |
| 2941 | firstvptsinuse = true; |
2933 | 2942 | |
2934 | 2943 | // fps debugging info |
2935 | 2944 | float avFPS = normalized_fps(stream, context); |
… |
… |
bool AvFormatDecoder::H264PreProcessPkt(AVStream *stream, AVPacket *pkt) |
3039 | 3048 | |
3040 | 3049 | gopset = false; |
3041 | 3050 | prevgoppos = 0; |
| 3051 | firstvpts = |
3042 | 3052 | lastapts = lastvpts = lastccptsu = 0; |
| 3053 | firstvptsinuse = true; |
3043 | 3054 | |
3044 | 3055 | // fps debugging info |
3045 | 3056 | float avFPS = normalized_fps(stream, context); |
… |
… |
bool AvFormatDecoder::ProcessVideoPacket(AVStream *curstream, AVPacket *pkt) |
3261 | 3272 | framesPlayed++; |
3262 | 3273 | |
3263 | 3274 | lastvpts = temppts; |
| 3275 | if (!firstvpts && firstvptsinuse) |
| 3276 | firstvpts = temppts; |
3264 | 3277 | |
3265 | 3278 | return true; |
3266 | 3279 | } |
… |
… |
bool AvFormatDecoder::ProcessAudioPacket(AVStream *curstream, AVPacket *pkt, |
4029 | 4042 | skipaudio = false; |
4030 | 4043 | } |
4031 | 4044 | |
| 4045 | // skip any audio frames preceding first video frame |
| 4046 | if (firstvptsinuse && firstvpts && (lastapts < firstvpts)) |
| 4047 | { |
| 4048 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, |
| 4049 | LOC + QString("discarding early audio timecode %1 %2 %3") |
| 4050 | .arg(pkt->pts).arg(pkt->dts).arg(lastapts)); |
| 4051 | break; |
| 4052 | } |
| 4053 | firstvptsinuse = false; |
| 4054 | |
4032 | 4055 | avcodeclock->lock(); |
4033 | 4056 | data_size = 0; |
4034 | 4057 | |
diff --git a/mythtv/libs/libmythtv/avformatdecoder.h b/mythtv/libs/libmythtv/avformatdecoder.h
index 90b8f58..f48bc18 100644
a
|
b
|
class AvFormatDecoder : public DecoderBase |
262 | 262 | long long lastvpts; |
263 | 263 | long long lastapts; |
264 | 264 | long long lastccptsu; |
| 265 | long long firstvpts; |
| 266 | bool firstvptsinuse; |
265 | 267 | |
266 | 268 | bool using_null_videoout; |
267 | 269 | MythCodecID video_codec_id; |
diff --git a/mythtv/libs/libmythtv/vsync.cpp b/mythtv/libs/libmythtv/vsync.cpp
index 060402d..4d6a2d2 100644
a
|
b
|
VideoSync::VideoSync(VideoOutput *video_output, |
123 | 123 | bool halve_frame_interval) : |
124 | 124 | m_video_output(video_output), m_frame_interval(frameint), |
125 | 125 | m_refresh_interval(refreshint), m_interlaced(halve_frame_interval), |
126 | | m_delay(-1) |
| 126 | m_nexttrigger(0), |
| 127 | m_delay(-1), |
| 128 | m_synchronous(false) |
127 | 129 | { |
128 | | bzero(&m_nexttrigger, sizeof(m_nexttrigger)); |
129 | 130 | |
130 | 131 | int tolerance = m_refresh_interval / 200; |
131 | 132 | if (m_interlaced && m_refresh_interval > ((m_frame_interval/2) + tolerance)) |
… |
… |
VideoSync::VideoSync(VideoOutput *video_output, |
136 | 137 | |
137 | 138 | void VideoSync::Start(void) |
138 | 139 | { |
139 | | gettimeofday(&m_nexttrigger, NULL); // now |
| 140 | struct timeval now_tv; |
| 141 | gettimeofday(&now_tv, NULL); // now |
| 142 | m_nexttrigger = now_tv.tv_sec * 1000000LL + now_tv.tv_usec; |
140 | 143 | } |
141 | 144 | |
142 | 145 | /** \fn VideoSync::SetFrameInterval(int fr, bool intr) |
… |
… |
void VideoSync::SetFrameInterval(int fr, bool intr) |
147 | 150 | m_frame_interval = fr; |
148 | 151 | m_interlaced = intr; |
149 | 152 | int tolerance = m_refresh_interval / 200; |
| 153 | double sync_factor = fr * 2.0f / intr; |
| 154 | sync_factor = sync_factor - round(sync_factor); |
| 155 | m_synchronous = (sync_factor >= -0.005) && (sync_factor <= 0.005); |
150 | 156 | if (m_interlaced && m_refresh_interval > ((m_frame_interval/2) + tolerance)) |
151 | 157 | m_interlaced = false; // can't display both fields at 2x rate |
152 | 158 | |
153 | | VERBOSE(VB_PLAYBACK, QString("Set video sync frame interval to %1") |
154 | | .arg(m_frame_interval)); |
155 | | } |
156 | | |
157 | | void VideoSync::OffsetTimeval(struct timeval& tv, int offset) |
158 | | { |
159 | | tv.tv_usec += offset; |
160 | | while (tv.tv_usec > 999999) |
161 | | { |
162 | | tv.tv_sec++; |
163 | | tv.tv_usec -= 1000000; |
164 | | } |
165 | | while (tv.tv_usec < 0) |
166 | | { |
167 | | tv.tv_sec--; |
168 | | tv.tv_usec += 1000000; |
169 | | } |
| 159 | VERBOSE(VB_PLAYBACK, QString("Set video sync frame interval to %1 (synced:%2)") |
| 160 | .arg(m_frame_interval).arg(m_synchronous)); |
170 | 161 | } |
171 | 162 | |
172 | 163 | /** \fn VideoSync::UpdateNexttrigger() |
… |
… |
void VideoSync::UpdateNexttrigger() |
179 | 170 | // Offset by frame interval -- if interlaced, only delay by half |
180 | 171 | // frame interval |
181 | 172 | if (m_interlaced) |
182 | | OffsetTimeval(m_nexttrigger, m_frame_interval/2); |
| 173 | m_nexttrigger += m_frame_interval/2; |
183 | 174 | else |
184 | | OffsetTimeval(m_nexttrigger, m_frame_interval); |
| 175 | m_nexttrigger += m_frame_interval; |
185 | 176 | } |
186 | 177 | |
187 | 178 | /** \fn VideoSync::CalcDelay() |
… |
… |
void VideoSync::UpdateNexttrigger() |
197 | 188 | */ |
198 | 189 | int VideoSync::CalcDelay() |
199 | 190 | { |
200 | | struct timeval now; |
201 | | gettimeofday(&now, NULL); |
| 191 | struct timeval now_tv; |
| 192 | gettimeofday(&now_tv, NULL); |
202 | 193 | //cout << "CalcDelay: next: " << timeval_str(m_nexttrigger) << " now " |
203 | 194 | // << timeval_str(now) << endl; |
| 195 | int64_t now = now_tv.tv_sec * 1000000LL + now_tv.tv_usec; |
204 | 196 | |
205 | | int ret_val = (m_nexttrigger.tv_sec - now.tv_sec) * 1000000 + |
206 | | (m_nexttrigger.tv_usec - now.tv_usec); |
| 197 | int ret_val = m_nexttrigger - now; |
207 | 198 | |
208 | 199 | //cout << "delay " << ret_val << endl; |
209 | 200 | |
… |
… |
int VideoSync::CalcDelay() |
215 | 206 | ret_val = m_frame_interval * 4; |
216 | 207 | |
217 | 208 | // set nexttrigger to our new target time |
218 | | m_nexttrigger.tv_sec = now.tv_sec; |
219 | | m_nexttrigger.tv_usec = now.tv_usec; |
220 | | OffsetTimeval(m_nexttrigger, ret_val); |
| 209 | m_nexttrigger = now; |
| 210 | m_nexttrigger += ret_val; |
221 | 211 | } |
222 | 212 | |
223 | 213 | if (ret_val < -m_frame_interval) |
… |
… |
int VideoSync::CalcDelay() |
225 | 215 | ret_val = -m_frame_interval; |
226 | 216 | |
227 | 217 | // set nexttrigger to our new target time |
228 | | m_nexttrigger.tv_sec = now.tv_sec; |
229 | | m_nexttrigger.tv_usec = now.tv_usec; |
230 | | OffsetTimeval(m_nexttrigger, ret_val); |
| 218 | m_nexttrigger = now; |
| 219 | m_nexttrigger += ret_val; |
231 | 220 | } |
232 | 221 | |
233 | 222 | return ret_val; |
… |
… |
int VideoSync::CalcDelay() |
244 | 233 | void VideoSync::KeepPhase() |
245 | 234 | { |
246 | 235 | // cerr << m_delay << endl; |
247 | | if (m_delay < -(m_refresh_interval/2)) |
248 | | OffsetTimeval(m_nexttrigger, 200); |
249 | | else if (m_delay > -500) |
250 | | OffsetTimeval(m_nexttrigger, -2000); |
| 236 | if (m_synchronous) |
| 237 | { |
| 238 | if (m_delay < -(m_refresh_interval - 500)) |
| 239 | m_nexttrigger += 200; |
| 240 | else if (m_delay > -500) |
| 241 | m_nexttrigger += -2000; |
| 242 | } |
| 243 | else |
| 244 | { |
| 245 | if (m_delay < -(m_refresh_interval + 500)) |
| 246 | m_nexttrigger += 200; |
| 247 | else if (m_delay >= 0) |
| 248 | m_nexttrigger += -2000; |
| 249 | } |
251 | 250 | } |
252 | 251 | |
253 | 252 | #ifndef _WIN32 |
… |
… |
void DRMVideoSync::Start(void) |
337 | 336 | VideoSync::Start(); |
338 | 337 | } |
339 | 338 | |
340 | | void DRMVideoSync::WaitForFrame(int sync_delay) |
| 339 | int DRMVideoSync::WaitForFrame(int sync_delay) |
341 | 340 | { |
342 | 341 | // Offset for externally-provided A/V sync delay |
343 | | OffsetTimeval(m_nexttrigger, sync_delay); |
| 342 | m_nexttrigger += sync_delay; |
344 | 343 | |
345 | 344 | m_delay = CalcDelay(); |
346 | 345 | //cerr << "WaitForFrame at : " << m_delay; |
… |
… |
void DRMVideoSync::WaitForFrame(int sync_delay) |
360 | 359 | if (m_delay > 0) |
361 | 360 | { |
362 | 361 | // Wait for any remaining retrace intervals in one pass. |
363 | | int n = m_delay / m_refresh_interval + 1; |
| 362 | int n = (m_delay + m_refresh_interval - 1) / m_refresh_interval; |
364 | 363 | |
365 | 364 | drm_wait_vblank_t blank; |
366 | 365 | blank.request.type = DRM_VBLANK_RELATIVE; |
… |
… |
void DRMVideoSync::WaitForFrame(int sync_delay) |
370 | 369 | //cerr << "Wait " << n << " intervals. Count " << blank.request.sequence; |
371 | 370 | //cerr << " Delay " << m_delay << endl; |
372 | 371 | } |
| 372 | return m_delay; |
373 | 373 | } |
374 | 374 | |
375 | 375 | void DRMVideoSync::AdvanceTrigger(void) |
… |
… |
void OpenGLVideoSync::Start(void) |
497 | 497 | #endif /* USING_OPENGL_VSYNC */ |
498 | 498 | } |
499 | 499 | |
500 | | void OpenGLVideoSync::WaitForFrame(int sync_delay) |
| 500 | int OpenGLVideoSync::WaitForFrame(int sync_delay) |
501 | 501 | { |
502 | 502 | (void) sync_delay; |
503 | 503 | #ifdef USING_OPENGL_VSYNC |
| 504 | //#define GLVSYNCDEBUG |
| 505 | #ifdef GLVSYNCDEBUG |
| 506 | int refreshcount = 0; |
| 507 | #endif |
504 | 508 | const QString msg1("First A/V Sync"), msg2("Second A/V Sync"); |
505 | | OffsetTimeval(m_nexttrigger, sync_delay); |
| 509 | m_nexttrigger += sync_delay; |
506 | 510 | |
507 | 511 | VideoOutput *vo = dynamic_cast<VideoOutput*>(m_video_output); |
508 | 512 | if (vo && vo->IsEmbedding()) |
… |
… |
void OpenGLVideoSync::WaitForFrame(int sync_delay) |
510 | 514 | m_delay = CalcDelay(); |
511 | 515 | if (m_delay > 0) |
512 | 516 | usleep(m_delay); |
513 | | return; |
| 517 | return 0; |
514 | 518 | } |
515 | 519 | |
516 | 520 | int err; |
517 | 521 | if (!m_context) |
518 | | return; |
| 522 | return 0; |
519 | 523 | unsigned int frameNum = 0; |
520 | 524 | |
521 | 525 | OpenGLContextLocker ctx_lock(m_context); |
522 | 526 | err = gMythGLXGetVideoSyncSGI(&frameNum); |
523 | 527 | checkGLSyncError("Frame Number Query", err); |
524 | 528 | |
| 529 | #ifdef GLVSYNCDEBUG |
| 530 | int delay1 = m_delay; |
| 531 | int delay2; |
| 532 | #endif |
525 | 533 | // Always sync to the next retrace execpt when we are very late. |
526 | 534 | if ((m_delay = CalcDelay()) > -(m_refresh_interval/2)) |
527 | 535 | { |
| 536 | #ifdef GLVSYNCDEBUG |
| 537 | delay2 = m_delay; |
| 538 | #endif |
528 | 539 | err = gMythGLXWaitVideoSyncSGI(2, (frameNum+1)%2 ,&frameNum); |
529 | 540 | checkGLSyncError(msg1, err); |
530 | 541 | m_delay = CalcDelay(); |
| 542 | #ifdef GLVSYNCDEBUG |
| 543 | refreshcount++; |
| 544 | #endif |
531 | 545 | } |
| 546 | #ifdef GLVSYNCDEBUG |
| 547 | else |
| 548 | delay2 = m_delay; |
| 549 | #endif |
532 | 550 | |
| 551 | #ifdef GLVSYNCDEBUG |
| 552 | int delay3 = m_delay; |
| 553 | #endif |
533 | 554 | // Wait for any remaining retrace intervals in one pass. |
534 | 555 | if (m_delay > 0) |
535 | 556 | { |
536 | | uint n = m_delay / m_refresh_interval + 1; |
| 557 | uint n = (m_delay + m_refresh_interval - 1) / m_refresh_interval; |
| 558 | #ifdef GLVSYNCDEBUG |
| 559 | refreshcount += (int)n; |
| 560 | #endif |
537 | 561 | err = gMythGLXWaitVideoSyncSGI((n+1), (frameNum+n)%(n+1), &frameNum); |
538 | 562 | checkGLSyncError(msg2, err); |
539 | 563 | m_delay = CalcDelay(); |
540 | 564 | } |
| 565 | #ifdef GLVSYNCDEBUG |
| 566 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, QString("VS: WFF: ri:%1 fi:%2 delay1:%3 delay2:%4 delay3:%5 skip:%6 finaldelay:%7") |
| 567 | .arg(m_refresh_interval) |
| 568 | .arg(m_frame_interval) |
| 569 | .arg(delay1) |
| 570 | .arg(delay2) |
| 571 | .arg(delay3) |
| 572 | .arg(refreshcount) |
| 573 | .arg(m_delay) |
| 574 | ); |
| 575 | #endif |
541 | 576 | |
542 | 577 | #endif /* USING_OPENGL_VSYNC */ |
| 578 | return m_delay; |
543 | 579 | } |
544 | 580 | |
545 | 581 | void OpenGLVideoSync::AdvanceTrigger(void) |
… |
… |
void OpenGLVideoSync::AdvanceTrigger(void) |
548 | 584 | |
549 | 585 | KeepPhase(); |
550 | 586 | UpdateNexttrigger(); |
| 587 | #ifdef GLVSYNCDEBUG |
| 588 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, "VS: AdvanceTrigger"); |
| 589 | #endif |
551 | 590 | #endif /* USING_OPENGL_VSYNC */ |
552 | 591 | } |
553 | 592 | #endif /* !_WIN32 */ |
… |
… |
bool RTCVideoSync::TryInit(void) |
594 | 633 | return true; |
595 | 634 | } |
596 | 635 | |
597 | | void RTCVideoSync::WaitForFrame(int sync_delay) |
| 636 | int RTCVideoSync::WaitForFrame(int sync_delay) |
598 | 637 | { |
599 | | OffsetTimeval(m_nexttrigger, sync_delay); |
| 638 | m_nexttrigger += sync_delay; |
600 | 639 | |
601 | 640 | m_delay = CalcDelay(); |
602 | 641 | |
… |
… |
void RTCVideoSync::WaitForFrame(int sync_delay) |
609 | 648 | if ((val < 0) && (m_delay > 0)) |
610 | 649 | usleep(m_delay); |
611 | 650 | } |
| 651 | return 0; |
612 | 652 | } |
613 | 653 | |
614 | 654 | void RTCVideoSync::AdvanceTrigger(void) |
… |
… |
bool VDPAUVideoSync::TryInit(void) |
637 | 677 | return true; |
638 | 678 | } |
639 | 679 | |
640 | | void VDPAUVideoSync::WaitForFrame(int sync_delay) |
| 680 | int VDPAUVideoSync::WaitForFrame(int sync_delay) |
641 | 681 | { |
642 | 682 | // Offset for externally-provided A/V sync delay |
643 | | OffsetTimeval(m_nexttrigger, sync_delay); |
| 683 | m_nexttrigger += sync_delay; |
644 | 684 | m_delay = CalcDelay(); |
645 | 685 | |
646 | 686 | if (m_delay < 0) |
… |
… |
void VDPAUVideoSync::WaitForFrame(int sync_delay) |
648 | 688 | |
649 | 689 | VideoOutputVDPAU *vo = (VideoOutputVDPAU *)(m_video_output); |
650 | 690 | vo->SetNextFrameDisplayTimeOffset(m_delay); |
| 691 | return 0; |
651 | 692 | } |
652 | 693 | |
653 | 694 | void VDPAUVideoSync::AdvanceTrigger(void) |
… |
… |
bool BusyWaitVideoSync::TryInit(void) |
674 | 715 | return true; |
675 | 716 | } |
676 | 717 | |
677 | | void BusyWaitVideoSync::WaitForFrame(int sync_delay) |
| 718 | int BusyWaitVideoSync::WaitForFrame(int sync_delay) |
678 | 719 | { |
679 | 720 | // Offset for externally-provided A/V sync delay |
680 | | OffsetTimeval(m_nexttrigger, sync_delay); |
| 721 | m_nexttrigger += sync_delay; |
681 | 722 | |
682 | 723 | m_delay = CalcDelay(); |
683 | 724 | |
… |
… |
void BusyWaitVideoSync::WaitForFrame(int sync_delay) |
703 | 744 | if (cnt > 1) |
704 | 745 | m_cheat -= 200; |
705 | 746 | } |
| 747 | return 0; |
706 | 748 | } |
707 | 749 | |
708 | 750 | void BusyWaitVideoSync::AdvanceTrigger(void) |
… |
… |
bool USleepVideoSync::TryInit(void) |
725 | 767 | return true; |
726 | 768 | } |
727 | 769 | |
728 | | void USleepVideoSync::WaitForFrame(int sync_delay) |
| 770 | int USleepVideoSync::WaitForFrame(int sync_delay) |
729 | 771 | { |
730 | 772 | // Offset for externally-provided A/V sync delay |
731 | | OffsetTimeval(m_nexttrigger, sync_delay); |
| 773 | m_nexttrigger += sync_delay; |
732 | 774 | |
733 | 775 | m_delay = CalcDelay(); |
734 | 776 | if (m_delay > 0) |
735 | 777 | usleep(m_delay); |
| 778 | return 0; |
736 | 779 | } |
737 | 780 | |
738 | 781 | void USleepVideoSync::AdvanceTrigger(void) |
diff --git a/mythtv/libs/libmythtv/vsync.h b/mythtv/libs/libmythtv/vsync.h
index f077949..f8b1c4b 100644
a
|
b
|
class VideoSync |
70 | 70 | virtual void Start(void); |
71 | 71 | |
72 | 72 | /** \brief Waits for next a frame or field. |
| 73 | * Returns delay to real frame timing in usec |
73 | 74 | * |
74 | 75 | * Start(void), WaitForFrame(void), and Stop(void) should |
75 | 76 | * always be called from same thread, to prevent bad |
… |
… |
class VideoSync |
78 | 79 | * \param sync_delay time until the desired frame or field |
79 | 80 | * \sa CalcDelay(void), KeepPhase(void) |
80 | 81 | */ |
81 | | virtual void WaitForFrame(int sync_delay) = 0; |
| 82 | virtual int WaitForFrame(int sync_delay) = 0; |
82 | 83 | |
83 | 84 | /// \brief Use the next frame or field for CalcDelay(void) |
84 | 85 | /// and WaitForFrame(int). |
… |
… |
class VideoSync |
104 | 105 | uint frame_interval, uint refresh_interval, |
105 | 106 | bool interlaced); |
106 | 107 | protected: |
107 | | static void OffsetTimeval(struct timeval& tv, int offset); |
108 | 108 | void UpdateNexttrigger(void); |
109 | 109 | int CalcDelay(void); |
110 | 110 | void KeepPhase(void); |
… |
… |
class VideoSync |
113 | 113 | int m_frame_interval; // of video |
114 | 114 | int m_refresh_interval; // of display |
115 | 115 | bool m_interlaced; |
116 | | struct timeval m_nexttrigger; |
| 116 | int64_t m_nexttrigger; |
117 | 117 | int m_delay; |
| 118 | bool m_synchronous; |
118 | 119 | |
119 | 120 | static int m_forceskip; |
120 | 121 | }; |
… |
… |
class DRMVideoSync : public VideoSync |
136 | 137 | QString getName(void) const { return QString("DRM"); } |
137 | 138 | bool TryInit(void); |
138 | 139 | void Start(void); |
139 | | void WaitForFrame(int sync_delay); |
| 140 | int WaitForFrame(int sync_delay); |
140 | 141 | void AdvanceTrigger(void); |
141 | 142 | |
142 | 143 | private: |
… |
… |
class OpenGLVideoSync : public VideoSync |
178 | 179 | QString getName(void) const { return QString("SGI OpenGL"); } |
179 | 180 | bool TryInit(void); |
180 | 181 | void Start(void); |
181 | | void WaitForFrame(int sync_delay); |
| 182 | int WaitForFrame(int sync_delay); |
182 | 183 | void AdvanceTrigger(void); |
183 | 184 | |
184 | 185 | private: |
… |
… |
class RTCVideoSync : public VideoSync |
207 | 208 | |
208 | 209 | QString getName(void) const { return QString("RTC"); } |
209 | 210 | bool TryInit(void); |
210 | | void WaitForFrame(int sync_delay); |
| 211 | int WaitForFrame(int sync_delay); |
211 | 212 | void AdvanceTrigger(void); |
212 | 213 | |
213 | 214 | private: |
… |
… |
class VDPAUVideoSync : public VideoSync |
228 | 229 | |
229 | 230 | QString getName(void) const { return QString("VDPAU"); } |
230 | 231 | bool TryInit(void); |
231 | | void WaitForFrame(int sync_delay); |
| 232 | int WaitForFrame(int sync_delay); |
232 | 233 | void AdvanceTrigger(void); |
233 | 234 | |
234 | 235 | private: |
… |
… |
class BusyWaitVideoSync : public VideoSync |
256 | 257 | |
257 | 258 | QString getName(void) const { return QString("USleep with busy wait"); } |
258 | 259 | bool TryInit(void); |
259 | | void WaitForFrame(int sync_delay); |
| 260 | int WaitForFrame(int sync_delay); |
260 | 261 | void AdvanceTrigger(void); |
261 | 262 | |
262 | 263 | private: |
… |
… |
class USleepVideoSync : public VideoSync |
284 | 285 | |
285 | 286 | QString getName(void) const { return QString("USleep"); } |
286 | 287 | bool TryInit(void); |
287 | | void WaitForFrame(int sync_delay); |
| 288 | int WaitForFrame(int sync_delay); |
288 | 289 | void AdvanceTrigger(void); |
289 | 290 | }; |
290 | 291 | #endif /* VSYNC_H_INCLUDED */ |