content/media/encoder/OmxTrackEncoder.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial