1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/encoder/OmxTrackEncoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,324 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "OmxTrackEncoder.h" 1.10 +#include "OMXCodecWrapper.h" 1.11 +#include "VideoUtils.h" 1.12 +#include "ISOTrackMetadata.h" 1.13 + 1.14 +#ifdef MOZ_WIDGET_GONK 1.15 +#include <android/log.h> 1.16 +#define OMX_LOG(args...) \ 1.17 + do { \ 1.18 + __android_log_print(ANDROID_LOG_INFO, "OmxTrackEncoder", ##args); \ 1.19 + } while (0) 1.20 +#else 1.21 +#define OMX_LOG(args, ...) 1.22 +#endif 1.23 + 1.24 +using namespace android; 1.25 + 1.26 +namespace mozilla { 1.27 + 1.28 +#define ENCODER_CONFIG_FRAME_RATE 30 // fps 1.29 +#define GET_ENCODED_VIDEO_FRAME_TIMEOUT 100000 // microseconds 1.30 + 1.31 +nsresult 1.32 +OmxVideoTrackEncoder::Init(int aWidth, int aHeight, int aDisplayWidth, 1.33 + int aDisplayHeight, TrackRate aTrackRate) 1.34 +{ 1.35 + mFrameWidth = aWidth; 1.36 + mFrameHeight = aHeight; 1.37 + mTrackRate = aTrackRate; 1.38 + mDisplayWidth = aDisplayWidth; 1.39 + mDisplayHeight = aDisplayHeight; 1.40 + 1.41 + mEncoder = OMXCodecWrapper::CreateAVCEncoder(); 1.42 + NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE); 1.43 + 1.44 + nsresult rv = mEncoder->Configure(mFrameWidth, mFrameHeight, 1.45 + ENCODER_CONFIG_FRAME_RATE); 1.46 + 1.47 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.48 + mInitialized = (rv == NS_OK); 1.49 + 1.50 + mReentrantMonitor.NotifyAll(); 1.51 + 1.52 + return rv; 1.53 +} 1.54 + 1.55 +already_AddRefed<TrackMetadataBase> 1.56 +OmxVideoTrackEncoder::GetMetadata() 1.57 +{ 1.58 + { 1.59 + // Wait if mEncoder is not initialized nor is being canceled. 1.60 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.61 + while (!mCanceled && !mInitialized) { 1.62 + mReentrantMonitor.Wait(); 1.63 + } 1.64 + } 1.65 + 1.66 + if (mCanceled || mEncodingComplete) { 1.67 + return nullptr; 1.68 + } 1.69 + 1.70 + nsRefPtr<AVCTrackMetadata> meta = new AVCTrackMetadata(); 1.71 + meta->mWidth = mFrameWidth; 1.72 + meta->mHeight = mFrameHeight; 1.73 + meta->mDisplayWidth = mDisplayWidth; 1.74 + meta->mDisplayHeight = mDisplayHeight; 1.75 + meta->mFrameRate = ENCODER_CONFIG_FRAME_RATE; 1.76 + return meta.forget(); 1.77 +} 1.78 + 1.79 +nsresult 1.80 +OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData) 1.81 +{ 1.82 + VideoSegment segment; 1.83 + { 1.84 + // Move all the samples from mRawSegment to segment. We only hold the 1.85 + // monitor in this block. 1.86 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.87 + 1.88 + // Wait if mEncoder is not initialized nor is being canceled. 1.89 + while (!mCanceled && (!mInitialized || 1.90 + (mRawSegment.GetDuration() == 0 && !mEndOfStream))) { 1.91 + mReentrantMonitor.Wait(); 1.92 + } 1.93 + 1.94 + if (mCanceled || mEncodingComplete) { 1.95 + return NS_ERROR_FAILURE; 1.96 + } 1.97 + 1.98 + segment.AppendFrom(&mRawSegment); 1.99 + } 1.100 + 1.101 + // Start queuing raw frames to the input buffers of OMXCodecWrapper. 1.102 + VideoSegment::ChunkIterator iter(segment); 1.103 + while (!iter.IsEnded()) { 1.104 + VideoChunk chunk = *iter; 1.105 + 1.106 + // Send only the unique video frames to OMXCodecWrapper. 1.107 + if (mLastFrame != chunk.mFrame) { 1.108 + uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate; 1.109 + layers::Image* img = (chunk.IsNull() || chunk.mFrame.GetForceBlack()) ? 1.110 + nullptr : chunk.mFrame.GetImage(); 1.111 + mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs); 1.112 + } 1.113 + 1.114 + mLastFrame.TakeFrom(&chunk.mFrame); 1.115 + mTotalFrameDuration += chunk.GetDuration(); 1.116 + 1.117 + iter.Next(); 1.118 + } 1.119 + 1.120 + // Send the EOS signal to OMXCodecWrapper. 1.121 + if (mEndOfStream && iter.IsEnded() && !mEosSetInEncoder) { 1.122 + uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate; 1.123 + layers::Image* img = (!mLastFrame.GetImage() || mLastFrame.GetForceBlack()) 1.124 + ? nullptr : mLastFrame.GetImage(); 1.125 + nsresult result = mEncoder->Encode(img, mFrameWidth, mFrameHeight, 1.126 + totalDurationUs, 1.127 + OMXCodecWrapper::BUFFER_EOS); 1.128 + // Keep sending EOS signal until OMXVideoEncoder gets it. 1.129 + if (result == NS_OK) { 1.130 + mEosSetInEncoder = true; 1.131 + } 1.132 + } 1.133 + 1.134 + // Dequeue an encoded frame from the output buffers of OMXCodecWrapper. 1.135 + nsresult rv; 1.136 + nsTArray<uint8_t> buffer; 1.137 + int outFlags = 0; 1.138 + int64_t outTimeStampUs = 0; 1.139 + mEncoder->GetNextEncodedFrame(&buffer, &outTimeStampUs, &outFlags, 1.140 + GET_ENCODED_VIDEO_FRAME_TIMEOUT); 1.141 + if (!buffer.IsEmpty()) { 1.142 + nsRefPtr<EncodedFrame> videoData = new EncodedFrame(); 1.143 + if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { 1.144 + videoData->SetFrameType(EncodedFrame::AVC_CSD); 1.145 + } else { 1.146 + videoData->SetFrameType((outFlags & OMXCodecWrapper::BUFFER_SYNC_FRAME) ? 1.147 + EncodedFrame::AVC_I_FRAME : EncodedFrame::AVC_P_FRAME); 1.148 + } 1.149 + rv = videoData->SwapInFrameData(buffer); 1.150 + NS_ENSURE_SUCCESS(rv, rv); 1.151 + videoData->SetTimeStamp(outTimeStampUs); 1.152 + aData.AppendEncodedFrame(videoData); 1.153 + } 1.154 + 1.155 + if (outFlags & OMXCodecWrapper::BUFFER_EOS) { 1.156 + mEncodingComplete = true; 1.157 + OMX_LOG("Done encoding video."); 1.158 + } 1.159 + 1.160 + return NS_OK; 1.161 +} 1.162 + 1.163 +nsresult 1.164 +OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer) 1.165 +{ 1.166 + nsTArray<uint8_t> frameData; 1.167 + int outFlags = 0; 1.168 + int64_t outTimeUs = -1; 1.169 + 1.170 + nsresult rv = mEncoder->GetNextEncodedFrame(&frameData, &outTimeUs, &outFlags, 1.171 + 3000); // wait up to 3ms 1.172 + NS_ENSURE_SUCCESS(rv, rv); 1.173 + 1.174 + if (!frameData.IsEmpty()) { 1.175 + bool isCSD = false; 1.176 + if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { // codec specific data 1.177 + isCSD = true; 1.178 + } else if (outFlags & OMXCodecWrapper::BUFFER_EOS) { // last frame 1.179 + mEncodingComplete = true; 1.180 + } 1.181 + 1.182 + nsRefPtr<EncodedFrame> audiodata = new EncodedFrame(); 1.183 + if (mEncoder->GetCodecType() == OMXCodecWrapper::AAC_ENC) { 1.184 + audiodata->SetFrameType(isCSD ? 1.185 + EncodedFrame::AAC_CSD : EncodedFrame::AAC_AUDIO_FRAME); 1.186 + } else if (mEncoder->GetCodecType() == OMXCodecWrapper::AMR_NB_ENC){ 1.187 + audiodata->SetFrameType(isCSD ? 1.188 + EncodedFrame::AMR_AUDIO_CSD : EncodedFrame::AMR_AUDIO_FRAME); 1.189 + } else { 1.190 + MOZ_ASSERT("audio codec not supported"); 1.191 + } 1.192 + audiodata->SetTimeStamp(outTimeUs); 1.193 + rv = audiodata->SwapInFrameData(frameData); 1.194 + NS_ENSURE_SUCCESS(rv, rv); 1.195 + aContainer.AppendEncodedFrame(audiodata); 1.196 + } 1.197 + 1.198 + return NS_OK; 1.199 +} 1.200 + 1.201 +nsresult 1.202 +OmxAudioTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData) 1.203 +{ 1.204 + AudioSegment segment; 1.205 + // Move all the samples from mRawSegment to segment. We only hold 1.206 + // the monitor in this block. 1.207 + { 1.208 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.209 + 1.210 + // Wait if mEncoder is not initialized nor canceled. 1.211 + while (!mInitialized && !mCanceled) { 1.212 + mReentrantMonitor.Wait(); 1.213 + } 1.214 + 1.215 + if (mCanceled || mEncodingComplete) { 1.216 + return NS_ERROR_FAILURE; 1.217 + } 1.218 + 1.219 + segment.AppendFrom(&mRawSegment); 1.220 + } 1.221 + 1.222 + nsresult rv; 1.223 + if (segment.GetDuration() == 0) { 1.224 + // Notify EOS at least once, even if segment is empty. 1.225 + if (mEndOfStream && !mEosSetInEncoder) { 1.226 + mEosSetInEncoder = true; 1.227 + rv = mEncoder->Encode(segment, OMXCodecWrapper::BUFFER_EOS); 1.228 + NS_ENSURE_SUCCESS(rv, rv); 1.229 + } 1.230 + // Nothing to encode but encoder could still have encoded data for earlier 1.231 + // input. 1.232 + return AppendEncodedFrames(aData); 1.233 + } 1.234 + 1.235 + // OMX encoder has limited input buffers only so we have to feed input and get 1.236 + // output more than once if there are too many samples pending in segment. 1.237 + while (segment.GetDuration() > 0) { 1.238 + rv = mEncoder->Encode(segment, 1.239 + mEndOfStream ? OMXCodecWrapper::BUFFER_EOS : 0); 1.240 + NS_ENSURE_SUCCESS(rv, rv); 1.241 + 1.242 + rv = AppendEncodedFrames(aData); 1.243 + NS_ENSURE_SUCCESS(rv, rv); 1.244 + } 1.245 + 1.246 + return NS_OK; 1.247 +} 1.248 + 1.249 +nsresult 1.250 +OmxAACAudioTrackEncoder::Init(int aChannels, int aSamplingRate) 1.251 +{ 1.252 + mChannels = aChannels; 1.253 + mSamplingRate = aSamplingRate; 1.254 + 1.255 + mEncoder = OMXCodecWrapper::CreateAACEncoder(); 1.256 + NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE); 1.257 + 1.258 + nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, mSamplingRate); 1.259 + 1.260 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.261 + mInitialized = (rv == NS_OK); 1.262 + 1.263 + mReentrantMonitor.NotifyAll(); 1.264 + 1.265 + return NS_OK; 1.266 +} 1.267 + 1.268 +already_AddRefed<TrackMetadataBase> 1.269 +OmxAACAudioTrackEncoder::GetMetadata() 1.270 +{ 1.271 + { 1.272 + // Wait if mEncoder is not initialized nor is being canceled. 1.273 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.274 + while (!mCanceled && !mInitialized) { 1.275 + mReentrantMonitor.Wait(); 1.276 + } 1.277 + } 1.278 + 1.279 + if (mCanceled || mEncodingComplete) { 1.280 + return nullptr; 1.281 + } 1.282 + nsRefPtr<AACTrackMetadata> meta = new AACTrackMetadata(); 1.283 + meta->mChannels = mChannels; 1.284 + meta->mSampleRate = mSamplingRate; 1.285 + meta->mFrameSize = OMXCodecWrapper::kAACFrameSize; 1.286 + meta->mFrameDuration = OMXCodecWrapper::kAACFrameDuration; 1.287 + return meta.forget(); 1.288 +} 1.289 + 1.290 +nsresult 1.291 +OmxAMRAudioTrackEncoder::Init(int aChannels, int aSamplingRate) 1.292 +{ 1.293 + mChannels = aChannels; 1.294 + mSamplingRate = aSamplingRate; 1.295 + 1.296 + mEncoder = OMXCodecWrapper::CreateAMRNBEncoder(); 1.297 + NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE); 1.298 + 1.299 + nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, AMR_NB_SAMPLERATE); 1.300 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.301 + mInitialized = (rv == NS_OK); 1.302 + 1.303 + mReentrantMonitor.NotifyAll(); 1.304 + 1.305 + return NS_OK; 1.306 +} 1.307 + 1.308 +already_AddRefed<TrackMetadataBase> 1.309 +OmxAMRAudioTrackEncoder::GetMetadata() 1.310 +{ 1.311 + { 1.312 + // Wait if mEncoder is not initialized nor is being canceled. 1.313 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.314 + while (!mCanceled && !mInitialized) { 1.315 + mReentrantMonitor.Wait(); 1.316 + } 1.317 + } 1.318 + 1.319 + if (mCanceled || mEncodingComplete) { 1.320 + return nullptr; 1.321 + } 1.322 + 1.323 + nsRefPtr<AMRTrackMetadata> meta = new AMRTrackMetadata(); 1.324 + return meta.forget(); 1.325 +} 1.326 + 1.327 +}