blob: d2a71545c991d188ae17fa9539934eca2a9ed520 [file] [log] [blame]
Linus Nilsson0da327a2020-01-31 16:22:18 -08001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// #define LOG_NDEBUG 0
18#define LOG_TAG "VideoTrackTranscoder"
19
20#include <android-base/logging.h>
21#include <media/VideoTrackTranscoder.h>
Linus Nilssonb09aac22020-07-29 11:56:53 -070022#include <utils/AndroidThreads.h>
Linus Nilsson0da327a2020-01-31 16:22:18 -080023
24namespace android {
25
26// Check that the codec sample flags have the expected NDK meaning.
27static_assert(SAMPLE_FLAG_CODEC_CONFIG == AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG,
28 "Sample flag mismatch: CODEC_CONFIG");
29static_assert(SAMPLE_FLAG_END_OF_STREAM == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM,
30 "Sample flag mismatch: END_OF_STREAM");
31static_assert(SAMPLE_FLAG_PARTIAL_FRAME == AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME,
32 "Sample flag mismatch: PARTIAL_FRAME");
33
Linus Nilssoncab39d82020-05-14 16:32:21 -070034// Color format defined by surface. (See MediaCodecInfo.CodecCapabilities#COLOR_FormatSurface.)
35static constexpr int32_t kColorFormatSurface = 0x7f000789;
36// Default key frame interval in seconds.
37static constexpr float kDefaultKeyFrameIntervalSeconds = 1.0f;
38
Linus Nilsson0da327a2020-01-31 16:22:18 -080039template <typename T>
40void VideoTrackTranscoder::BlockingQueue<T>::push(T const& value, bool front) {
41 {
42 std::unique_lock<std::mutex> lock(mMutex);
43 if (front) {
44 mQueue.push_front(value);
45 } else {
46 mQueue.push_back(value);
47 }
48 }
49 mCondition.notify_one();
50}
51
52template <typename T>
53T VideoTrackTranscoder::BlockingQueue<T>::pop() {
54 std::unique_lock<std::mutex> lock(mMutex);
55 while (mQueue.empty()) {
56 mCondition.wait(lock);
57 }
58 T value = mQueue.front();
59 mQueue.pop_front();
60 return value;
61}
62
Linus Nilssone4716f22020-07-10 16:07:57 -070063// The CodecWrapper class is used to let AMediaCodec instances outlive the transcoder object itself
64// by giving the codec a weak pointer to the transcoder. Codecs wrapped in this object are kept
65// alive by the transcoder and the codec's outstanding buffers. Once the transcoder stops and all
66// output buffers have been released by downstream components the codec will also be released.
67class VideoTrackTranscoder::CodecWrapper {
68public:
69 CodecWrapper(AMediaCodec* codec, const std::weak_ptr<VideoTrackTranscoder>& transcoder)
70 : mCodec(codec), mTranscoder(transcoder), mCodecStarted(false) {}
71 ~CodecWrapper() {
72 if (mCodecStarted) {
73 AMediaCodec_stop(mCodec);
74 }
75 AMediaCodec_delete(mCodec);
76 }
77
78 AMediaCodec* getCodec() { return mCodec; }
79 std::shared_ptr<VideoTrackTranscoder> getTranscoder() const { return mTranscoder.lock(); };
80 void setStarted() { mCodecStarted = true; }
81
82private:
83 AMediaCodec* mCodec;
84 std::weak_ptr<VideoTrackTranscoder> mTranscoder;
85 bool mCodecStarted;
86};
87
Linus Nilsson0da327a2020-01-31 16:22:18 -080088// Dispatch responses to codec callbacks onto the message queue.
89struct AsyncCodecCallbackDispatch {
90 static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
Linus Nilssone4716f22020-07-10 16:07:57 -070091 VideoTrackTranscoder::CodecWrapper* wrapper =
92 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
93 if (auto transcoder = wrapper->getTranscoder()) {
94 if (codec == transcoder->mDecoder) {
95 transcoder->mCodecMessageQueue.push(
96 [transcoder, index] { transcoder->enqueueInputSample(index); });
97 }
Linus Nilsson0da327a2020-01-31 16:22:18 -080098 }
99 }
100
101 static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
102 AMediaCodecBufferInfo* bufferInfoPtr) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700103 VideoTrackTranscoder::CodecWrapper* wrapper =
104 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800105 AMediaCodecBufferInfo bufferInfo = *bufferInfoPtr;
Linus Nilssone4716f22020-07-10 16:07:57 -0700106 if (auto transcoder = wrapper->getTranscoder()) {
107 transcoder->mCodecMessageQueue.push([transcoder, index, codec, bufferInfo] {
108 if (codec == transcoder->mDecoder) {
109 transcoder->transferBuffer(index, bufferInfo);
110 } else if (codec == transcoder->mEncoder->getCodec()) {
111 transcoder->dequeueOutputSample(index, bufferInfo);
112 }
113 });
114 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800115 }
116
117 static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700118 VideoTrackTranscoder::CodecWrapper* wrapper =
119 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
120 if (auto transcoder = wrapper->getTranscoder()) {
121 const char* kCodecName = (codec == transcoder->mDecoder ? "Decoder" : "Encoder");
122 LOG(DEBUG) << kCodecName << " format changed: " << AMediaFormat_toString(format);
123 if (codec == transcoder->mEncoder->getCodec()) {
124 transcoder->mCodecMessageQueue.push(
125 [transcoder, format] { transcoder->updateTrackFormat(format); });
126 }
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700127 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800128 }
129
130 static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t error,
131 int32_t actionCode, const char* detail) {
132 LOG(ERROR) << "Error from codec " << codec << ", userdata " << userdata << ", error "
133 << error << ", action " << actionCode << ", detail " << detail;
Linus Nilssone4716f22020-07-10 16:07:57 -0700134 VideoTrackTranscoder::CodecWrapper* wrapper =
135 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
136 if (auto transcoder = wrapper->getTranscoder()) {
137 transcoder->mCodecMessageQueue.push(
138 [transcoder, error] {
139 transcoder->mStatus = error;
140 transcoder->mStopRequested = true;
141 },
142 true);
143 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800144 }
145};
146
Linus Nilssone4716f22020-07-10 16:07:57 -0700147// static
148std::shared_ptr<VideoTrackTranscoder> VideoTrackTranscoder::create(
149 const std::weak_ptr<MediaTrackTranscoderCallback>& transcoderCallback) {
150 return std::shared_ptr<VideoTrackTranscoder>(new VideoTrackTranscoder(transcoderCallback));
151}
152
Linus Nilsson0da327a2020-01-31 16:22:18 -0800153VideoTrackTranscoder::~VideoTrackTranscoder() {
154 if (mDecoder != nullptr) {
155 AMediaCodec_delete(mDecoder);
156 }
157
Linus Nilsson0da327a2020-01-31 16:22:18 -0800158 if (mSurface != nullptr) {
159 ANativeWindow_release(mSurface);
160 }
161}
162
163// Creates and configures the codecs.
164media_status_t VideoTrackTranscoder::configureDestinationFormat(
165 const std::shared_ptr<AMediaFormat>& destinationFormat) {
166 media_status_t status = AMEDIA_OK;
167
168 if (destinationFormat == nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700169 LOG(ERROR) << "Destination format is null, use passthrough transcoder";
Linus Nilsson0da327a2020-01-31 16:22:18 -0800170 return AMEDIA_ERROR_INVALID_PARAMETER;
171 }
172
Linus Nilssoncab39d82020-05-14 16:32:21 -0700173 AMediaFormat* encoderFormat = AMediaFormat_new();
174 if (!encoderFormat || AMediaFormat_copy(encoderFormat, destinationFormat.get()) != AMEDIA_OK) {
175 LOG(ERROR) << "Unable to copy destination format";
176 return AMEDIA_ERROR_INVALID_PARAMETER;
177 }
178
179 float tmp;
180 if (!AMediaFormat_getFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, &tmp)) {
181 AMediaFormat_setFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
182 kDefaultKeyFrameIntervalSeconds);
183 }
184 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
185
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700186 // Always encode without rotation. The rotation degree will be transferred directly to
187 // MediaSampleWriter track format, and MediaSampleWriter will call AMediaMuxer_setOrientationHint.
188 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_ROTATION, 0);
189
Linus Nilssoncab39d82020-05-14 16:32:21 -0700190 mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800191
192 // Create and configure the encoder.
193 const char* destinationMime = nullptr;
194 bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
195 &destinationMime);
196 if (!ok) {
197 LOG(ERROR) << "Destination MIME type is required for transcoding.";
198 return AMEDIA_ERROR_INVALID_PARAMETER;
199 }
200
Linus Nilssonc6221db2020-03-18 14:46:22 -0700201 AMediaCodec* encoder = AMediaCodec_createEncoderByType(destinationMime);
202 if (encoder == nullptr) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800203 LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
204 return AMEDIA_ERROR_UNSUPPORTED;
205 }
Linus Nilssone4716f22020-07-10 16:07:57 -0700206 mEncoder = std::make_shared<CodecWrapper>(encoder, shared_from_this());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800207
Linus Nilssone4716f22020-07-10 16:07:57 -0700208 status = AMediaCodec_configure(mEncoder->getCodec(), mDestinationFormat.get(),
209 NULL /* surface */, NULL /* crypto */,
210 AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800211 if (status != AMEDIA_OK) {
212 LOG(ERROR) << "Unable to configure video encoder: " << status;
213 return status;
214 }
215
Linus Nilssone4716f22020-07-10 16:07:57 -0700216 status = AMediaCodec_createInputSurface(mEncoder->getCodec(), &mSurface);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800217 if (status != AMEDIA_OK) {
218 LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
219 return status;
220 }
221
222 // Create and configure the decoder.
223 const char* sourceMime = nullptr;
224 ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
225 if (!ok) {
226 LOG(ERROR) << "Source MIME type is required for transcoding.";
227 return AMEDIA_ERROR_INVALID_PARAMETER;
228 }
229
230 mDecoder = AMediaCodec_createDecoderByType(sourceMime);
231 if (mDecoder == nullptr) {
232 LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
233 return AMEDIA_ERROR_UNSUPPORTED;
234 }
235
236 status = AMediaCodec_configure(mDecoder, mSourceFormat.get(), mSurface, NULL /* crypto */,
237 0 /* flags */);
238 if (status != AMEDIA_OK) {
239 LOG(ERROR) << "Unable to configure video decoder: " << status;
240 return status;
241 }
242
243 // Configure codecs to run in async mode.
244 AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
245 .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
246 .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
247 .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
248 .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
249
Linus Nilssone4716f22020-07-10 16:07:57 -0700250 // Note: The decoder does not need its own wrapper because its lifetime is tied to the
251 // transcoder. But the same callbacks are reused for decoder and encoder so we pass the encoder
252 // wrapper as userdata here but never read the codec from it in the callback.
253 status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800254 if (status != AMEDIA_OK) {
255 LOG(ERROR) << "Unable to set decoder to async mode: " << status;
256 return status;
257 }
258
Linus Nilssone4716f22020-07-10 16:07:57 -0700259 status = AMediaCodec_setAsyncNotifyCallback(mEncoder->getCodec(), asyncCodecCallbacks,
260 mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800261 if (status != AMEDIA_OK) {
262 LOG(ERROR) << "Unable to set encoder to async mode: " << status;
263 return status;
264 }
265
266 return AMEDIA_OK;
267}
268
269void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
270 media_status_t status = AMEDIA_OK;
271
Linus Nilssonc6221db2020-03-18 14:46:22 -0700272 if (mEosFromSource) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800273 return;
274 }
275
276 status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
277 if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
278 LOG(ERROR) << "Error getting next sample info: " << status;
279 mStatus = status;
280 return;
281 }
282 const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
283
284 if (!endOfStream) {
285 size_t bufferSize = 0;
286 uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
287 if (sourceBuffer == nullptr) {
288 LOG(ERROR) << "Decoder returned a NULL input buffer.";
289 mStatus = AMEDIA_ERROR_UNKNOWN;
290 return;
291 } else if (bufferSize < mSampleInfo.size) {
292 LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
293 mStatus = AMEDIA_ERROR_UNKNOWN;
294 return;
295 }
296
297 status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
298 mSampleInfo.size);
299 if (status != AMEDIA_OK) {
300 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
301 mStatus = status;
302 return;
303 }
304
305 mMediaSampleReader->advanceTrack(mTrackIndex);
306 } else {
307 LOG(DEBUG) << "EOS from source.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700308 mEosFromSource = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800309 }
310
311 status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
312 mSampleInfo.presentationTimeUs, mSampleInfo.flags);
313 if (status != AMEDIA_OK) {
314 LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
315 mStatus = status;
316 return;
317 }
318}
319
320void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
321 if (bufferIndex >= 0) {
322 bool needsRender = bufferInfo.size > 0;
323 AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
324 }
325
326 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
327 LOG(DEBUG) << "EOS from decoder.";
Linus Nilssone4716f22020-07-10 16:07:57 -0700328 media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder->getCodec());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800329 if (status != AMEDIA_OK) {
330 LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
331 mStatus = status;
332 }
333 }
334}
335
336void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
337 AMediaCodecBufferInfo bufferInfo) {
338 if (bufferIndex >= 0) {
339 size_t sampleSize = 0;
Linus Nilssone4716f22020-07-10 16:07:57 -0700340 uint8_t* buffer =
341 AMediaCodec_getOutputBuffer(mEncoder->getCodec(), bufferIndex, &sampleSize);
Linus Nilssonc6221db2020-03-18 14:46:22 -0700342
Linus Nilssone4716f22020-07-10 16:07:57 -0700343 MediaSample::OnSampleReleasedCallback bufferReleaseCallback =
344 [encoder = mEncoder](MediaSample* sample) {
345 AMediaCodec_releaseOutputBuffer(encoder->getCodec(), sample->bufferId,
346 false /* render */);
347 };
Linus Nilsson0da327a2020-01-31 16:22:18 -0800348
349 std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
Linus Nilssonc6221db2020-03-18 14:46:22 -0700350 buffer, bufferInfo.offset, bufferIndex, bufferReleaseCallback);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800351 sample->info.size = bufferInfo.size;
352 sample->info.flags = bufferInfo.flags;
353 sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
354
Linus Nilssoncab39d82020-05-14 16:32:21 -0700355 const bool aborted = mOutputQueue->enqueue(sample);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800356 if (aborted) {
357 LOG(ERROR) << "Output sample queue was aborted. Stopping transcode.";
358 mStatus = AMEDIA_ERROR_IO; // TODO: Define custom error codes?
359 return;
360 }
361 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700362 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mEncoder->getCodec());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800363 LOG(DEBUG) << "Encoder output format changed: " << AMediaFormat_toString(newFormat);
364 }
365
366 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
367 LOG(DEBUG) << "EOS from encoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700368 mEosFromEncoder = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800369 }
370}
371
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700372void VideoTrackTranscoder::updateTrackFormat(AMediaFormat* outputFormat) {
373 if (mActualOutputFormat != nullptr) {
374 LOG(WARNING) << "Ignoring duplicate format change.";
375 return;
376 }
377
378 AMediaFormat* formatCopy = AMediaFormat_new();
379 if (!formatCopy || AMediaFormat_copy(formatCopy, outputFormat) != AMEDIA_OK) {
380 LOG(ERROR) << "Unable to copy outputFormat";
381 AMediaFormat_delete(formatCopy);
382 mStatus = AMEDIA_ERROR_INVALID_PARAMETER;
383 return;
384 }
385
386 // Generate the actual track format for muxer based on the encoder output format,
387 // since many vital information comes in the encoder format (eg. CSD).
388 // Transfer necessary fields from the user-configured track format (derived from
389 // source track format and user transcoding request) where needed.
390
391 // Transfer SAR settings:
392 // If mDestinationFormat has SAR set, it means the original source has SAR specified
393 // at container level. This is supposed to override any SAR settings in the bitstream,
394 // thus should always be transferred to the container of the transcoded file.
395 int32_t sarWidth, sarHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700396 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_WIDTH, &sarWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700397 (sarWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700398 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_HEIGHT, &sarHeight) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700399 (sarHeight > 0)) {
400 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
401 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
402 }
403 // Transfer DAR settings.
404 int32_t displayWidth, displayHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700405 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_WIDTH, &displayWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700406 (displayWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700407 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700408 &displayHeight) &&
409 (displayHeight > 0)) {
410 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, displayWidth);
411 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, displayHeight);
412 }
413
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700414 // Transfer rotation settings.
415 // Note that muxer itself doesn't take rotation from the track format. It requires
416 // AMediaMuxer_setOrientationHint to set the rotation. Here we pass the rotation to
417 // MediaSampleWriter using the track format. MediaSampleWriter will then call
418 // AMediaMuxer_setOrientationHint as needed.
419 int32_t rotation;
420 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_ROTATION, &rotation) &&
421 (rotation != 0)) {
422 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_ROTATION, rotation);
423 }
424
Linus Nilsson42a971b2020-07-01 16:41:11 -0700425 // Transfer track duration.
426 // Preserve the source track duration by sending it to MediaSampleWriter.
427 int64_t durationUs;
428 if (AMediaFormat_getInt64(mSourceFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs) &&
429 durationUs > 0) {
430 AMediaFormat_setInt64(formatCopy, AMEDIAFORMAT_KEY_DURATION, durationUs);
431 }
432
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700433 // TODO: transfer other fields as required.
434
435 mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
436
437 notifyTrackFormatAvailable();
438}
439
Linus Nilsson0da327a2020-01-31 16:22:18 -0800440media_status_t VideoTrackTranscoder::runTranscodeLoop() {
Linus Nilssonb09aac22020-07-29 11:56:53 -0700441 androidSetThreadPriority(0 /* tid (0 = current) */, ANDROID_PRIORITY_VIDEO);
442
Chong Zhangb55c5452020-06-26 14:32:12 -0700443 // Push start decoder and encoder as two messages, so that these are subject to the
444 // stop request as well. If the job is cancelled (or paused) immediately after start,
445 // we don't need to waste time start then stop the codecs.
446 mCodecMessageQueue.push([this] {
447 media_status_t status = AMediaCodec_start(mDecoder);
448 if (status != AMEDIA_OK) {
449 LOG(ERROR) << "Unable to start video decoder: " << status;
450 mStatus = status;
451 }
452 });
Linus Nilsson0da327a2020-01-31 16:22:18 -0800453
Chong Zhangb55c5452020-06-26 14:32:12 -0700454 mCodecMessageQueue.push([this] {
Linus Nilssone4716f22020-07-10 16:07:57 -0700455 media_status_t status = AMediaCodec_start(mEncoder->getCodec());
Chong Zhangb55c5452020-06-26 14:32:12 -0700456 if (status != AMEDIA_OK) {
457 LOG(ERROR) << "Unable to start video encoder: " << status;
458 mStatus = status;
459 }
Linus Nilssone4716f22020-07-10 16:07:57 -0700460 mEncoder->setStarted();
Chong Zhangb55c5452020-06-26 14:32:12 -0700461 });
Linus Nilsson0da327a2020-01-31 16:22:18 -0800462
463 // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700464 while (!mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800465 std::function<void()> message = mCodecMessageQueue.pop();
466 message();
467 }
468
469 // Return error if transcoding was stopped before it finished.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700470 if (mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800471 mStatus = AMEDIA_ERROR_UNKNOWN; // TODO: Define custom error codes?
472 }
473
474 AMediaCodec_stop(mDecoder);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800475 return mStatus;
476}
477
478void VideoTrackTranscoder::abortTranscodeLoop() {
479 // Push abort message to the front of the codec event queue.
480 mCodecMessageQueue.push([this] { mStopRequested = true; }, true /* front */);
481}
482
Linus Nilssoncab39d82020-05-14 16:32:21 -0700483std::shared_ptr<AMediaFormat> VideoTrackTranscoder::getOutputFormat() const {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700484 return mActualOutputFormat;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700485}
486
Linus Nilsson0da327a2020-01-31 16:22:18 -0800487} // namespace android