blob: e5c065bb42798cd20b36b68692989b2a0ac09e25 [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>
Linus Nilsson93591892020-08-03 18:56:55 -070021#include <media/NdkCommon.h>
Linus Nilsson0da327a2020-01-31 16:22:18 -080022#include <media/VideoTrackTranscoder.h>
Linus Nilssonb09aac22020-07-29 11:56:53 -070023#include <utils/AndroidThreads.h>
Linus Nilsson0da327a2020-01-31 16:22:18 -080024
25namespace android {
26
27// Check that the codec sample flags have the expected NDK meaning.
28static_assert(SAMPLE_FLAG_CODEC_CONFIG == AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG,
29 "Sample flag mismatch: CODEC_CONFIG");
30static_assert(SAMPLE_FLAG_END_OF_STREAM == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM,
31 "Sample flag mismatch: END_OF_STREAM");
32static_assert(SAMPLE_FLAG_PARTIAL_FRAME == AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME,
33 "Sample flag mismatch: PARTIAL_FRAME");
34
Linus Nilssoncab39d82020-05-14 16:32:21 -070035// Color format defined by surface. (See MediaCodecInfo.CodecCapabilities#COLOR_FormatSurface.)
36static constexpr int32_t kColorFormatSurface = 0x7f000789;
37// Default key frame interval in seconds.
38static constexpr float kDefaultKeyFrameIntervalSeconds = 1.0f;
39
Linus Nilsson0da327a2020-01-31 16:22:18 -080040template <typename T>
41void VideoTrackTranscoder::BlockingQueue<T>::push(T const& value, bool front) {
42 {
43 std::unique_lock<std::mutex> lock(mMutex);
44 if (front) {
45 mQueue.push_front(value);
46 } else {
47 mQueue.push_back(value);
48 }
49 }
50 mCondition.notify_one();
51}
52
53template <typename T>
54T VideoTrackTranscoder::BlockingQueue<T>::pop() {
55 std::unique_lock<std::mutex> lock(mMutex);
56 while (mQueue.empty()) {
57 mCondition.wait(lock);
58 }
59 T value = mQueue.front();
60 mQueue.pop_front();
61 return value;
62}
63
Linus Nilssone4716f22020-07-10 16:07:57 -070064// The CodecWrapper class is used to let AMediaCodec instances outlive the transcoder object itself
65// by giving the codec a weak pointer to the transcoder. Codecs wrapped in this object are kept
66// alive by the transcoder and the codec's outstanding buffers. Once the transcoder stops and all
67// output buffers have been released by downstream components the codec will also be released.
68class VideoTrackTranscoder::CodecWrapper {
69public:
70 CodecWrapper(AMediaCodec* codec, const std::weak_ptr<VideoTrackTranscoder>& transcoder)
71 : mCodec(codec), mTranscoder(transcoder), mCodecStarted(false) {}
72 ~CodecWrapper() {
73 if (mCodecStarted) {
74 AMediaCodec_stop(mCodec);
75 }
76 AMediaCodec_delete(mCodec);
77 }
78
79 AMediaCodec* getCodec() { return mCodec; }
80 std::shared_ptr<VideoTrackTranscoder> getTranscoder() const { return mTranscoder.lock(); };
81 void setStarted() { mCodecStarted = true; }
82
83private:
84 AMediaCodec* mCodec;
85 std::weak_ptr<VideoTrackTranscoder> mTranscoder;
86 bool mCodecStarted;
87};
88
Linus Nilsson0da327a2020-01-31 16:22:18 -080089// Dispatch responses to codec callbacks onto the message queue.
90struct AsyncCodecCallbackDispatch {
91 static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
Linus Nilssone4716f22020-07-10 16:07:57 -070092 VideoTrackTranscoder::CodecWrapper* wrapper =
93 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
94 if (auto transcoder = wrapper->getTranscoder()) {
95 if (codec == transcoder->mDecoder) {
96 transcoder->mCodecMessageQueue.push(
97 [transcoder, index] { transcoder->enqueueInputSample(index); });
98 }
Linus Nilsson0da327a2020-01-31 16:22:18 -080099 }
100 }
101
102 static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
103 AMediaCodecBufferInfo* bufferInfoPtr) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700104 VideoTrackTranscoder::CodecWrapper* wrapper =
105 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800106 AMediaCodecBufferInfo bufferInfo = *bufferInfoPtr;
Linus Nilssone4716f22020-07-10 16:07:57 -0700107 if (auto transcoder = wrapper->getTranscoder()) {
108 transcoder->mCodecMessageQueue.push([transcoder, index, codec, bufferInfo] {
109 if (codec == transcoder->mDecoder) {
110 transcoder->transferBuffer(index, bufferInfo);
111 } else if (codec == transcoder->mEncoder->getCodec()) {
112 transcoder->dequeueOutputSample(index, bufferInfo);
113 }
114 });
115 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800116 }
117
118 static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700119 VideoTrackTranscoder::CodecWrapper* wrapper =
120 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
121 if (auto transcoder = wrapper->getTranscoder()) {
122 const char* kCodecName = (codec == transcoder->mDecoder ? "Decoder" : "Encoder");
123 LOG(DEBUG) << kCodecName << " format changed: " << AMediaFormat_toString(format);
124 if (codec == transcoder->mEncoder->getCodec()) {
125 transcoder->mCodecMessageQueue.push(
126 [transcoder, format] { transcoder->updateTrackFormat(format); });
127 }
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700128 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800129 }
130
131 static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t error,
132 int32_t actionCode, const char* detail) {
133 LOG(ERROR) << "Error from codec " << codec << ", userdata " << userdata << ", error "
134 << error << ", action " << actionCode << ", detail " << detail;
Linus Nilssone4716f22020-07-10 16:07:57 -0700135 VideoTrackTranscoder::CodecWrapper* wrapper =
136 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
137 if (auto transcoder = wrapper->getTranscoder()) {
138 transcoder->mCodecMessageQueue.push(
139 [transcoder, error] {
140 transcoder->mStatus = error;
141 transcoder->mStopRequested = true;
142 },
143 true);
144 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800145 }
146};
147
Linus Nilssone4716f22020-07-10 16:07:57 -0700148// static
149std::shared_ptr<VideoTrackTranscoder> VideoTrackTranscoder::create(
150 const std::weak_ptr<MediaTrackTranscoderCallback>& transcoderCallback) {
151 return std::shared_ptr<VideoTrackTranscoder>(new VideoTrackTranscoder(transcoderCallback));
152}
153
Linus Nilsson0da327a2020-01-31 16:22:18 -0800154VideoTrackTranscoder::~VideoTrackTranscoder() {
155 if (mDecoder != nullptr) {
156 AMediaCodec_delete(mDecoder);
157 }
158
Linus Nilsson0da327a2020-01-31 16:22:18 -0800159 if (mSurface != nullptr) {
160 ANativeWindow_release(mSurface);
161 }
162}
163
164// Creates and configures the codecs.
165media_status_t VideoTrackTranscoder::configureDestinationFormat(
166 const std::shared_ptr<AMediaFormat>& destinationFormat) {
167 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
180 float tmp;
181 if (!AMediaFormat_getFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, &tmp)) {
182 AMediaFormat_setFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
183 kDefaultKeyFrameIntervalSeconds);
184 }
185 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
186
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700187 // Always encode without rotation. The rotation degree will be transferred directly to
188 // MediaSampleWriter track format, and MediaSampleWriter will call AMediaMuxer_setOrientationHint.
189 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_ROTATION, 0);
190
Linus Nilssoncab39d82020-05-14 16:32:21 -0700191 mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800192
193 // Create and configure the encoder.
194 const char* destinationMime = nullptr;
195 bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
196 &destinationMime);
197 if (!ok) {
198 LOG(ERROR) << "Destination MIME type is required for transcoding.";
199 return AMEDIA_ERROR_INVALID_PARAMETER;
200 }
201
Linus Nilssonc6221db2020-03-18 14:46:22 -0700202 AMediaCodec* encoder = AMediaCodec_createEncoderByType(destinationMime);
203 if (encoder == nullptr) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800204 LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
205 return AMEDIA_ERROR_UNSUPPORTED;
206 }
Linus Nilssone4716f22020-07-10 16:07:57 -0700207 mEncoder = std::make_shared<CodecWrapper>(encoder, shared_from_this());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800208
Linus Nilssone4716f22020-07-10 16:07:57 -0700209 status = AMediaCodec_configure(mEncoder->getCodec(), mDestinationFormat.get(),
210 NULL /* surface */, NULL /* crypto */,
211 AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800212 if (status != AMEDIA_OK) {
213 LOG(ERROR) << "Unable to configure video encoder: " << status;
214 return status;
215 }
216
Linus Nilssone4716f22020-07-10 16:07:57 -0700217 status = AMediaCodec_createInputSurface(mEncoder->getCodec(), &mSurface);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800218 if (status != AMEDIA_OK) {
219 LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
220 return status;
221 }
222
223 // Create and configure the decoder.
224 const char* sourceMime = nullptr;
225 ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
226 if (!ok) {
227 LOG(ERROR) << "Source MIME type is required for transcoding.";
228 return AMEDIA_ERROR_INVALID_PARAMETER;
229 }
230
231 mDecoder = AMediaCodec_createDecoderByType(sourceMime);
232 if (mDecoder == nullptr) {
233 LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
234 return AMEDIA_ERROR_UNSUPPORTED;
235 }
236
Linus Nilsson93591892020-08-03 18:56:55 -0700237 auto decoderFormat = std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
238 if (!decoderFormat ||
239 AMediaFormat_copy(decoderFormat.get(), mSourceFormat.get()) != AMEDIA_OK) {
240 LOG(ERROR) << "Unable to copy source format";
241 return AMEDIA_ERROR_INVALID_PARAMETER;
242 }
243
244 // Prevent decoder from overwriting frames that the encoder has not yet consumed.
245 AMediaFormat_setInt32(decoderFormat.get(), TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP, 0);
246
247 status = AMediaCodec_configure(mDecoder, decoderFormat.get(), mSurface, NULL /* crypto */,
Linus Nilsson0da327a2020-01-31 16:22:18 -0800248 0 /* flags */);
249 if (status != AMEDIA_OK) {
250 LOG(ERROR) << "Unable to configure video decoder: " << status;
251 return status;
252 }
253
254 // Configure codecs to run in async mode.
255 AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
256 .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
257 .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
258 .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
259 .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
260
Linus Nilssone4716f22020-07-10 16:07:57 -0700261 // Note: The decoder does not need its own wrapper because its lifetime is tied to the
262 // transcoder. But the same callbacks are reused for decoder and encoder so we pass the encoder
263 // wrapper as userdata here but never read the codec from it in the callback.
264 status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800265 if (status != AMEDIA_OK) {
266 LOG(ERROR) << "Unable to set decoder to async mode: " << status;
267 return status;
268 }
269
Linus Nilssone4716f22020-07-10 16:07:57 -0700270 status = AMediaCodec_setAsyncNotifyCallback(mEncoder->getCodec(), asyncCodecCallbacks,
271 mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800272 if (status != AMEDIA_OK) {
273 LOG(ERROR) << "Unable to set encoder to async mode: " << status;
274 return status;
275 }
276
277 return AMEDIA_OK;
278}
279
280void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
281 media_status_t status = AMEDIA_OK;
282
Linus Nilssonc6221db2020-03-18 14:46:22 -0700283 if (mEosFromSource) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800284 return;
285 }
286
287 status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
288 if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
289 LOG(ERROR) << "Error getting next sample info: " << status;
290 mStatus = status;
291 return;
292 }
293 const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
294
295 if (!endOfStream) {
296 size_t bufferSize = 0;
297 uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
298 if (sourceBuffer == nullptr) {
299 LOG(ERROR) << "Decoder returned a NULL input buffer.";
300 mStatus = AMEDIA_ERROR_UNKNOWN;
301 return;
302 } else if (bufferSize < mSampleInfo.size) {
303 LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
304 mStatus = AMEDIA_ERROR_UNKNOWN;
305 return;
306 }
307
308 status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
309 mSampleInfo.size);
310 if (status != AMEDIA_OK) {
311 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
312 mStatus = status;
313 return;
314 }
315
316 mMediaSampleReader->advanceTrack(mTrackIndex);
317 } else {
318 LOG(DEBUG) << "EOS from source.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700319 mEosFromSource = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800320 }
321
322 status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
323 mSampleInfo.presentationTimeUs, mSampleInfo.flags);
324 if (status != AMEDIA_OK) {
325 LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
326 mStatus = status;
327 return;
328 }
329}
330
331void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
332 if (bufferIndex >= 0) {
333 bool needsRender = bufferInfo.size > 0;
334 AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
335 }
336
337 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
338 LOG(DEBUG) << "EOS from decoder.";
Linus Nilssone4716f22020-07-10 16:07:57 -0700339 media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder->getCodec());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800340 if (status != AMEDIA_OK) {
341 LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
342 mStatus = status;
343 }
344 }
345}
346
347void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
348 AMediaCodecBufferInfo bufferInfo) {
349 if (bufferIndex >= 0) {
350 size_t sampleSize = 0;
Linus Nilssone4716f22020-07-10 16:07:57 -0700351 uint8_t* buffer =
352 AMediaCodec_getOutputBuffer(mEncoder->getCodec(), bufferIndex, &sampleSize);
Linus Nilssonc6221db2020-03-18 14:46:22 -0700353
Linus Nilssone4716f22020-07-10 16:07:57 -0700354 MediaSample::OnSampleReleasedCallback bufferReleaseCallback =
355 [encoder = mEncoder](MediaSample* sample) {
356 AMediaCodec_releaseOutputBuffer(encoder->getCodec(), sample->bufferId,
357 false /* render */);
358 };
Linus Nilsson0da327a2020-01-31 16:22:18 -0800359
360 std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
Linus Nilssonc6221db2020-03-18 14:46:22 -0700361 buffer, bufferInfo.offset, bufferIndex, bufferReleaseCallback);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800362 sample->info.size = bufferInfo.size;
363 sample->info.flags = bufferInfo.flags;
364 sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
365
Linus Nilssoncab39d82020-05-14 16:32:21 -0700366 const bool aborted = mOutputQueue->enqueue(sample);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800367 if (aborted) {
368 LOG(ERROR) << "Output sample queue was aborted. Stopping transcode.";
369 mStatus = AMEDIA_ERROR_IO; // TODO: Define custom error codes?
370 return;
371 }
372 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700373 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mEncoder->getCodec());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800374 LOG(DEBUG) << "Encoder output format changed: " << AMediaFormat_toString(newFormat);
375 }
376
377 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
378 LOG(DEBUG) << "EOS from encoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700379 mEosFromEncoder = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800380 }
381}
382
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700383void VideoTrackTranscoder::updateTrackFormat(AMediaFormat* outputFormat) {
384 if (mActualOutputFormat != nullptr) {
385 LOG(WARNING) << "Ignoring duplicate format change.";
386 return;
387 }
388
389 AMediaFormat* formatCopy = AMediaFormat_new();
390 if (!formatCopy || AMediaFormat_copy(formatCopy, outputFormat) != AMEDIA_OK) {
391 LOG(ERROR) << "Unable to copy outputFormat";
392 AMediaFormat_delete(formatCopy);
393 mStatus = AMEDIA_ERROR_INVALID_PARAMETER;
394 return;
395 }
396
397 // Generate the actual track format for muxer based on the encoder output format,
398 // since many vital information comes in the encoder format (eg. CSD).
399 // Transfer necessary fields from the user-configured track format (derived from
400 // source track format and user transcoding request) where needed.
401
402 // Transfer SAR settings:
403 // If mDestinationFormat has SAR set, it means the original source has SAR specified
404 // at container level. This is supposed to override any SAR settings in the bitstream,
405 // thus should always be transferred to the container of the transcoded file.
406 int32_t sarWidth, sarHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700407 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_WIDTH, &sarWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700408 (sarWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700409 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_HEIGHT, &sarHeight) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700410 (sarHeight > 0)) {
411 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
412 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
413 }
414 // Transfer DAR settings.
415 int32_t displayWidth, displayHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700416 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_WIDTH, &displayWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700417 (displayWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700418 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700419 &displayHeight) &&
420 (displayHeight > 0)) {
421 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, displayWidth);
422 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, displayHeight);
423 }
424
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700425 // Transfer rotation settings.
426 // Note that muxer itself doesn't take rotation from the track format. It requires
427 // AMediaMuxer_setOrientationHint to set the rotation. Here we pass the rotation to
428 // MediaSampleWriter using the track format. MediaSampleWriter will then call
429 // AMediaMuxer_setOrientationHint as needed.
430 int32_t rotation;
431 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_ROTATION, &rotation) &&
432 (rotation != 0)) {
433 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_ROTATION, rotation);
434 }
435
Linus Nilsson42a971b2020-07-01 16:41:11 -0700436 // Transfer track duration.
437 // Preserve the source track duration by sending it to MediaSampleWriter.
438 int64_t durationUs;
439 if (AMediaFormat_getInt64(mSourceFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs) &&
440 durationUs > 0) {
441 AMediaFormat_setInt64(formatCopy, AMEDIAFORMAT_KEY_DURATION, durationUs);
442 }
443
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700444 // TODO: transfer other fields as required.
445
446 mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
447
448 notifyTrackFormatAvailable();
449}
450
Linus Nilsson0da327a2020-01-31 16:22:18 -0800451media_status_t VideoTrackTranscoder::runTranscodeLoop() {
Linus Nilssonb09aac22020-07-29 11:56:53 -0700452 androidSetThreadPriority(0 /* tid (0 = current) */, ANDROID_PRIORITY_VIDEO);
453
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