content/media/encoder/OmxTrackEncoder.cpp

branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
equal deleted inserted replaced
-1:000000000000 0:69e0f4059c4e
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/. */
5
6 #include "OmxTrackEncoder.h"
7 #include "OMXCodecWrapper.h"
8 #include "VideoUtils.h"
9 #include "ISOTrackMetadata.h"
10
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
20
21 using namespace android;
22
23 namespace mozilla {
24
25 #define ENCODER_CONFIG_FRAME_RATE 30 // fps
26 #define GET_ENCODED_VIDEO_FRAME_TIMEOUT 100000 // microseconds
27
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;
37
38 mEncoder = OMXCodecWrapper::CreateAVCEncoder();
39 NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
40
41 nsresult rv = mEncoder->Configure(mFrameWidth, mFrameHeight,
42 ENCODER_CONFIG_FRAME_RATE);
43
44 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
45 mInitialized = (rv == NS_OK);
46
47 mReentrantMonitor.NotifyAll();
48
49 return rv;
50 }
51
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 }
62
63 if (mCanceled || mEncodingComplete) {
64 return nullptr;
65 }
66
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 }
75
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);
84
85 // Wait if mEncoder is not initialized nor is being canceled.
86 while (!mCanceled && (!mInitialized ||
87 (mRawSegment.GetDuration() == 0 && !mEndOfStream))) {
88 mReentrantMonitor.Wait();
89 }
90
91 if (mCanceled || mEncodingComplete) {
92 return NS_ERROR_FAILURE;
93 }
94
95 segment.AppendFrom(&mRawSegment);
96 }
97
98 // Start queuing raw frames to the input buffers of OMXCodecWrapper.
99 VideoSegment::ChunkIterator iter(segment);
100 while (!iter.IsEnded()) {
101 VideoChunk chunk = *iter;
102
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 }
110
111 mLastFrame.TakeFrom(&chunk.mFrame);
112 mTotalFrameDuration += chunk.GetDuration();
113
114 iter.Next();
115 }
116
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 }
130
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 }
151
152 if (outFlags & OMXCodecWrapper::BUFFER_EOS) {
153 mEncodingComplete = true;
154 OMX_LOG("Done encoding video.");
155 }
156
157 return NS_OK;
158 }
159
160 nsresult
161 OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
162 {
163 nsTArray<uint8_t> frameData;
164 int outFlags = 0;
165 int64_t outTimeUs = -1;
166
167 nsresult rv = mEncoder->GetNextEncodedFrame(&frameData, &outTimeUs, &outFlags,
168 3000); // wait up to 3ms
169 NS_ENSURE_SUCCESS(rv, rv);
170
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 }
178
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 }
194
195 return NS_OK;
196 }
197
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);
206
207 // Wait if mEncoder is not initialized nor canceled.
208 while (!mInitialized && !mCanceled) {
209 mReentrantMonitor.Wait();
210 }
211
212 if (mCanceled || mEncodingComplete) {
213 return NS_ERROR_FAILURE;
214 }
215
216 segment.AppendFrom(&mRawSegment);
217 }
218
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 }
231
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);
238
239 rv = AppendEncodedFrames(aData);
240 NS_ENSURE_SUCCESS(rv, rv);
241 }
242
243 return NS_OK;
244 }
245
246 nsresult
247 OmxAACAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
248 {
249 mChannels = aChannels;
250 mSamplingRate = aSamplingRate;
251
252 mEncoder = OMXCodecWrapper::CreateAACEncoder();
253 NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
254
255 nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, mSamplingRate);
256
257 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
258 mInitialized = (rv == NS_OK);
259
260 mReentrantMonitor.NotifyAll();
261
262 return NS_OK;
263 }
264
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 }
275
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 }
286
287 nsresult
288 OmxAMRAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
289 {
290 mChannels = aChannels;
291 mSamplingRate = aSamplingRate;
292
293 mEncoder = OMXCodecWrapper::CreateAMRNBEncoder();
294 NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
295
296 nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, AMR_NB_SAMPLERATE);
297 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
298 mInitialized = (rv == NS_OK);
299
300 mReentrantMonitor.NotifyAll();
301
302 return NS_OK;
303 }
304
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 }
315
316 if (mCanceled || mEncodingComplete) {
317 return nullptr;
318 }
319
320 nsRefPtr<AMRTrackMetadata> meta = new AMRTrackMetadata();
321 return meta.forget();
322 }
323
324 }

mercurial