|
|
|
Lines 292-327
void nsBuiltinDecoderStateMachine::Decod
|
Link Here
|
|---|
|
| 292 |
((!audioPump && audioPlaying && audioDecoded < lowAudioThreshold) || |
292 |
((!audioPump && audioPlaying && audioDecoded < lowAudioThreshold) || |
| 293 |
(!videoPump && videoQueueSize < LOW_VIDEO_FRAMES))) |
293 |
(!videoPump && videoQueueSize < LOW_VIDEO_FRAMES))) |
| 294 |
{ |
294 |
{ |
| 295 |
skipToNextKeyframe = PR_TRUE; |
295 |
skipToNextKeyframe = PR_TRUE; |
| 296 |
LOG(PR_LOG_DEBUG, ("Skipping video decode to the next keyframe")); |
296 |
LOG(PR_LOG_DEBUG, ("Skipping video decode to the next keyframe")); |
| 297 |
} |
297 |
} |
| 298 |
|
298 |
|
| 299 |
// Video decode. |
299 |
// Video decode. |
|
|
300 |
PRUint32 parsed = 0, decoded = 0; |
| 300 |
if (videoPlaying && !videoWait) { |
301 |
if (videoPlaying && !videoWait) { |
| 301 |
// Time the video decode, so that if it's slow, we can increase our low |
302 |
// Time the video decode, so that if it's slow, we can increase our low |
| 302 |
// audio threshold to reduce the chance of an audio underrun while we're |
303 |
// audio threshold to reduce the chance of an audio underrun while we're |
| 303 |
// waiting for a video decode to complete. |
304 |
// waiting for a video decode to complete. |
| 304 |
TimeStamp start = TimeStamp::Now(); |
305 |
TimeStamp start = TimeStamp::Now(); |
| 305 |
videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, currentTime); |
306 |
videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, |
|
|
307 |
currentTime, |
| 308 |
parsed, |
| 309 |
decoded); |
| 306 |
TimeDuration decodeTime = TimeStamp::Now() - start; |
310 |
TimeDuration decodeTime = TimeStamp::Now() - start; |
| 307 |
if (!decodeCloseToDownload && |
311 |
if (!decodeCloseToDownload && |
| 308 |
THRESHOLD_FACTOR * decodeTime.ToMilliseconds() > lowAudioThreshold) |
312 |
THRESHOLD_FACTOR * decodeTime.ToMilliseconds() > lowAudioThreshold) |
| 309 |
{ |
313 |
{ |
| 310 |
lowAudioThreshold = |
314 |
lowAudioThreshold = |
| 311 |
NS_MIN(static_cast<PRInt64>(THRESHOLD_FACTOR * decodeTime.ToMilliseconds()), |
315 |
NS_MIN(static_cast<PRInt64>(THRESHOLD_FACTOR * decodeTime.ToMilliseconds()), |
| 312 |
static_cast<PRInt64>(AMPLE_AUDIO_MS)); |
316 |
static_cast<PRInt64>(AMPLE_AUDIO_MS)); |
| 313 |
ampleAudioThreshold = NS_MAX(THRESHOLD_FACTOR * lowAudioThreshold, |
317 |
ampleAudioThreshold = NS_MAX(THRESHOLD_FACTOR * lowAudioThreshold, |
| 314 |
ampleAudioThreshold); |
318 |
ampleAudioThreshold); |
| 315 |
LOG(PR_LOG_DEBUG, |
319 |
LOG(PR_LOG_DEBUG, |
| 316 |
("Slow video decode, set lowAudioThreshold=%lld ampleAudioThreshold=%lld", |
320 |
("Slow video decode, set lowAudioThreshold=%lld ampleAudioThreshold=%lld", |
| 317 |
lowAudioThreshold, ampleAudioThreshold)); |
321 |
lowAudioThreshold, ampleAudioThreshold)); |
| 318 |
} |
322 |
} |
| 319 |
} |
323 |
} |
|
|
324 |
if (parsed || decoded) { |
| 325 |
mDecoder->NotifyDecodedFrames(parsed, decoded); |
| 326 |
} |
| 320 |
{ |
327 |
{ |
| 321 |
MonitorAutoEnter mon(mDecoder->GetMonitor()); |
328 |
MonitorAutoEnter mon(mDecoder->GetMonitor()); |
| 322 |
mDecoder->GetMonitor().NotifyAll(); |
329 |
mDecoder->GetMonitor().NotifyAll(); |
| 323 |
} |
330 |
} |
| 324 |
|
331 |
|
| 325 |
// Audio decode. |
332 |
// Audio decode. |
| 326 |
if (audioPlaying && !audioWait) { |
333 |
if (audioPlaying && !audioWait) { |
| 327 |
audioPlaying = mReader->DecodeAudioData(); |
334 |
audioPlaying = mReader->DecodeAudioData(); |
|
Lines 922-938
nsresult nsBuiltinDecoderStateMachine::R
|
Link Here
|
|---|
|
| 922 |
LoadMetadata(); |
929 |
LoadMetadata(); |
| 923 |
if (mState == DECODER_STATE_SHUTDOWN) { |
930 |
if (mState == DECODER_STATE_SHUTDOWN) { |
| 924 |
continue; |
931 |
continue; |
| 925 |
} |
932 |
} |
| 926 |
|
933 |
|
| 927 |
VideoData* videoData = FindStartTime(); |
934 |
VideoData* videoData = FindStartTime(); |
| 928 |
if (videoData) { |
935 |
if (videoData) { |
| 929 |
MonitorAutoExit exitMon(mDecoder->GetMonitor()); |
936 |
MonitorAutoExit exitMon(mDecoder->GetMonitor()); |
| 930 |
RenderVideoFrame(videoData); |
937 |
RenderVideoFrame(videoData, TimeStamp::Now()); |
| 931 |
} |
938 |
} |
| 932 |
|
939 |
|
| 933 |
// Start the decode threads, so that we can pre buffer the streams. |
940 |
// Start the decode threads, so that we can pre buffer the streams. |
| 934 |
// and calculate the start time in order to determine the duration. |
941 |
// and calculate the start time in order to determine the duration. |
| 935 |
if (NS_FAILED(StartDecodeThreads())) { |
942 |
if (NS_FAILED(StartDecodeThreads())) { |
| 936 |
continue; |
943 |
continue; |
| 937 |
} |
944 |
} |
| 938 |
|
945 |
|
|
Lines 1051-1073
nsresult nsBuiltinDecoderStateMachine::R
|
Link Here
|
|---|
|
| 1051 |
} |
1058 |
} |
| 1052 |
if (NS_SUCCEEDED(res)){ |
1059 |
if (NS_SUCCEEDED(res)){ |
| 1053 |
SoundData* audio = HasAudio() ? mReader->mAudioQueue.PeekFront() : nsnull; |
1060 |
SoundData* audio = HasAudio() ? mReader->mAudioQueue.PeekFront() : nsnull; |
| 1054 |
NS_ASSERTION(!audio || (audio->mTime <= seekTime && |
1061 |
NS_ASSERTION(!audio || (audio->mTime <= seekTime && |
| 1055 |
seekTime <= audio->mTime + audio->mDuration), |
1062 |
seekTime <= audio->mTime + audio->mDuration), |
| 1056 |
"Seek target should lie inside the first audio block after seek"); |
1063 |
"Seek target should lie inside the first audio block after seek"); |
| 1057 |
PRInt64 startTime = (audio && audio->mTime < seekTime) ? audio->mTime : seekTime; |
1064 |
PRInt64 startTime = (audio && audio->mTime < seekTime) ? audio->mTime : seekTime; |
| 1058 |
mAudioStartTime = startTime; |
1065 |
mAudioStartTime = startTime; |
| 1059 |
mPlayDuration = TimeDuration::FromMilliseconds(startTime); |
1066 |
mPlayDuration = TimeDuration::FromMilliseconds(startTime - mStartTime); |
| 1060 |
if (HasVideo()) { |
1067 |
if (HasVideo()) { |
| 1061 |
nsAutoPtr<VideoData> video(mReader->mVideoQueue.PeekFront()); |
1068 |
nsAutoPtr<VideoData> video(mReader->mVideoQueue.PeekFront()); |
| 1062 |
if (video) { |
1069 |
if (video) { |
| 1063 |
NS_ASSERTION(video->mTime <= seekTime && seekTime <= video->mEndTime, |
1070 |
NS_ASSERTION(video->mTime <= seekTime && seekTime <= video->mEndTime, |
| 1064 |
"Seek target should lie inside the first frame after seek"); |
1071 |
"Seek target should lie inside the first frame after seek"); |
| 1065 |
RenderVideoFrame(video); |
1072 |
RenderVideoFrame(video, TimeStamp::Now()); |
| 1066 |
mReader->mVideoQueue.PopFront(); |
1073 |
mReader->mVideoQueue.PopFront(); |
| 1067 |
} |
1074 |
} |
| 1068 |
} |
1075 |
} |
| 1069 |
} |
1076 |
} |
| 1070 |
} |
1077 |
} |
| 1071 |
mDecoder->StartProgressUpdates(); |
1078 |
mDecoder->StartProgressUpdates(); |
| 1072 |
if (mState == DECODER_STATE_SHUTDOWN) |
1079 |
if (mState == DECODER_STATE_SHUTDOWN) |
| 1073 |
continue; |
1080 |
continue; |
|
Lines 1193-1220
nsresult nsBuiltinDecoderStateMachine::R
|
Link Here
|
|---|
|
| 1193 |
} |
1200 |
} |
| 1194 |
break; |
1201 |
break; |
| 1195 |
} |
1202 |
} |
| 1196 |
} |
1203 |
} |
| 1197 |
|
1204 |
|
| 1198 |
return NS_OK; |
1205 |
return NS_OK; |
| 1199 |
} |
1206 |
} |
| 1200 |
|
1207 |
|
| 1201 |
void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData) |
1208 |
void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData, TimeStamp aTarget) |
| 1202 |
{ |
1209 |
{ |
| 1203 |
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread."); |
1210 |
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread."); |
| 1204 |
|
1211 |
|
| 1205 |
if (aData->mDuplicate) { |
1212 |
if (aData->mDuplicate) { |
| 1206 |
return; |
1213 |
return; |
| 1207 |
} |
1214 |
} |
| 1208 |
|
1215 |
|
| 1209 |
nsRefPtr<Image> image = aData->mImage; |
1216 |
nsRefPtr<Image> image = aData->mImage; |
| 1210 |
if (image) { |
1217 |
if (image) { |
| 1211 |
const nsVideoInfo& info = mReader->GetInfo(); |
1218 |
const nsVideoInfo& info = mReader->GetInfo(); |
| 1212 |
mDecoder->SetVideoData(gfxIntSize(info.mPicture.width, info.mPicture.height), info.mPixelAspectRatio, image); |
1219 |
mDecoder->SetVideoData(gfxIntSize(info.mPicture.width, info.mPicture.height), |
|
|
1220 |
info.mPixelAspectRatio, |
| 1221 |
image, |
| 1222 |
aTarget); |
| 1213 |
} |
1223 |
} |
| 1214 |
} |
1224 |
} |
| 1215 |
|
1225 |
|
| 1216 |
PRInt64 |
1226 |
PRInt64 |
| 1217 |
nsBuiltinDecoderStateMachine::GetAudioClock() |
1227 |
nsBuiltinDecoderStateMachine::GetAudioClock() |
| 1218 |
{ |
1228 |
{ |
| 1219 |
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread."); |
1229 |
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread."); |
| 1220 |
if (!mAudioStream || !HasAudio()) |
1230 |
if (!mAudioStream || !HasAudio()) |
|
Lines 1249-1301
void nsBuiltinDecoderStateMachine::Advan
|
Link Here
|
|---|
|
| 1249 |
// the end of the audio, use the audio clock. However if we've finished |
1259 |
// the end of the audio, use the audio clock. However if we've finished |
| 1250 |
// audio, or don't have audio, use the system clock. |
1260 |
// audio, or don't have audio, use the system clock. |
| 1251 |
PRInt64 clock_time = -1; |
1261 |
PRInt64 clock_time = -1; |
| 1252 |
PRInt64 audio_time = GetAudioClock(); |
1262 |
PRInt64 audio_time = GetAudioClock(); |
| 1253 |
if (HasAudio() && !mAudioCompleted && audio_time != -1) { |
1263 |
if (HasAudio() && !mAudioCompleted && audio_time != -1) { |
| 1254 |
clock_time = audio_time; |
1264 |
clock_time = audio_time; |
| 1255 |
// Resync against the audio clock, while we're trusting the |
1265 |
// Resync against the audio clock, while we're trusting the |
| 1256 |
// audio clock. This ensures no "drift", particularly on Linux. |
1266 |
// audio clock. This ensures no "drift", particularly on Linux. |
| 1257 |
mPlayStartTime = TimeStamp::Now() - TimeDuration::FromMilliseconds(clock_time); |
1267 |
mPlayDuration = TimeDuration::FromMilliseconds(clock_time); |
|
|
1268 |
mPlayStartTime = TimeStamp::Now(); |
| 1258 |
} else { |
1269 |
} else { |
| 1259 |
// Sound is disabled on this system. Sync to the system clock. |
1270 |
// Sound is disabled on this system. Sync to the system clock. |
| 1260 |
TimeDuration t = TimeStamp::Now() - mPlayStartTime + mPlayDuration; |
1271 |
TimeDuration t = TimeStamp::Now() - mPlayStartTime + mPlayDuration; |
| 1261 |
clock_time = (PRInt64)(1000 * t.ToSeconds()); |
1272 |
clock_time = (PRInt64)(1000 * t.ToSeconds()); |
| 1262 |
// Ensure the clock can never go backwards. |
1273 |
// Ensure the clock can never go backwards. |
| 1263 |
NS_ASSERTION(mCurrentFrameTime <= clock_time, "Clock should go forwards"); |
1274 |
NS_ASSERTION(mCurrentFrameTime <= clock_time, "Clock should go forwards"); |
| 1264 |
clock_time = NS_MAX(mCurrentFrameTime, clock_time) + mStartTime; |
1275 |
clock_time = NS_MAX(mCurrentFrameTime, clock_time) + mStartTime; |
| 1265 |
} |
1276 |
} |
| 1266 |
|
1277 |
|
|
|
1278 |
// Skip video frames up to the current playback position. |
| 1267 |
NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time."); |
1279 |
NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time."); |
| 1268 |
nsAutoPtr<VideoData> videoData; |
1280 |
nsAutoPtr<VideoData> videoData; |
| 1269 |
if (mReader->mVideoQueue.GetSize() > 0) { |
1281 |
if (mReader->mVideoQueue.GetSize() > 0) { |
| 1270 |
VideoData* data = mReader->mVideoQueue.PeekFront(); |
1282 |
VideoData* data = mReader->mVideoQueue.PeekFront(); |
| 1271 |
while (clock_time >= data->mTime) { |
1283 |
while (clock_time >= data->mTime) { |
| 1272 |
mVideoFrameEndTime = data->mEndTime; |
1284 |
mVideoFrameEndTime = data->mEndTime; |
| 1273 |
videoData = data; |
1285 |
videoData = data; |
| 1274 |
mReader->mVideoQueue.PopFront(); |
1286 |
mReader->mVideoQueue.PopFront(); |
| 1275 |
mDecoder->UpdatePlaybackOffset(data->mOffset); |
1287 |
mDecoder->UpdatePlaybackOffset(data->mOffset); |
| 1276 |
if (mReader->mVideoQueue.GetSize() == 0) |
1288 |
if (mReader->mVideoQueue.GetSize() == 0) |
| 1277 |
break; |
1289 |
break; |
| 1278 |
data = mReader->mVideoQueue.PeekFront(); |
1290 |
data = mReader->mVideoQueue.PeekFront(); |
| 1279 |
} |
1291 |
} |
| 1280 |
} |
1292 |
} |
| 1281 |
|
1293 |
|
| 1282 |
PRInt64 frameDuration = AUDIO_DURATION_MS; |
1294 |
// The timestamp of the next frame to be displayed. |
|
|
1295 |
TimeStamp nextFrameTime; |
| 1296 |
|
| 1283 |
if (videoData) { |
1297 |
if (videoData) { |
| 1284 |
// Decode one frame and display it |
1298 |
// Display the next frame. |
| 1285 |
NS_ASSERTION(videoData->mTime >= mStartTime, "Should have positive frame time"); |
1299 |
NS_ASSERTION(videoData->mTime >= mStartTime, |
|
|
1300 |
"Should have positive frame time"); |
| 1301 |
|
| 1302 |
// Calculate the system-clock time at which the frame should start being displayed. |
| 1303 |
TimeStamp presTime = mPlayStartTime - mPlayDuration + |
| 1304 |
TimeDuration::FromMilliseconds(videoData->mTime - mStartTime); |
| 1305 |
|
| 1306 |
// Frame end system-clock time. |
| 1307 |
nextFrameTime = mPlayStartTime - mPlayDuration + |
| 1308 |
TimeDuration::FromMilliseconds(videoData->mEndTime - mStartTime); |
| 1309 |
|
| 1310 |
NS_ASSERTION(presTime <= TimeStamp::Now(), |
| 1311 |
"Frame must start before or at now"); |
| 1286 |
{ |
1312 |
{ |
| 1287 |
MonitorAutoExit exitMon(mDecoder->GetMonitor()); |
1313 |
MonitorAutoExit exitMon(mDecoder->GetMonitor()); |
| 1288 |
// If we have video, we want to increment the clock in steps of the frame |
1314 |
RenderVideoFrame(videoData, presTime); |
| 1289 |
// duration. |
|
|
| 1290 |
RenderVideoFrame(videoData); |
| 1291 |
} |
1315 |
} |
| 1292 |
frameDuration = videoData->mEndTime - videoData->mTime; |
1316 |
mDecoder->NotifyPresentedFrame(); |
|
|
1317 |
|
| 1293 |
videoData = nsnull; |
1318 |
videoData = nsnull; |
|
|
1319 |
} else { |
| 1320 |
// We don't have video, just assume an arbitrary "frame" duration. |
| 1321 |
nextFrameTime = |
| 1322 |
TimeStamp::Now() + TimeDuration::FromMilliseconds(AUDIO_DURATION_MS); |
| 1294 |
} |
1323 |
} |
| 1295 |
|
1324 |
|
| 1296 |
// Kick the decode thread in case it filled its buffers and put itself |
1325 |
// Kick the decode thread in case it filled its buffers and put itself |
| 1297 |
// to sleep. |
1326 |
// to sleep. |
| 1298 |
mDecoder->GetMonitor().NotifyAll(); |
1327 |
mDecoder->GetMonitor().NotifyAll(); |
| 1299 |
|
1328 |
|
| 1300 |
// Cap the current time to the larger of the audio and video end time. |
1329 |
// Cap the current time to the larger of the audio and video end time. |
| 1301 |
// This ensures that if we're running off the system clock, we don't |
1330 |
// This ensures that if we're running off the system clock, we don't |
|
Lines 1313-1330
void nsBuiltinDecoderStateMachine::Advan
|
Link Here
|
|---|
|
| 1313 |
} |
1342 |
} |
| 1314 |
|
1343 |
|
| 1315 |
// If the number of audio/video samples queued has changed, either by |
1344 |
// If the number of audio/video samples queued has changed, either by |
| 1316 |
// this function popping and playing a video sample, or by the audio |
1345 |
// this function popping and playing a video sample, or by the audio |
| 1317 |
// thread popping and playing an audio sample, we may need to update our |
1346 |
// thread popping and playing an audio sample, we may need to update our |
| 1318 |
// ready state. Post an update to do so. |
1347 |
// ready state. Post an update to do so. |
| 1319 |
UpdateReadyState(); |
1348 |
UpdateReadyState(); |
| 1320 |
|
1349 |
|
| 1321 |
NS_ASSERTION(frameDuration >= 0, "Frame duration must be positive."); |
1350 |
// Wait until it's about time to display the next frame. |
| 1322 |
Wait(frameDuration); |
1351 |
TimeStamp now = TimeStamp::Now(); |
|
|
1352 |
if (now < nextFrameTime) { |
| 1353 |
Wait((nextFrameTime - now).ToMilliseconds()); |
| 1354 |
} |
| 1355 |
|
| 1323 |
} else { |
1356 |
} else { |
| 1324 |
if (IsPlaying()) { |
1357 |
if (IsPlaying()) { |
| 1325 |
StopPlayback(AUDIO_PAUSE); |
1358 |
StopPlayback(AUDIO_PAUSE); |
| 1326 |
mDecoder->GetMonitor().NotifyAll(); |
1359 |
mDecoder->GetMonitor().NotifyAll(); |
| 1327 |
} |
1360 |
} |
| 1328 |
|
1361 |
|
| 1329 |
if (mState == DECODER_STATE_DECODING || |
1362 |
if (mState == DECODER_STATE_DECODING || |
| 1330 |
mState == DECODER_STATE_COMPLETED) { |
1363 |
mState == DECODER_STATE_COMPLETED) { |