nsresult VideoTrackEncoder::AppendVideoSegment(const VideoSegment& aSegment) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); // Append all video segments from MediaStreamGraph, including null an // non-null frames. VideoSegment::ChunkIterator iter(const_cast<VideoSegment&>(aSegment)); while (!iter.IsEnded()) { VideoChunk chunk = *iter; mTotalFrameDuration += chunk.GetDuration(); mLastFrameDuration += chunk.GetDuration(); // Send only the unique video frames for encoding. // Or if we got the same video chunks more than 1 seconds, // force to send into encoder. if ((mLastFrame != chunk.mFrame) || (mLastFrameDuration >= mTrackRate)) { RefPtr<layers::Image> image = chunk.mFrame.GetImage(); // Because we may get chunks with a null image (due to input blocking), // accumulate duration and give it to the next frame that arrives. // Canonically incorrect - the duration should go to the previous frame // - but that would require delaying until the next frame arrives. // Best would be to do like OMXEncoder and pass an effective timestamp // in with each frame. if (image) { mRawSegment.AppendFrame(image.forget(), mLastFrameDuration, chunk.mFrame.GetIntrinsicSize(), PRINCIPAL_HANDLE_NONE, chunk.mFrame.GetForceBlack()); mLastFrameDuration = 0; } } mLastFrame.TakeFrom(&chunk.mFrame); iter.Next(); } if (mRawSegment.GetDuration() > 0) { mReentrantMonitor.NotifyAll(); } return NS_OK; }
nsresult VideoTrackEncoder::AppendVideoSegment(const VideoSegment& aSegment) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); // Append all video segments from MediaStreamGraph, including null an // non-null frames. VideoSegment::ChunkIterator iter(const_cast<VideoSegment&>(aSegment)); while (!iter.IsEnded()) { VideoChunk chunk = *iter; nsRefPtr<layers::Image> image = chunk.mFrame.GetImage(); mRawSegment.AppendFrame(image.forget(), chunk.GetDuration(), chunk.mFrame.GetIntrinsicSize().ToIntSize()); iter.Next(); } if (mRawSegment.GetDuration() > 0) { mReentrantMonitor.NotifyAll(); } return NS_OK; }
nsresult OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData) { VideoSegment segment; { // Move all the samples from mRawSegment to segment. We only hold the // monitor in this block. ReentrantMonitorAutoEnter mon(mReentrantMonitor); // Wait if mEncoder is not initialized nor is being canceled. while (!mCanceled && (!mInitialized || (mRawSegment.GetDuration() == 0 && !mEndOfStream))) { mReentrantMonitor.Wait(); } if (mCanceled || mEncodingComplete) { return NS_ERROR_FAILURE; } segment.AppendFrom(&mRawSegment); } nsresult rv; // Start queuing raw frames to the input buffers of OMXCodecWrapper. VideoSegment::ChunkIterator iter(segment); while (!iter.IsEnded()) { VideoChunk chunk = *iter; // Send only the unique video frames to OMXCodecWrapper. if (mLastFrame != chunk.mFrame) { uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate; layers::Image* img = (chunk.IsNull() || chunk.mFrame.GetForceBlack()) ? nullptr : chunk.mFrame.GetImage(); rv = mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs); NS_ENSURE_SUCCESS(rv, rv); } mLastFrame.TakeFrom(&chunk.mFrame); mTotalFrameDuration += chunk.GetDuration(); iter.Next(); } // Send the EOS signal to OMXCodecWrapper. if (mEndOfStream && iter.IsEnded() && !mEosSetInEncoder) { uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate; layers::Image* img = (!mLastFrame.GetImage() || mLastFrame.GetForceBlack()) ? nullptr : mLastFrame.GetImage(); rv = mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs, OMXCodecWrapper::BUFFER_EOS); NS_ENSURE_SUCCESS(rv, rv); // Keep sending EOS signal until OMXVideoEncoder gets it. mEosSetInEncoder = true; } // Dequeue an encoded frame from the output buffers of OMXCodecWrapper. nsTArray<uint8_t> buffer; int outFlags = 0; int64_t outTimeStampUs = 0; rv = mEncoder->GetNextEncodedFrame(&buffer, &outTimeStampUs, &outFlags, GET_ENCODED_VIDEO_FRAME_TIMEOUT); NS_ENSURE_SUCCESS(rv, rv); if (!buffer.IsEmpty()) { nsRefPtr<EncodedFrame> videoData = new EncodedFrame(); if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { videoData->SetFrameType(EncodedFrame::AVC_CSD); } else { videoData->SetFrameType((outFlags & OMXCodecWrapper::BUFFER_SYNC_FRAME) ? EncodedFrame::AVC_I_FRAME : EncodedFrame::AVC_P_FRAME); } videoData->SwapInFrameData(buffer); videoData->SetTimeStamp(outTimeStampUs); aData.AppendEncodedFrame(videoData); } if (outFlags & OMXCodecWrapper::BUFFER_EOS) { mEncodingComplete = true; OMX_LOG("Done encoding video."); } return NS_OK; }