blob: 65dcad3d52326a04482114fe3127cdb8971794e2 [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) {
165 media_status_t status = AMEDIA_OK;
166
167 if (destinationFormat == nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700168 LOG(ERROR) << "Destination format is null, use passthrough transcoder";
Linus Nilsson0da327a2020-01-31 16:22:18 -0800169 return AMEDIA_ERROR_INVALID_PARAMETER;
170 }
171
Linus Nilssoncab39d82020-05-14 16:32:21 -0700172 AMediaFormat* encoderFormat = AMediaFormat_new();
173 if (!encoderFormat || AMediaFormat_copy(encoderFormat, destinationFormat.get()) != AMEDIA_OK) {
174 LOG(ERROR) << "Unable to copy destination format";
175 return AMEDIA_ERROR_INVALID_PARAMETER;
176 }
177
178 float tmp;
179 if (!AMediaFormat_getFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, &tmp)) {
180 AMediaFormat_setFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
181 kDefaultKeyFrameIntervalSeconds);
182 }
183 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
184
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700185 // Always encode without rotation. The rotation degree will be transferred directly to
186 // MediaSampleWriter track format, and MediaSampleWriter will call AMediaMuxer_setOrientationHint.
187 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_ROTATION, 0);
188
Linus Nilssoncab39d82020-05-14 16:32:21 -0700189 mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800190
191 // Create and configure the encoder.
192 const char* destinationMime = nullptr;
193 bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
194 &destinationMime);
195 if (!ok) {
196 LOG(ERROR) << "Destination MIME type is required for transcoding.";
197 return AMEDIA_ERROR_INVALID_PARAMETER;
198 }
199
Linus Nilssonc6221db2020-03-18 14:46:22 -0700200 AMediaCodec* encoder = AMediaCodec_createEncoderByType(destinationMime);
201 if (encoder == nullptr) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800202 LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
203 return AMEDIA_ERROR_UNSUPPORTED;
204 }
Linus Nilssone4716f22020-07-10 16:07:57 -0700205 mEncoder = std::make_shared<CodecWrapper>(encoder, shared_from_this());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800206
Linus Nilssone4716f22020-07-10 16:07:57 -0700207 status = AMediaCodec_configure(mEncoder->getCodec(), mDestinationFormat.get(),
208 NULL /* surface */, NULL /* crypto */,
209 AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800210 if (status != AMEDIA_OK) {
211 LOG(ERROR) << "Unable to configure video encoder: " << status;
212 return status;
213 }
214
Linus Nilssone4716f22020-07-10 16:07:57 -0700215 status = AMediaCodec_createInputSurface(mEncoder->getCodec(), &mSurface);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800216 if (status != AMEDIA_OK) {
217 LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
218 return status;
219 }
220
221 // Create and configure the decoder.
222 const char* sourceMime = nullptr;
223 ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
224 if (!ok) {
225 LOG(ERROR) << "Source MIME type is required for transcoding.";
226 return AMEDIA_ERROR_INVALID_PARAMETER;
227 }
228
229 mDecoder = AMediaCodec_createDecoderByType(sourceMime);
230 if (mDecoder == nullptr) {
231 LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
232 return AMEDIA_ERROR_UNSUPPORTED;
233 }
234
235 status = AMediaCodec_configure(mDecoder, mSourceFormat.get(), mSurface, NULL /* crypto */,
236 0 /* flags */);
237 if (status != AMEDIA_OK) {
238 LOG(ERROR) << "Unable to configure video decoder: " << status;
239 return status;
240 }
241
242 // Configure codecs to run in async mode.
243 AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
244 .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
245 .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
246 .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
247 .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
248
Linus Nilssone4716f22020-07-10 16:07:57 -0700249 // Note: The decoder does not need its own wrapper because its lifetime is tied to the
250 // transcoder. But the same callbacks are reused for decoder and encoder so we pass the encoder
251 // wrapper as userdata here but never read the codec from it in the callback.
252 status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800253 if (status != AMEDIA_OK) {
254 LOG(ERROR) << "Unable to set decoder to async mode: " << status;
255 return status;
256 }
257
Linus Nilssone4716f22020-07-10 16:07:57 -0700258 status = AMediaCodec_setAsyncNotifyCallback(mEncoder->getCodec(), asyncCodecCallbacks,
259 mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800260 if (status != AMEDIA_OK) {
261 LOG(ERROR) << "Unable to set encoder to async mode: " << status;
262 return status;
263 }
264
265 return AMEDIA_OK;
266}
267
268void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
269 media_status_t status = AMEDIA_OK;
270
Linus Nilssonc6221db2020-03-18 14:46:22 -0700271 if (mEosFromSource) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800272 return;
273 }
274
275 status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
276 if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
277 LOG(ERROR) << "Error getting next sample info: " << status;
278 mStatus = status;
279 return;
280 }
281 const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
282
283 if (!endOfStream) {
284 size_t bufferSize = 0;
285 uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
286 if (sourceBuffer == nullptr) {
287 LOG(ERROR) << "Decoder returned a NULL input buffer.";
288 mStatus = AMEDIA_ERROR_UNKNOWN;
289 return;
290 } else if (bufferSize < mSampleInfo.size) {
291 LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
292 mStatus = AMEDIA_ERROR_UNKNOWN;
293 return;
294 }
295
296 status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
297 mSampleInfo.size);
298 if (status != AMEDIA_OK) {
299 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
300 mStatus = status;
301 return;
302 }
303
304 mMediaSampleReader->advanceTrack(mTrackIndex);
305 } else {
306 LOG(DEBUG) << "EOS from source.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700307 mEosFromSource = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800308 }
309
310 status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
311 mSampleInfo.presentationTimeUs, mSampleInfo.flags);
312 if (status != AMEDIA_OK) {
313 LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
314 mStatus = status;
315 return;
316 }
317}
318
319void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
320 if (bufferIndex >= 0) {
321 bool needsRender = bufferInfo.size > 0;
322 AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
323 }
324
325 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
326 LOG(DEBUG) << "EOS from decoder.";
Linus Nilssone4716f22020-07-10 16:07:57 -0700327 media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder->getCodec());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800328 if (status != AMEDIA_OK) {
329 LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
330 mStatus = status;
331 }
332 }
333}
334
335void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
336 AMediaCodecBufferInfo bufferInfo) {
337 if (bufferIndex >= 0) {
338 size_t sampleSize = 0;
Linus Nilssone4716f22020-07-10 16:07:57 -0700339 uint8_t* buffer =
340 AMediaCodec_getOutputBuffer(mEncoder->getCodec(), bufferIndex, &sampleSize);
Linus Nilssonc6221db2020-03-18 14:46:22 -0700341
Linus Nilssone4716f22020-07-10 16:07:57 -0700342 MediaSample::OnSampleReleasedCallback bufferReleaseCallback =
343 [encoder = mEncoder](MediaSample* sample) {
344 AMediaCodec_releaseOutputBuffer(encoder->getCodec(), sample->bufferId,
345 false /* render */);
346 };
Linus Nilsson0da327a2020-01-31 16:22:18 -0800347
348 std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
Linus Nilssonc6221db2020-03-18 14:46:22 -0700349 buffer, bufferInfo.offset, bufferIndex, bufferReleaseCallback);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800350 sample->info.size = bufferInfo.size;
351 sample->info.flags = bufferInfo.flags;
352 sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
353
Linus Nilssoncab39d82020-05-14 16:32:21 -0700354 const bool aborted = mOutputQueue->enqueue(sample);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800355 if (aborted) {
356 LOG(ERROR) << "Output sample queue was aborted. Stopping transcode.";
357 mStatus = AMEDIA_ERROR_IO; // TODO: Define custom error codes?
358 return;
359 }
360 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700361 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mEncoder->getCodec());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800362 LOG(DEBUG) << "Encoder output format changed: " << AMediaFormat_toString(newFormat);
363 }
364
365 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
366 LOG(DEBUG) << "EOS from encoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700367 mEosFromEncoder = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800368 }
369}
370
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700371void VideoTrackTranscoder::updateTrackFormat(AMediaFormat* outputFormat) {
372 if (mActualOutputFormat != nullptr) {
373 LOG(WARNING) << "Ignoring duplicate format change.";
374 return;
375 }
376
377 AMediaFormat* formatCopy = AMediaFormat_new();
378 if (!formatCopy || AMediaFormat_copy(formatCopy, outputFormat) != AMEDIA_OK) {
379 LOG(ERROR) << "Unable to copy outputFormat";
380 AMediaFormat_delete(formatCopy);
381 mStatus = AMEDIA_ERROR_INVALID_PARAMETER;
382 return;
383 }
384
385 // Generate the actual track format for muxer based on the encoder output format,
386 // since many vital information comes in the encoder format (eg. CSD).
387 // Transfer necessary fields from the user-configured track format (derived from
388 // source track format and user transcoding request) where needed.
389
390 // Transfer SAR settings:
391 // If mDestinationFormat has SAR set, it means the original source has SAR specified
392 // at container level. This is supposed to override any SAR settings in the bitstream,
393 // thus should always be transferred to the container of the transcoded file.
394 int32_t sarWidth, sarHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700395 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_WIDTH, &sarWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700396 (sarWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700397 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_HEIGHT, &sarHeight) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700398 (sarHeight > 0)) {
399 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
400 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
401 }
402 // Transfer DAR settings.
403 int32_t displayWidth, displayHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700404 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_WIDTH, &displayWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700405 (displayWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700406 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700407 &displayHeight) &&
408 (displayHeight > 0)) {
409 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, displayWidth);
410 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, displayHeight);
411 }
412
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700413 // Transfer rotation settings.
414 // Note that muxer itself doesn't take rotation from the track format. It requires
415 // AMediaMuxer_setOrientationHint to set the rotation. Here we pass the rotation to
416 // MediaSampleWriter using the track format. MediaSampleWriter will then call
417 // AMediaMuxer_setOrientationHint as needed.
418 int32_t rotation;
419 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_ROTATION, &rotation) &&
420 (rotation != 0)) {
421 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_ROTATION, rotation);
422 }
423
Linus Nilsson42a971b2020-07-01 16:41:11 -0700424 // Transfer track duration.
425 // Preserve the source track duration by sending it to MediaSampleWriter.
426 int64_t durationUs;
427 if (AMediaFormat_getInt64(mSourceFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs) &&
428 durationUs > 0) {
429 AMediaFormat_setInt64(formatCopy, AMEDIAFORMAT_KEY_DURATION, durationUs);
430 }
431
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700432 // TODO: transfer other fields as required.
433
434 mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
435
436 notifyTrackFormatAvailable();
437}
438
Linus Nilsson0da327a2020-01-31 16:22:18 -0800439media_status_t VideoTrackTranscoder::runTranscodeLoop() {
Chong Zhangb55c5452020-06-26 14:32:12 -0700440 // Push start decoder and encoder as two messages, so that these are subject to the
441 // stop request as well. If the job is cancelled (or paused) immediately after start,
442 // we don't need to waste time start then stop the codecs.
443 mCodecMessageQueue.push([this] {
444 media_status_t status = AMediaCodec_start(mDecoder);
445 if (status != AMEDIA_OK) {
446 LOG(ERROR) << "Unable to start video decoder: " << status;
447 mStatus = status;
448 }
449 });
Linus Nilsson0da327a2020-01-31 16:22:18 -0800450
Chong Zhangb55c5452020-06-26 14:32:12 -0700451 mCodecMessageQueue.push([this] {
Linus Nilssone4716f22020-07-10 16:07:57 -0700452 media_status_t status = AMediaCodec_start(mEncoder->getCodec());
Chong Zhangb55c5452020-06-26 14:32:12 -0700453 if (status != AMEDIA_OK) {
454 LOG(ERROR) << "Unable to start video encoder: " << status;
455 mStatus = status;
456 }
Linus Nilssone4716f22020-07-10 16:07:57 -0700457 mEncoder->setStarted();
Chong Zhangb55c5452020-06-26 14:32:12 -0700458 });
Linus Nilsson0da327a2020-01-31 16:22:18 -0800459
460 // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700461 while (!mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800462 std::function<void()> message = mCodecMessageQueue.pop();
463 message();
464 }
465
466 // Return error if transcoding was stopped before it finished.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700467 if (mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800468 mStatus = AMEDIA_ERROR_UNKNOWN; // TODO: Define custom error codes?
469 }
470
471 AMediaCodec_stop(mDecoder);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800472 return mStatus;
473}
474
475void VideoTrackTranscoder::abortTranscodeLoop() {
476 // Push abort message to the front of the codec event queue.
477 mCodecMessageQueue.push([this] { mStopRequested = true; }, true /* front */);
478}
479
Linus Nilssoncab39d82020-05-14 16:32:21 -0700480std::shared_ptr<AMediaFormat> VideoTrackTranscoder::getOutputFormat() const {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700481 return mActualOutputFormat;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700482}
483
Linus Nilsson0da327a2020-01-31 16:22:18 -0800484} // namespace android