RefPtr<MediaDecoderReader::MediaDataPromise> MediaDecoderReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) { RefPtr<MediaDataPromise> p = mBaseVideoPromise.Ensure(__func__); bool skip = aSkipToNextKeyframe; while (VideoQueue().GetSize() == 0 && !VideoQueue().IsFinished()) { if (!DecodeVideoFrame(skip, aTimeThreshold)) { VideoQueue().Finish(); } else if (skip) { // We still need to decode more data in order to skip to the next // keyframe. Post another task to the decode task queue to decode // again. We don't just decode straight in a loop here, as that // would hog the decode task queue. RefPtr<nsIRunnable> task(new ReRequestVideoWithSkipTask(this, aTimeThreshold)); mTaskQueue->Dispatch(task.forget()); return p; } } if (VideoQueue().GetSize() > 0) { RefPtr<VideoData> v = VideoQueue().PopFront(); mBaseVideoPromise.Resolve(v, __func__); } else if (VideoQueue().IsFinished()) { mBaseVideoPromise.Reject(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); } else { MOZ_ASSERT(false, "Dropping this promise on the floor"); } return p; }
void MediaDecoderReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) { bool skip = aSkipToNextKeyframe; while (VideoQueue().GetSize() == 0 && !VideoQueue().IsFinished()) { if (!DecodeVideoFrame(skip, aTimeThreshold)) { VideoQueue().Finish(); } else if (skip) { // We still need to decode more data in order to skip to the next // keyframe. Post another task to the decode task queue to decode // again. We don't just decode straight in a loop here, as that // would hog the decode task queue. RefPtr<nsIRunnable> task(new RequestVideoWithSkipTask(this, aTimeThreshold)); mTaskQueue->Dispatch(task); return; } } if (VideoQueue().GetSize() > 0) { nsRefPtr<VideoData> v = VideoQueue().PopFront(); if (v && mVideoDiscontinuity) { v->mDiscontinuity = true; mVideoDiscontinuity = false; } GetCallback()->OnVideoDecoded(v); } else if (VideoQueue().IsFinished()) { GetCallback()->OnNotDecoded(MediaData::VIDEO_DATA, END_OF_STREAM); } }
bool BIKPlayer::next_frame() { if (timer_last_sec) { timer_wait(); } if(frameCount>=header.framecount) { return false; } binkframe frame = frames[frameCount++]; str->Seek(frame.pos, GEM_STREAM_START); ieDword audframesize; str->ReadDword(&audframesize); frame.size = str->Read( inbuff, frame.size - 4 ); if (DecodeAudioFrame(inbuff, audframesize)) { //buggy frame, we stop immediately //return false; } if (DecodeVideoFrame(inbuff+audframesize, frame.size-audframesize)) { //buggy frame, we stop immediately return false; } if (!timer_last_sec) { timer_start(); } return true; }
nsresult nsRawReader::Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime) { mozilla::MonitorAutoEnter autoEnter(mMonitor); NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread."); nsMediaStream *stream = mDecoder->GetCurrentStream(); NS_ASSERTION(stream, "Decoder has no media stream"); PRUint32 frame = mCurrentFrame; if (aTime >= UINT_MAX) return NS_ERROR_FAILURE; mCurrentFrame = aTime * mFrameRate / USECS_PER_S; PRUint32 offset; if (!MulOverflow32(mCurrentFrame, mFrameSize, offset)) return NS_ERROR_FAILURE; offset += sizeof(nsRawVideoHeader); nsresult rv = stream->Seek(nsISeekableStream::NS_SEEK_SET, offset); NS_ENSURE_SUCCESS(rv, rv); mVideoQueue.Erase(); while(mVideoQueue.GetSize() == 0) { PRBool keyframeSkip = PR_FALSE; if (!DecodeVideoFrame(keyframeSkip, 0)) { mCurrentFrame = frame; return NS_ERROR_FAILURE; } { mozilla::MonitorAutoExit autoMonitorExit(mMonitor); mozilla::MonitorAutoEnter autoMonitor(mDecoder->GetMonitor()); if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) { mCurrentFrame = frame; return NS_ERROR_FAILURE; } } nsAutoPtr<VideoData> video(mVideoQueue.PeekFront()); if (video && video->mEndTime < aTime) { mVideoQueue.PopFront(); video = nsnull; } else { video.forget(); } } return NS_OK; }
nsresult nsRawReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime) { NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); MediaResource *resource = mDecoder->GetResource(); NS_ASSERTION(resource, "Decoder has no media resource"); uint32_t frame = mCurrentFrame; if (aTime >= UINT_MAX) return NS_ERROR_FAILURE; mCurrentFrame = aTime * mFrameRate / USECS_PER_S; CheckedUint32 offset = CheckedUint32(mCurrentFrame) * mFrameSize; offset += sizeof(nsRawVideoHeader); NS_ENSURE_TRUE(offset.isValid(), NS_ERROR_FAILURE); nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, offset.value()); NS_ENSURE_SUCCESS(rv, rv); mVideoQueue.Erase(); while(mVideoQueue.GetSize() == 0) { bool keyframeSkip = false; if (!DecodeVideoFrame(keyframeSkip, 0)) { mCurrentFrame = frame; return NS_ERROR_FAILURE; } { mozilla::ReentrantMonitorAutoEnter autoMonitor(mDecoder->GetReentrantMonitor()); if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) { mCurrentFrame = frame; return NS_ERROR_FAILURE; } } nsAutoPtr<VideoData> video(mVideoQueue.PeekFront()); if (video && video->mEndTime < aTime) { mVideoQueue.PopFront(); video = nullptr; } else { video.forget(); } } return NS_OK; }
VideoData* MediaDecoderReader::DecodeToFirstVideoData() { bool eof = false; while (!eof && VideoQueue().GetSize() == 0) { { ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor()); if (mDecoder->IsShutdown()) { return nullptr; } } bool keyframeSkip = false; eof = !DecodeVideoFrame(keyframeSkip, 0); } VideoData* d = nullptr; return (d = VideoQueue().PeekFront()) ? d : nullptr; }
nsresult MediaDecoderReader::DecodeToTarget(int64_t aTarget) { DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoderReader::DecodeToTarget(%lld) Begin", aTarget)); // Decode forward to the target frame. Start with video, if we have it. if (HasVideo()) { bool eof = false; int64_t startTime = -1; nsAutoPtr<VideoData> video; while (HasVideo() && !eof) { while (VideoQueue().GetSize() == 0 && !eof) { bool skip = false; eof = !DecodeVideoFrame(skip, 0); { ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor()); if (mDecoder->IsShutdown()) { return NS_ERROR_FAILURE; } } } if (VideoQueue().GetSize() == 0) { // Hit end of file, we want to display the last frame of the video. if (video) { VideoQueue().PushFront(video.forget()); } break; } video = VideoQueue().PeekFront(); // If the frame end time is less than the seek target, we won't want // to display this frame after the seek, so discard it. if (video && video->GetEndTime() <= aTarget) { if (startTime == -1) { startTime = video->mTime; } VideoQueue().PopFront(); } else { video.forget(); break; } } { ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor()); if (mDecoder->IsShutdown()) { return NS_ERROR_FAILURE; } } DECODER_LOG(PR_LOG_DEBUG, ("First video frame after decode is %lld", startTime)); } if (HasAudio()) { // Decode audio forward to the seek target. bool eof = false; while (HasAudio() && !eof) { while (!eof && AudioQueue().GetSize() == 0) { eof = !DecodeAudioData(); { ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor()); if (mDecoder->IsShutdown()) { return NS_ERROR_FAILURE; } } } const AudioData* audio = AudioQueue().PeekFront(); if (!audio) break; CheckedInt64 startFrame = UsecsToFrames(audio->mTime, mInfo.mAudio.mRate); CheckedInt64 targetFrame = UsecsToFrames(aTarget, mInfo.mAudio.mRate); if (!startFrame.isValid() || !targetFrame.isValid()) { return NS_ERROR_FAILURE; } if (startFrame.value() + audio->mFrames <= targetFrame.value()) { // Our seek target lies after the frames in this AudioData. Pop it // off the queue, and keep decoding forwards. delete AudioQueue().PopFront(); audio = nullptr; continue; } if (startFrame.value() > targetFrame.value()) { // The seek target doesn't lie in the audio block just after the last // audio frames we've seen which were before the seek target. This // could have been the first audio data we've seen after seek, i.e. the // seek terminated after the seek target in the audio stream. Just // abort the audio decode-to-target, the state machine will play // silence to cover the gap. Typically this happens in poorly muxed // files. NS_WARNING("Audio not synced after seek, maybe a poorly muxed file?"); break; } // The seek target lies somewhere in this AudioData's frames, strip off // any frames which lie before the seek target, so we'll begin playback // exactly at the seek target. NS_ASSERTION(targetFrame.value() >= startFrame.value(), "Target must at or be after data start."); NS_ASSERTION(targetFrame.value() < startFrame.value() + audio->mFrames, "Data must end after target."); int64_t framesToPrune = targetFrame.value() - startFrame.value(); if (framesToPrune > audio->mFrames) { // We've messed up somehow. Don't try to trim frames, the |frames| // variable below will overflow. NS_WARNING("Can't prune more frames that we have!"); break; } uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune); uint32_t channels = audio->mChannels; nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[frames * channels]); memcpy(audioData.get(), audio->mAudioData.get() + (framesToPrune * channels), frames * channels * sizeof(AudioDataValue)); CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate); if (!duration.isValid()) { return NS_ERROR_FAILURE; } nsAutoPtr<AudioData> data(new AudioData(audio->mOffset, aTarget, duration.value(), frames, audioData.forget(), channels)); delete AudioQueue().PopFront(); AudioQueue().PushFront(data.forget()); break; } } DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoderReader::DecodeToTarget(%lld) End", aTarget)); return NS_OK; }