content/media/encoder/OmxTrackEncoder.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "OmxTrackEncoder.h"
michael@0 7 #include "OMXCodecWrapper.h"
michael@0 8 #include "VideoUtils.h"
michael@0 9 #include "ISOTrackMetadata.h"
michael@0 10
michael@0 11 #ifdef MOZ_WIDGET_GONK
michael@0 12 #include <android/log.h>
michael@0 13 #define OMX_LOG(args...) \
michael@0 14 do { \
michael@0 15 __android_log_print(ANDROID_LOG_INFO, "OmxTrackEncoder", ##args); \
michael@0 16 } while (0)
michael@0 17 #else
michael@0 18 #define OMX_LOG(args, ...)
michael@0 19 #endif
michael@0 20
michael@0 21 using namespace android;
michael@0 22
michael@0 23 namespace mozilla {
michael@0 24
michael@0 25 #define ENCODER_CONFIG_FRAME_RATE 30 // fps
michael@0 26 #define GET_ENCODED_VIDEO_FRAME_TIMEOUT 100000 // microseconds
michael@0 27
michael@0 28 nsresult
michael@0 29 OmxVideoTrackEncoder::Init(int aWidth, int aHeight, int aDisplayWidth,
michael@0 30 int aDisplayHeight, TrackRate aTrackRate)
michael@0 31 {
michael@0 32 mFrameWidth = aWidth;
michael@0 33 mFrameHeight = aHeight;
michael@0 34 mTrackRate = aTrackRate;
michael@0 35 mDisplayWidth = aDisplayWidth;
michael@0 36 mDisplayHeight = aDisplayHeight;
michael@0 37
michael@0 38 mEncoder = OMXCodecWrapper::CreateAVCEncoder();
michael@0 39 NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
michael@0 40
michael@0 41 nsresult rv = mEncoder->Configure(mFrameWidth, mFrameHeight,
michael@0 42 ENCODER_CONFIG_FRAME_RATE);
michael@0 43
michael@0 44 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
michael@0 45 mInitialized = (rv == NS_OK);
michael@0 46
michael@0 47 mReentrantMonitor.NotifyAll();
michael@0 48
michael@0 49 return rv;
michael@0 50 }
michael@0 51
michael@0 52 already_AddRefed<TrackMetadataBase>
michael@0 53 OmxVideoTrackEncoder::GetMetadata()
michael@0 54 {
michael@0 55 {
michael@0 56 // Wait if mEncoder is not initialized nor is being canceled.
michael@0 57 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
michael@0 58 while (!mCanceled && !mInitialized) {
michael@0 59 mReentrantMonitor.Wait();
michael@0 60 }
michael@0 61 }
michael@0 62
michael@0 63 if (mCanceled || mEncodingComplete) {
michael@0 64 return nullptr;
michael@0 65 }
michael@0 66
michael@0 67 nsRefPtr<AVCTrackMetadata> meta = new AVCTrackMetadata();
michael@0 68 meta->mWidth = mFrameWidth;
michael@0 69 meta->mHeight = mFrameHeight;
michael@0 70 meta->mDisplayWidth = mDisplayWidth;
michael@0 71 meta->mDisplayHeight = mDisplayHeight;
michael@0 72 meta->mFrameRate = ENCODER_CONFIG_FRAME_RATE;
michael@0 73 return meta.forget();
michael@0 74 }
michael@0 75
michael@0 76 nsresult
michael@0 77 OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
michael@0 78 {
michael@0 79 VideoSegment segment;
michael@0 80 {
michael@0 81 // Move all the samples from mRawSegment to segment. We only hold the
michael@0 82 // monitor in this block.
michael@0 83 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
michael@0 84
michael@0 85 // Wait if mEncoder is not initialized nor is being canceled.
michael@0 86 while (!mCanceled && (!mInitialized ||
michael@0 87 (mRawSegment.GetDuration() == 0 && !mEndOfStream))) {
michael@0 88 mReentrantMonitor.Wait();
michael@0 89 }
michael@0 90
michael@0 91 if (mCanceled || mEncodingComplete) {
michael@0 92 return NS_ERROR_FAILURE;
michael@0 93 }
michael@0 94
michael@0 95 segment.AppendFrom(&mRawSegment);
michael@0 96 }
michael@0 97
michael@0 98 // Start queuing raw frames to the input buffers of OMXCodecWrapper.
michael@0 99 VideoSegment::ChunkIterator iter(segment);
michael@0 100 while (!iter.IsEnded()) {
michael@0 101 VideoChunk chunk = *iter;
michael@0 102
michael@0 103 // Send only the unique video frames to OMXCodecWrapper.
michael@0 104 if (mLastFrame != chunk.mFrame) {
michael@0 105 uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate;
michael@0 106 layers::Image* img = (chunk.IsNull() || chunk.mFrame.GetForceBlack()) ?
michael@0 107 nullptr : chunk.mFrame.GetImage();
michael@0 108 mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs);
michael@0 109 }
michael@0 110
michael@0 111 mLastFrame.TakeFrom(&chunk.mFrame);
michael@0 112 mTotalFrameDuration += chunk.GetDuration();
michael@0 113
michael@0 114 iter.Next();
michael@0 115 }
michael@0 116
michael@0 117 // Send the EOS signal to OMXCodecWrapper.
michael@0 118 if (mEndOfStream && iter.IsEnded() && !mEosSetInEncoder) {
michael@0 119 uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate;
michael@0 120 layers::Image* img = (!mLastFrame.GetImage() || mLastFrame.GetForceBlack())
michael@0 121 ? nullptr : mLastFrame.GetImage();
michael@0 122 nsresult result = mEncoder->Encode(img, mFrameWidth, mFrameHeight,
michael@0 123 totalDurationUs,
michael@0 124 OMXCodecWrapper::BUFFER_EOS);
michael@0 125 // Keep sending EOS signal until OMXVideoEncoder gets it.
michael@0 126 if (result == NS_OK) {
michael@0 127 mEosSetInEncoder = true;
michael@0 128 }
michael@0 129 }
michael@0 130
michael@0 131 // Dequeue an encoded frame from the output buffers of OMXCodecWrapper.
michael@0 132 nsresult rv;
michael@0 133 nsTArray<uint8_t> buffer;
michael@0 134 int outFlags = 0;
michael@0 135 int64_t outTimeStampUs = 0;
michael@0 136 mEncoder->GetNextEncodedFrame(&buffer, &outTimeStampUs, &outFlags,
michael@0 137 GET_ENCODED_VIDEO_FRAME_TIMEOUT);
michael@0 138 if (!buffer.IsEmpty()) {
michael@0 139 nsRefPtr<EncodedFrame> videoData = new EncodedFrame();
michael@0 140 if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) {
michael@0 141 videoData->SetFrameType(EncodedFrame::AVC_CSD);
michael@0 142 } else {
michael@0 143 videoData->SetFrameType((outFlags & OMXCodecWrapper::BUFFER_SYNC_FRAME) ?
michael@0 144 EncodedFrame::AVC_I_FRAME : EncodedFrame::AVC_P_FRAME);
michael@0 145 }
michael@0 146 rv = videoData->SwapInFrameData(buffer);
michael@0 147 NS_ENSURE_SUCCESS(rv, rv);
michael@0 148 videoData->SetTimeStamp(outTimeStampUs);
michael@0 149 aData.AppendEncodedFrame(videoData);
michael@0 150 }
michael@0 151
michael@0 152 if (outFlags & OMXCodecWrapper::BUFFER_EOS) {
michael@0 153 mEncodingComplete = true;
michael@0 154 OMX_LOG("Done encoding video.");
michael@0 155 }
michael@0 156
michael@0 157 return NS_OK;
michael@0 158 }
michael@0 159
michael@0 160 nsresult
michael@0 161 OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
michael@0 162 {
michael@0 163 nsTArray<uint8_t> frameData;
michael@0 164 int outFlags = 0;
michael@0 165 int64_t outTimeUs = -1;
michael@0 166
michael@0 167 nsresult rv = mEncoder->GetNextEncodedFrame(&frameData, &outTimeUs, &outFlags,
michael@0 168 3000); // wait up to 3ms
michael@0 169 NS_ENSURE_SUCCESS(rv, rv);
michael@0 170
michael@0 171 if (!frameData.IsEmpty()) {
michael@0 172 bool isCSD = false;
michael@0 173 if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { // codec specific data
michael@0 174 isCSD = true;
michael@0 175 } else if (outFlags & OMXCodecWrapper::BUFFER_EOS) { // last frame
michael@0 176 mEncodingComplete = true;
michael@0 177 }
michael@0 178
michael@0 179 nsRefPtr<EncodedFrame> audiodata = new EncodedFrame();
michael@0 180 if (mEncoder->GetCodecType() == OMXCodecWrapper::AAC_ENC) {
michael@0 181 audiodata->SetFrameType(isCSD ?
michael@0 182 EncodedFrame::AAC_CSD : EncodedFrame::AAC_AUDIO_FRAME);
michael@0 183 } else if (mEncoder->GetCodecType() == OMXCodecWrapper::AMR_NB_ENC){
michael@0 184 audiodata->SetFrameType(isCSD ?
michael@0 185 EncodedFrame::AMR_AUDIO_CSD : EncodedFrame::AMR_AUDIO_FRAME);
michael@0 186 } else {
michael@0 187 MOZ_ASSERT("audio codec not supported");
michael@0 188 }
michael@0 189 audiodata->SetTimeStamp(outTimeUs);
michael@0 190 rv = audiodata->SwapInFrameData(frameData);
michael@0 191 NS_ENSURE_SUCCESS(rv, rv);
michael@0 192 aContainer.AppendEncodedFrame(audiodata);
michael@0 193 }
michael@0 194
michael@0 195 return NS_OK;
michael@0 196 }
michael@0 197
michael@0 198 nsresult
michael@0 199 OmxAudioTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
michael@0 200 {
michael@0 201 AudioSegment segment;
michael@0 202 // Move all the samples from mRawSegment to segment. We only hold
michael@0 203 // the monitor in this block.
michael@0 204 {
michael@0 205 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
michael@0 206
michael@0 207 // Wait if mEncoder is not initialized nor canceled.
michael@0 208 while (!mInitialized && !mCanceled) {
michael@0 209 mReentrantMonitor.Wait();
michael@0 210 }
michael@0 211
michael@0 212 if (mCanceled || mEncodingComplete) {
michael@0 213 return NS_ERROR_FAILURE;
michael@0 214 }
michael@0 215
michael@0 216 segment.AppendFrom(&mRawSegment);
michael@0 217 }
michael@0 218
michael@0 219 nsresult rv;
michael@0 220 if (segment.GetDuration() == 0) {
michael@0 221 // Notify EOS at least once, even if segment is empty.
michael@0 222 if (mEndOfStream && !mEosSetInEncoder) {
michael@0 223 mEosSetInEncoder = true;
michael@0 224 rv = mEncoder->Encode(segment, OMXCodecWrapper::BUFFER_EOS);
michael@0 225 NS_ENSURE_SUCCESS(rv, rv);
michael@0 226 }
michael@0 227 // Nothing to encode but encoder could still have encoded data for earlier
michael@0 228 // input.
michael@0 229 return AppendEncodedFrames(aData);
michael@0 230 }
michael@0 231
michael@0 232 // OMX encoder has limited input buffers only so we have to feed input and get
michael@0 233 // output more than once if there are too many samples pending in segment.
michael@0 234 while (segment.GetDuration() > 0) {
michael@0 235 rv = mEncoder->Encode(segment,
michael@0 236 mEndOfStream ? OMXCodecWrapper::BUFFER_EOS : 0);
michael@0 237 NS_ENSURE_SUCCESS(rv, rv);
michael@0 238
michael@0 239 rv = AppendEncodedFrames(aData);
michael@0 240 NS_ENSURE_SUCCESS(rv, rv);
michael@0 241 }
michael@0 242
michael@0 243 return NS_OK;
michael@0 244 }
michael@0 245
michael@0 246 nsresult
michael@0 247 OmxAACAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
michael@0 248 {
michael@0 249 mChannels = aChannels;
michael@0 250 mSamplingRate = aSamplingRate;
michael@0 251
michael@0 252 mEncoder = OMXCodecWrapper::CreateAACEncoder();
michael@0 253 NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
michael@0 254
michael@0 255 nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, mSamplingRate);
michael@0 256
michael@0 257 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
michael@0 258 mInitialized = (rv == NS_OK);
michael@0 259
michael@0 260 mReentrantMonitor.NotifyAll();
michael@0 261
michael@0 262 return NS_OK;
michael@0 263 }
michael@0 264
michael@0 265 already_AddRefed<TrackMetadataBase>
michael@0 266 OmxAACAudioTrackEncoder::GetMetadata()
michael@0 267 {
michael@0 268 {
michael@0 269 // Wait if mEncoder is not initialized nor is being canceled.
michael@0 270 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
michael@0 271 while (!mCanceled && !mInitialized) {
michael@0 272 mReentrantMonitor.Wait();
michael@0 273 }
michael@0 274 }
michael@0 275
michael@0 276 if (mCanceled || mEncodingComplete) {
michael@0 277 return nullptr;
michael@0 278 }
michael@0 279 nsRefPtr<AACTrackMetadata> meta = new AACTrackMetadata();
michael@0 280 meta->mChannels = mChannels;
michael@0 281 meta->mSampleRate = mSamplingRate;
michael@0 282 meta->mFrameSize = OMXCodecWrapper::kAACFrameSize;
michael@0 283 meta->mFrameDuration = OMXCodecWrapper::kAACFrameDuration;
michael@0 284 return meta.forget();
michael@0 285 }
michael@0 286
michael@0 287 nsresult
michael@0 288 OmxAMRAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
michael@0 289 {
michael@0 290 mChannels = aChannels;
michael@0 291 mSamplingRate = aSamplingRate;
michael@0 292
michael@0 293 mEncoder = OMXCodecWrapper::CreateAMRNBEncoder();
michael@0 294 NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
michael@0 295
michael@0 296 nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, AMR_NB_SAMPLERATE);
michael@0 297 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
michael@0 298 mInitialized = (rv == NS_OK);
michael@0 299
michael@0 300 mReentrantMonitor.NotifyAll();
michael@0 301
michael@0 302 return NS_OK;
michael@0 303 }
michael@0 304
michael@0 305 already_AddRefed<TrackMetadataBase>
michael@0 306 OmxAMRAudioTrackEncoder::GetMetadata()
michael@0 307 {
michael@0 308 {
michael@0 309 // Wait if mEncoder is not initialized nor is being canceled.
michael@0 310 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
michael@0 311 while (!mCanceled && !mInitialized) {
michael@0 312 mReentrantMonitor.Wait();
michael@0 313 }
michael@0 314 }
michael@0 315
michael@0 316 if (mCanceled || mEncodingComplete) {
michael@0 317 return nullptr;
michael@0 318 }
michael@0 319
michael@0 320 nsRefPtr<AMRTrackMetadata> meta = new AMRTrackMetadata();
michael@0 321 return meta.forget();
michael@0 322 }
michael@0 323
michael@0 324 }

mercurial