Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 | } |