diff --git a/content/media/encoder/Makefile.in b/content/media/encoder/Makefile.in new file mode 100644 index 00000000000..3f27e6e851f --- /dev/null +++ b/content/media/encoder/Makefile.in @@ -0,0 +1,13 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +include $(topsrcdir)/config/rules.mk + +# These includes are from Android JB, for use of MediaCodec. +INCLUDES += \ + -I$(topsrcdir)/ipc/chromium/src \ + -I$(ANDROID_SOURCE)/frameworks/native/opengl/include/ \ + -I$(ANDROID_SOURCE)/frameworks/native/include/ \ + -I$(ANDROID_SOURCE)/frameworks/av/include/media/ \ + $(NULL) diff --git a/content/media/encoder/OmxTrackEncoder.cpp b/content/media/encoder/OmxTrackEncoder.cpp new file mode 100644 index 00000000000..94de42c4649 --- /dev/null +++ b/content/media/encoder/OmxTrackEncoder.cpp @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "OmxTrackEncoder.h" +#include "OMXCodecWrapper.h" +#include "VideoUtils.h" +#include "ISOTrackMetadata.h" + +#ifdef MOZ_WIDGET_GONK +#include +#define OMX_LOG(args...) \ + do { \ + __android_log_print(ANDROID_LOG_INFO, "OmxTrackEncoder", ##args); \ + } while (0) +#else +#define OMX_LOG(args, ...) +#endif + +using namespace android; + +namespace mozilla { + +#define ENCODER_CONFIG_FRAME_RATE 30 // fps +#define GET_ENCODED_VIDEO_FRAME_TIMEOUT 100000 // microseconds + +nsresult +OmxVideoTrackEncoder::Init(int aWidth, int aHeight, TrackRate aTrackRate) +{ + mFrameWidth = aWidth; + mFrameHeight = aHeight; + mTrackRate = aTrackRate; + + mEncoder = OMXCodecWrapper::CreateAVCEncoder(); + NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE); + + nsresult rv = mEncoder->Configure(mFrameWidth, mFrameHeight, + ENCODER_CONFIG_FRAME_RATE); + + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + mInitialized = (rv == NS_OK); + + mReentrantMonitor.NotifyAll(); + + return rv; +} + +already_AddRefed +OmxVideoTrackEncoder::GetMetadata() +{ + { + // Wait if mEncoder is not initialized nor is being canceled. + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + while (!mCanceled && !mInitialized) { + mReentrantMonitor.Wait(); + } + } + + if (mCanceled || mEncodingComplete) { + return nullptr; + } + + nsRefPtr meta = new AVCTrackMetadata(); + meta->Width = mFrameWidth; + meta->Height = mFrameHeight; + meta->FrameRate = ENCODER_CONFIG_FRAME_RATE; + meta->VideoFrequency = 90000; // Hz + return meta.forget(); +} + +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); + } + + // 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(); + mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs); + } + + mLastFrame.TakeFrom(&chunk.mFrame); + mTotalFrameDuration += chunk.GetDuration(); + + iter.Next(); + } + + // Send the EOS signal to OMXCodecWrapper. + if (mEndOfStream && iter.IsEnded() && !mEosSetInEncoder) { + mEosSetInEncoder = true; + uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate; + layers::Image* img = (!mLastFrame.GetImage() || mLastFrame.GetForceBlack()) + ? nullptr : mLastFrame.GetImage(); + mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs, + OMXCodecWrapper::BUFFER_EOS); + } + + // Dequeue an encoded frame from the output buffers of OMXCodecWrapper. + nsTArray buffer; + int outFlags = 0; + int64_t outTimeStampUs = 0; + mEncoder->GetNextEncodedFrame(&buffer, &outTimeStampUs, &outFlags, + GET_ENCODED_VIDEO_FRAME_TIMEOUT); + if (!buffer.IsEmpty()) { + nsRefPtr videoData = new EncodedFrame(); + if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { + videoData->SetFrameType(EncodedFrame::AVC_CSD); + } else { + videoData->SetFrameType((outFlags & OMXCodecWrapper::BUFFER_SYNC_FRAME) ? + EncodedFrame::I_FRAME : EncodedFrame::P_FRAME); + } + videoData->SetFrameData(&buffer); + videoData->SetTimeStamp(outTimeStampUs); + aData.AppendEncodedFrame(videoData); + } + + if (outFlags & OMXCodecWrapper::BUFFER_EOS) { + mEncodingComplete = true; + OMX_LOG("Done encoding video."); + } + + return NS_OK; +} + +} diff --git a/content/media/encoder/OmxTrackEncoder.h b/content/media/encoder/OmxTrackEncoder.h new file mode 100644 index 00000000000..1256df7dd20 --- /dev/null +++ b/content/media/encoder/OmxTrackEncoder.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef OmxTrackEncoder_h_ +#define OmxTrackEncoder_h_ + +#include "TrackEncoder.h" + +namespace android { +class OMXVideoEncoder; +} + +/** + * There are two major classes defined in file OmxTrackEncoder; + * OmxVideoTrackEncoder and OmxAudioTrackEncoder, the video and audio track + * encoder for media type AVC/h.264 and AAC. OMXCodecWrapper wraps and controls + * an instance of MediaCodec, defined in libstagefright, runs on Android Jelly + * Bean platform. + */ + +namespace mozilla { + +class OmxVideoTrackEncoder: public VideoTrackEncoder +{ +public: + OmxVideoTrackEncoder() + : VideoTrackEncoder() + {} + + already_AddRefed GetMetadata() MOZ_OVERRIDE; + + nsresult GetEncodedTrack(EncodedFrameContainer& aData) MOZ_OVERRIDE; + +protected: + nsresult Init(int aWidth, int aHeight, TrackRate aTrackRate) MOZ_OVERRIDE; + +private: + nsAutoPtr mEncoder; +}; + +} +#endif diff --git a/content/media/encoder/moz.build b/content/media/encoder/moz.build index f236107b60f..e9a16eb7aaf 100644 --- a/content/media/encoder/moz.build +++ b/content/media/encoder/moz.build @@ -26,7 +26,11 @@ if CONFIG['MOZ_OPUS']: if CONFIG['MOZ_OMX_ENCODER']: DEFINES['MOZ_OMX_ENCODER'] = True + EXPORTS += ['OmxTrackEncoder.h'] + UNIFIED_SOURCES += ['OmxTrackEncoder.cpp'] FAIL_ON_WARNINGS = True FINAL_LIBRARY = 'gklayout' + +include('/ipc/chromium/chromium-config.mozbuild')