blob: e192d2a8365c26999b5e481f16cb060cedb2c7d9 [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>
22
23namespace android {
24
25// Check that the codec sample flags have the expected NDK meaning.
26static_assert(SAMPLE_FLAG_CODEC_CONFIG == AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG,
27 "Sample flag mismatch: CODEC_CONFIG");
28static_assert(SAMPLE_FLAG_END_OF_STREAM == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM,
29 "Sample flag mismatch: END_OF_STREAM");
30static_assert(SAMPLE_FLAG_PARTIAL_FRAME == AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME,
31 "Sample flag mismatch: PARTIAL_FRAME");
32
Linus Nilssoncab39d82020-05-14 16:32:21 -070033// Color format defined by surface. (See MediaCodecInfo.CodecCapabilities#COLOR_FormatSurface.)
34static constexpr int32_t kColorFormatSurface = 0x7f000789;
35// Default key frame interval in seconds.
36static constexpr float kDefaultKeyFrameIntervalSeconds = 1.0f;
37
Linus Nilsson0da327a2020-01-31 16:22:18 -080038template <typename T>
39void VideoTrackTranscoder::BlockingQueue<T>::push(T const& value, bool front) {
40 {
41 std::unique_lock<std::mutex> lock(mMutex);
42 if (front) {
43 mQueue.push_front(value);
44 } else {
45 mQueue.push_back(value);
46 }
47 }
48 mCondition.notify_one();
49}
50
51template <typename T>
52T VideoTrackTranscoder::BlockingQueue<T>::pop() {
53 std::unique_lock<std::mutex> lock(mMutex);
54 while (mQueue.empty()) {
55 mCondition.wait(lock);
56 }
57 T value = mQueue.front();
58 mQueue.pop_front();
59 return value;
60}
61
Linus Nilssone4716f22020-07-10 16:07:57 -070062// The CodecWrapper class is used to let AMediaCodec instances outlive the transcoder object itself
63// by giving the codec a weak pointer to the transcoder. Codecs wrapped in this object are kept
64// alive by the transcoder and the codec's outstanding buffers. Once the transcoder stops and all
65// output buffers have been released by downstream components the codec will also be released.
66class VideoTrackTranscoder::CodecWrapper {
67public:
68 CodecWrapper(AMediaCodec* codec, const std::weak_ptr<VideoTrackTranscoder>& transcoder)
69 : mCodec(codec), mTranscoder(transcoder), mCodecStarted(false) {}
70 ~CodecWrapper() {
71 if (mCodecStarted) {
72 AMediaCodec_stop(mCodec);
73 }
74 AMediaCodec_delete(mCodec);
75 }
76
77 AMediaCodec* getCodec() { return mCodec; }
78 std::shared_ptr<VideoTrackTranscoder> getTranscoder() const { return mTranscoder.lock(); };
79 void setStarted() { mCodecStarted = true; }
80
81private:
82 AMediaCodec* mCodec;
83 std::weak_ptr<VideoTrackTranscoder> mTranscoder;
84 bool mCodecStarted;
85};
86
Linus Nilsson0da327a2020-01-31 16:22:18 -080087// Dispatch responses to codec callbacks onto the message queue.
88struct AsyncCodecCallbackDispatch {
89 static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
Linus Nilssone4716f22020-07-10 16:07:57 -070090 VideoTrackTranscoder::CodecWrapper* wrapper =
91 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
92 if (auto transcoder = wrapper->getTranscoder()) {
93 if (codec == transcoder->mDecoder) {
94 transcoder->mCodecMessageQueue.push(
95 [transcoder, index] { transcoder->enqueueInputSample(index); });
96 }
Linus Nilsson0da327a2020-01-31 16:22:18 -080097 }
98 }
99
100 static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
101 AMediaCodecBufferInfo* bufferInfoPtr) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700102 VideoTrackTranscoder::CodecWrapper* wrapper =
103 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800104 AMediaCodecBufferInfo bufferInfo = *bufferInfoPtr;
Linus Nilssone4716f22020-07-10 16:07:57 -0700105 if (auto transcoder = wrapper->getTranscoder()) {
106 transcoder->mCodecMessageQueue.push([transcoder, index, codec, bufferInfo] {
107 if (codec == transcoder->mDecoder) {
108 transcoder->transferBuffer(index, bufferInfo);
109 } else if (codec == transcoder->mEncoder->getCodec()) {
110 transcoder->dequeueOutputSample(index, bufferInfo);
111 }
112 });
113 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800114 }
115
116 static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700117 VideoTrackTranscoder::CodecWrapper* wrapper =
118 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
119 if (auto transcoder = wrapper->getTranscoder()) {
120 const char* kCodecName = (codec == transcoder->mDecoder ? "Decoder" : "Encoder");
121 LOG(DEBUG) << kCodecName << " format changed: " << AMediaFormat_toString(format);
122 if (codec == transcoder->mEncoder->getCodec()) {
123 transcoder->mCodecMessageQueue.push(
124 [transcoder, format] { transcoder->updateTrackFormat(format); });
125 }
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700126 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800127 }
128
129 static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t error,
130 int32_t actionCode, const char* detail) {
131 LOG(ERROR) << "Error from codec " << codec << ", userdata " << userdata << ", error "
132 << error << ", action " << actionCode << ", detail " << detail;
Linus Nilssone4716f22020-07-10 16:07:57 -0700133 VideoTrackTranscoder::CodecWrapper* wrapper =
134 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
135 if (auto transcoder = wrapper->getTranscoder()) {
136 transcoder->mCodecMessageQueue.push(
137 [transcoder, error] {
138 transcoder->mStatus = error;
139 transcoder->mStopRequested = true;
140 },
141 true);
142 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800143 }
144};
145
Linus Nilssone4716f22020-07-10 16:07:57 -0700146// static
147std::shared_ptr<VideoTrackTranscoder> VideoTrackTranscoder::create(
148 const std::weak_ptr<MediaTrackTranscoderCallback>& transcoderCallback) {
149 return std::shared_ptr<VideoTrackTranscoder>(new VideoTrackTranscoder(transcoderCallback));
150}
151
Linus Nilsson0da327a2020-01-31 16:22:18 -0800152VideoTrackTranscoder::~VideoTrackTranscoder() {
153 if (mDecoder != nullptr) {
154 AMediaCodec_delete(mDecoder);
155 }
156
Linus Nilsson0da327a2020-01-31 16:22:18 -0800157 if (mSurface != nullptr) {
158 ANativeWindow_release(mSurface);
159 }
160}
161
162// Creates and configures the codecs.
163media_status_t VideoTrackTranscoder::configureDestinationFormat(
164 const std::shared_ptr<AMediaFormat>& destinationFormat) {
Linus Nilsson800793f2020-07-31 16:16:38 -0700165 static constexpr int32_t kDefaultBitrateMbps = 10 * 1000 * 1000;
166
Linus Nilsson0da327a2020-01-31 16:22:18 -0800167 media_status_t status = AMEDIA_OK;
168
169 if (destinationFormat == nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700170 LOG(ERROR) << "Destination format is null, use passthrough transcoder";
Linus Nilsson0da327a2020-01-31 16:22:18 -0800171 return AMEDIA_ERROR_INVALID_PARAMETER;
172 }
173
Linus Nilssoncab39d82020-05-14 16:32:21 -0700174 AMediaFormat* encoderFormat = AMediaFormat_new();
175 if (!encoderFormat || AMediaFormat_copy(encoderFormat, destinationFormat.get()) != AMEDIA_OK) {
176 LOG(ERROR) << "Unable to copy destination format";
177 return AMEDIA_ERROR_INVALID_PARAMETER;
178 }
179
Linus Nilsson800793f2020-07-31 16:16:38 -0700180 int32_t bitrate;
181 if (!AMediaFormat_getInt32(encoderFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrate)) {
182 status = mMediaSampleReader->getEstimatedBitrateForTrack(mTrackIndex, &bitrate);
183 if (status != AMEDIA_OK) {
184 LOG(ERROR) << "Unable to estimate bitrate. Using default " << kDefaultBitrateMbps;
185 bitrate = kDefaultBitrateMbps;
186 }
187
188 LOG(INFO) << "Configuring bitrate " << bitrate;
189 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_BIT_RATE, bitrate);
190 }
191
Linus Nilssoncab39d82020-05-14 16:32:21 -0700192 float tmp;
193 if (!AMediaFormat_getFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, &tmp)) {
194 AMediaFormat_setFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
195 kDefaultKeyFrameIntervalSeconds);
196 }
197 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
198
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700199 // Always encode without rotation. The rotation degree will be transferred directly to
200 // MediaSampleWriter track format, and MediaSampleWriter will call AMediaMuxer_setOrientationHint.
201 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_ROTATION, 0);
202
Linus Nilssoncab39d82020-05-14 16:32:21 -0700203 mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800204
205 // Create and configure the encoder.
206 const char* destinationMime = nullptr;
207 bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
208 &destinationMime);
209 if (!ok) {
210 LOG(ERROR) << "Destination MIME type is required for transcoding.";
211 return AMEDIA_ERROR_INVALID_PARAMETER;
212 }
213
Linus Nilssonc6221db2020-03-18 14:46:22 -0700214 AMediaCodec* encoder = AMediaCodec_createEncoderByType(destinationMime);
215 if (encoder == nullptr) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800216 LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
217 return AMEDIA_ERROR_UNSUPPORTED;
218 }
Linus Nilssone4716f22020-07-10 16:07:57 -0700219 mEncoder = std::make_shared<CodecWrapper>(encoder, shared_from_this());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800220
Linus Nilssone4716f22020-07-10 16:07:57 -0700221 status = AMediaCodec_configure(mEncoder->getCodec(), mDestinationFormat.get(),
222 NULL /* surface */, NULL /* crypto */,
223 AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800224 if (status != AMEDIA_OK) {
225 LOG(ERROR) << "Unable to configure video encoder: " << status;
226 return status;
227 }
228
Linus Nilssone4716f22020-07-10 16:07:57 -0700229 status = AMediaCodec_createInputSurface(mEncoder->getCodec(), &mSurface);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800230 if (status != AMEDIA_OK) {
231 LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
232 return status;
233 }
234
235 // Create and configure the decoder.
236 const char* sourceMime = nullptr;
237 ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
238 if (!ok) {
239 LOG(ERROR) << "Source MIME type is required for transcoding.";
240 return AMEDIA_ERROR_INVALID_PARAMETER;
241 }
242
243 mDecoder = AMediaCodec_createDecoderByType(sourceMime);
244 if (mDecoder == nullptr) {
245 LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
246 return AMEDIA_ERROR_UNSUPPORTED;
247 }
248
249 status = AMediaCodec_configure(mDecoder, mSourceFormat.get(), mSurface, NULL /* crypto */,
250 0 /* flags */);
251 if (status != AMEDIA_OK) {
252 LOG(ERROR) << "Unable to configure video decoder: " << status;
253 return status;
254 }
255
256 // Configure codecs to run in async mode.
257 AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
258 .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
259 .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
260 .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
261 .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
262
Linus Nilssone4716f22020-07-10 16:07:57 -0700263 // Note: The decoder does not need its own wrapper because its lifetime is tied to the
264 // transcoder. But the same callbacks are reused for decoder and encoder so we pass the encoder
265 // wrapper as userdata here but never read the codec from it in the callback.
266 status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800267 if (status != AMEDIA_OK) {
268 LOG(ERROR) << "Unable to set decoder to async mode: " << status;
269 return status;
270 }
271
Linus Nilssone4716f22020-07-10 16:07:57 -0700272 status = AMediaCodec_setAsyncNotifyCallback(mEncoder->getCodec(), asyncCodecCallbacks,
273 mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800274 if (status != AMEDIA_OK) {
275 LOG(ERROR) << "Unable to set encoder to async mode: " << status;
276 return status;
277 }
278
279 return AMEDIA_OK;
280}
281
282void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
283 media_status_t status = AMEDIA_OK;
284
Linus Nilssonc6221db2020-03-18 14:46:22 -0700285 if (mEosFromSource) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800286 return;
287 }
288
289 status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
290 if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
291 LOG(ERROR) << "Error getting next sample info: " << status;
292 mStatus = status;
293 return;
294 }
295 const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
296
297 if (!endOfStream) {
298 size_t bufferSize = 0;
299 uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
300 if (sourceBuffer == nullptr) {
301 LOG(ERROR) << "Decoder returned a NULL input buffer.";
302 mStatus = AMEDIA_ERROR_UNKNOWN;
303 return;
304 } else if (bufferSize < mSampleInfo.size) {
305 LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
306 mStatus = AMEDIA_ERROR_UNKNOWN;
307 return;
308 }
309
310 status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
311 mSampleInfo.size);
312 if (status != AMEDIA_OK) {
313 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
314 mStatus = status;
315 return;
316 }
317
318 mMediaSampleReader->advanceTrack(mTrackIndex);
319 } else {
320 LOG(DEBUG) << "EOS from source.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700321 mEosFromSource = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800322 }
323
324 status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
325 mSampleInfo.presentationTimeUs, mSampleInfo.flags);
326 if (status != AMEDIA_OK) {
327 LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
328 mStatus = status;
329 return;
330 }
331}
332
333void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
334 if (bufferIndex >= 0) {
335 bool needsRender = bufferInfo.size > 0;
336 AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
337 }
338
339 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
340 LOG(DEBUG) << "EOS from decoder.";
Linus Nilssone4716f22020-07-10 16:07:57 -0700341 media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder->getCodec());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800342 if (status != AMEDIA_OK) {
343 LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
344 mStatus = status;
345 }
346 }
347}
348
349void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
350 AMediaCodecBufferInfo bufferInfo) {
351 if (bufferIndex >= 0) {
352 size_t sampleSize = 0;
Linus Nilssone4716f22020-07-10 16:07:57 -0700353 uint8_t* buffer =
354 AMediaCodec_getOutputBuffer(mEncoder->getCodec(), bufferIndex, &sampleSize);
Linus Nilssonc6221db2020-03-18 14:46:22 -0700355
Linus Nilssone4716f22020-07-10 16:07:57 -0700356 MediaSample::OnSampleReleasedCallback bufferReleaseCallback =
357 [encoder = mEncoder](MediaSample* sample) {
358 AMediaCodec_releaseOutputBuffer(encoder->getCodec(), sample->bufferId,
359 false /* render */);
360 };
Linus Nilsson0da327a2020-01-31 16:22:18 -0800361
362 std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
Linus Nilssonc6221db2020-03-18 14:46:22 -0700363 buffer, bufferInfo.offset, bufferIndex, bufferReleaseCallback);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800364 sample->info.size = bufferInfo.size;
365 sample->info.flags = bufferInfo.flags;
366 sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
367
Linus Nilssoncab39d82020-05-14 16:32:21 -0700368 const bool aborted = mOutputQueue->enqueue(sample);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800369 if (aborted) {
370 LOG(ERROR) << "Output sample queue was aborted. Stopping transcode.";
371 mStatus = AMEDIA_ERROR_IO; // TODO: Define custom error codes?
372 return;
373 }
374 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700375 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mEncoder->getCodec());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800376 LOG(DEBUG) << "Encoder output format changed: " << AMediaFormat_toString(newFormat);
377 }
378
379 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
380 LOG(DEBUG) << "EOS from encoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700381 mEosFromEncoder = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800382 }
383}
384
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700385void VideoTrackTranscoder::updateTrackFormat(AMediaFormat* outputFormat) {
386 if (mActualOutputFormat != nullptr) {
387 LOG(WARNING) << "Ignoring duplicate format change.";
388 return;
389 }
390
391 AMediaFormat* formatCopy = AMediaFormat_new();
392 if (!formatCopy || AMediaFormat_copy(formatCopy, outputFormat) != AMEDIA_OK) {
393 LOG(ERROR) << "Unable to copy outputFormat";
394 AMediaFormat_delete(formatCopy);
395 mStatus = AMEDIA_ERROR_INVALID_PARAMETER;
396 return;
397 }
398
399 // Generate the actual track format for muxer based on the encoder output format,
400 // since many vital information comes in the encoder format (eg. CSD).
401 // Transfer necessary fields from the user-configured track format (derived from
402 // source track format and user transcoding request) where needed.
403
404 // Transfer SAR settings:
405 // If mDestinationFormat has SAR set, it means the original source has SAR specified
406 // at container level. This is supposed to override any SAR settings in the bitstream,
407 // thus should always be transferred to the container of the transcoded file.
408 int32_t sarWidth, sarHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700409 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_WIDTH, &sarWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700410 (sarWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700411 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_HEIGHT, &sarHeight) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700412 (sarHeight > 0)) {
413 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
414 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
415 }
416 // Transfer DAR settings.
417 int32_t displayWidth, displayHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700418 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_WIDTH, &displayWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700419 (displayWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700420 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700421 &displayHeight) &&
422 (displayHeight > 0)) {
423 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, displayWidth);
424 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, displayHeight);
425 }
426
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700427 // Transfer rotation settings.
428 // Note that muxer itself doesn't take rotation from the track format. It requires
429 // AMediaMuxer_setOrientationHint to set the rotation. Here we pass the rotation to
430 // MediaSampleWriter using the track format. MediaSampleWriter will then call
431 // AMediaMuxer_setOrientationHint as needed.
432 int32_t rotation;
433 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_ROTATION, &rotation) &&
434 (rotation != 0)) {
435 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_ROTATION, rotation);
436 }
437
Linus Nilsson42a971b2020-07-01 16:41:11 -0700438 // Transfer track duration.
439 // Preserve the source track duration by sending it to MediaSampleWriter.
440 int64_t durationUs;
441 if (AMediaFormat_getInt64(mSourceFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs) &&
442 durationUs > 0) {
443 AMediaFormat_setInt64(formatCopy, AMEDIAFORMAT_KEY_DURATION, durationUs);
444 }
445
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700446 // TODO: transfer other fields as required.
447
448 mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
449
450 notifyTrackFormatAvailable();
451}
452
Linus Nilsson0da327a2020-01-31 16:22:18 -0800453media_status_t VideoTrackTranscoder::runTranscodeLoop() {
Chong Zhangb55c5452020-06-26 14:32:12 -0700454 // Push start decoder and encoder as two messages, so that these are subject to the
455 // stop request as well. If the job is cancelled (or paused) immediately after start,
456 // we don't need to waste time start then stop the codecs.
457 mCodecMessageQueue.push([this] {
458 media_status_t status = AMediaCodec_start(mDecoder);
459 if (status != AMEDIA_OK) {
460 LOG(ERROR) << "Unable to start video decoder: " << status;
461 mStatus = status;
462 }
463 });
Linus Nilsson0da327a2020-01-31 16:22:18 -0800464
Chong Zhangb55c5452020-06-26 14:32:12 -0700465 mCodecMessageQueue.push([this] {
Linus Nilssone4716f22020-07-10 16:07:57 -0700466 media_status_t status = AMediaCodec_start(mEncoder->getCodec());
Chong Zhangb55c5452020-06-26 14:32:12 -0700467 if (status != AMEDIA_OK) {
468 LOG(ERROR) << "Unable to start video encoder: " << status;
469 mStatus = status;
470 }
Linus Nilssone4716f22020-07-10 16:07:57 -0700471 mEncoder->setStarted();
Chong Zhangb55c5452020-06-26 14:32:12 -0700472 });
Linus Nilsson0da327a2020-01-31 16:22:18 -0800473
474 // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700475 while (!mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800476 std::function<void()> message = mCodecMessageQueue.pop();
477 message();
478 }
479
480 // Return error if transcoding was stopped before it finished.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700481 if (mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800482 mStatus = AMEDIA_ERROR_UNKNOWN; // TODO: Define custom error codes?
483 }
484
485 AMediaCodec_stop(mDecoder);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800486 return mStatus;
487}
488
489void VideoTrackTranscoder::abortTranscodeLoop() {
490 // Push abort message to the front of the codec event queue.
491 mCodecMessageQueue.push([this] { mStopRequested = true; }, true /* front */);
492}
493
Linus Nilssoncab39d82020-05-14 16:32:21 -0700494std::shared_ptr<AMediaFormat> VideoTrackTranscoder::getOutputFormat() const {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700495 return mActualOutputFormat;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700496}
497
Linus Nilsson0da327a2020-01-31 16:22:18 -0800498} // namespace android