blob: b5b4a9d387bdbd7a92697911f0d979bc62f24f03 [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>
Hangyu Kuang0d93f0b2020-12-11 18:51:16 +000021#include <android-base/properties.h>
Linus Nilsson93591892020-08-03 18:56:55 -070022#include <media/NdkCommon.h>
Linus Nilsson0da327a2020-01-31 16:22:18 -080023#include <media/VideoTrackTranscoder.h>
Linus Nilssonb09aac22020-07-29 11:56:53 -070024#include <utils/AndroidThreads.h>
Linus Nilsson0da327a2020-01-31 16:22:18 -080025
Linus Nilsson7a127b22020-10-15 16:23:54 -070026using namespace AMediaFormatUtils;
27
Linus Nilsson0da327a2020-01-31 16:22:18 -080028namespace android {
29
30// Check that the codec sample flags have the expected NDK meaning.
31static_assert(SAMPLE_FLAG_CODEC_CONFIG == AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG,
32 "Sample flag mismatch: CODEC_CONFIG");
33static_assert(SAMPLE_FLAG_END_OF_STREAM == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM,
34 "Sample flag mismatch: END_OF_STREAM");
35static_assert(SAMPLE_FLAG_PARTIAL_FRAME == AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME,
36 "Sample flag mismatch: PARTIAL_FRAME");
37
Linus Nilssoncab39d82020-05-14 16:32:21 -070038// Color format defined by surface. (See MediaCodecInfo.CodecCapabilities#COLOR_FormatSurface.)
39static constexpr int32_t kColorFormatSurface = 0x7f000789;
40// Default key frame interval in seconds.
41static constexpr float kDefaultKeyFrameIntervalSeconds = 1.0f;
Linus Nilsson7a127b22020-10-15 16:23:54 -070042// Default codec operating rate.
Linus Nilsson504394a2020-12-16 11:10:10 -080043static int32_t kDefaultCodecOperatingRate720P = base::GetIntProperty(
hkuangbcb9ec12020-12-14 22:16:09 -080044 "debug.media.transcoding.codec_max_operating_rate_720P", /*default*/ 480);
45static int32_t kDefaultCodecOperatingRate1080P = base::GetIntProperty(
46 "debug.media.transcoding.codec_max_operating_rate_1080P", /*default*/ 240);
Linus Nilsson7a127b22020-10-15 16:23:54 -070047// Default codec priority.
48static constexpr int32_t kDefaultCodecPriority = 1;
49// Default bitrate, in case source estimation fails.
50static constexpr int32_t kDefaultBitrateMbps = 10 * 1000 * 1000;
Linus Nilssonaf4a3212020-12-15 08:18:25 -080051// Default frame rate.
52static constexpr int32_t kDefaultFrameRate = 30;
Linus Nilssoncab39d82020-05-14 16:32:21 -070053
Linus Nilssonf4641542021-02-04 15:12:26 -080054// Determines whether a track format describes HDR video content or not. The
55// logic is based on isHdr() in libstagefright/Utils.cpp.
56static bool isHdr(AMediaFormat* format) {
57 // If VUI signals HDR content, this internal flag is set by the extractor.
58 int32_t isHdr;
59 if (AMediaFormat_getInt32(format, "android._is-hdr", &isHdr)) {
60 return isHdr;
61 }
62
63 // If container supplied HDR static info without transfer set, assume HDR.
64 const char* hdrInfo;
65 int32_t transfer;
66 if ((AMediaFormat_getString(format, AMEDIAFORMAT_KEY_HDR_STATIC_INFO, &hdrInfo) ||
67 AMediaFormat_getString(format, "hdr10-plus-info", &hdrInfo)) &&
68 !AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_TRANSFER, &transfer)) {
69 return true;
70 }
71
72 // Otherwise, check if an HDR transfer function is set.
73 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_TRANSFER, &transfer)) {
74 return transfer == COLOR_TRANSFER_ST2084 || transfer == COLOR_TRANSFER_HLG;
75 }
76
77 return false;
78}
79
Linus Nilsson0da327a2020-01-31 16:22:18 -080080template <typename T>
81void VideoTrackTranscoder::BlockingQueue<T>::push(T const& value, bool front) {
82 {
Linus Nilsson93cf9132020-09-24 12:12:48 -070083 std::scoped_lock lock(mMutex);
84 if (mAborted) {
85 return;
86 }
87
Linus Nilsson0da327a2020-01-31 16:22:18 -080088 if (front) {
89 mQueue.push_front(value);
90 } else {
91 mQueue.push_back(value);
92 }
93 }
94 mCondition.notify_one();
95}
96
97template <typename T>
98T VideoTrackTranscoder::BlockingQueue<T>::pop() {
Linus Nilsson93cf9132020-09-24 12:12:48 -070099 std::unique_lock lock(mMutex);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800100 while (mQueue.empty()) {
101 mCondition.wait(lock);
102 }
103 T value = mQueue.front();
104 mQueue.pop_front();
105 return value;
106}
107
Linus Nilsson93cf9132020-09-24 12:12:48 -0700108// Note: Do not call if another thread might waiting in pop.
109template <typename T>
110void VideoTrackTranscoder::BlockingQueue<T>::abort() {
111 std::scoped_lock lock(mMutex);
112 mAborted = true;
113 mQueue.clear();
114}
115
Linus Nilssone4716f22020-07-10 16:07:57 -0700116// The CodecWrapper class is used to let AMediaCodec instances outlive the transcoder object itself
117// by giving the codec a weak pointer to the transcoder. Codecs wrapped in this object are kept
118// alive by the transcoder and the codec's outstanding buffers. Once the transcoder stops and all
119// output buffers have been released by downstream components the codec will also be released.
120class VideoTrackTranscoder::CodecWrapper {
121public:
122 CodecWrapper(AMediaCodec* codec, const std::weak_ptr<VideoTrackTranscoder>& transcoder)
123 : mCodec(codec), mTranscoder(transcoder), mCodecStarted(false) {}
124 ~CodecWrapper() {
125 if (mCodecStarted) {
126 AMediaCodec_stop(mCodec);
127 }
128 AMediaCodec_delete(mCodec);
129 }
130
131 AMediaCodec* getCodec() { return mCodec; }
132 std::shared_ptr<VideoTrackTranscoder> getTranscoder() const { return mTranscoder.lock(); };
133 void setStarted() { mCodecStarted = true; }
134
135private:
136 AMediaCodec* mCodec;
137 std::weak_ptr<VideoTrackTranscoder> mTranscoder;
138 bool mCodecStarted;
139};
140
Linus Nilsson0da327a2020-01-31 16:22:18 -0800141// Dispatch responses to codec callbacks onto the message queue.
142struct AsyncCodecCallbackDispatch {
143 static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700144 VideoTrackTranscoder::CodecWrapper* wrapper =
145 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
146 if (auto transcoder = wrapper->getTranscoder()) {
147 if (codec == transcoder->mDecoder) {
148 transcoder->mCodecMessageQueue.push(
149 [transcoder, index] { transcoder->enqueueInputSample(index); });
150 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800151 }
152 }
153
154 static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
155 AMediaCodecBufferInfo* bufferInfoPtr) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700156 VideoTrackTranscoder::CodecWrapper* wrapper =
157 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800158 AMediaCodecBufferInfo bufferInfo = *bufferInfoPtr;
Linus Nilssone4716f22020-07-10 16:07:57 -0700159 if (auto transcoder = wrapper->getTranscoder()) {
160 transcoder->mCodecMessageQueue.push([transcoder, index, codec, bufferInfo] {
161 if (codec == transcoder->mDecoder) {
162 transcoder->transferBuffer(index, bufferInfo);
163 } else if (codec == transcoder->mEncoder->getCodec()) {
164 transcoder->dequeueOutputSample(index, bufferInfo);
165 }
166 });
167 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800168 }
169
170 static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700171 VideoTrackTranscoder::CodecWrapper* wrapper =
172 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
173 if (auto transcoder = wrapper->getTranscoder()) {
174 const char* kCodecName = (codec == transcoder->mDecoder ? "Decoder" : "Encoder");
175 LOG(DEBUG) << kCodecName << " format changed: " << AMediaFormat_toString(format);
176 if (codec == transcoder->mEncoder->getCodec()) {
177 transcoder->mCodecMessageQueue.push(
178 [transcoder, format] { transcoder->updateTrackFormat(format); });
179 }
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700180 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800181 }
182
183 static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t error,
184 int32_t actionCode, const char* detail) {
185 LOG(ERROR) << "Error from codec " << codec << ", userdata " << userdata << ", error "
186 << error << ", action " << actionCode << ", detail " << detail;
Linus Nilssone4716f22020-07-10 16:07:57 -0700187 VideoTrackTranscoder::CodecWrapper* wrapper =
188 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
189 if (auto transcoder = wrapper->getTranscoder()) {
190 transcoder->mCodecMessageQueue.push(
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700191 [transcoder, error] { transcoder->mStatus = error; }, true);
Linus Nilssone4716f22020-07-10 16:07:57 -0700192 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800193 }
194};
195
Linus Nilssone4716f22020-07-10 16:07:57 -0700196// static
197std::shared_ptr<VideoTrackTranscoder> VideoTrackTranscoder::create(
Chong Zhangbbb4eac2020-11-18 11:12:06 -0800198 const std::weak_ptr<MediaTrackTranscoderCallback>& transcoderCallback, pid_t pid,
199 uid_t uid) {
200 return std::shared_ptr<VideoTrackTranscoder>(
201 new VideoTrackTranscoder(transcoderCallback, pid, uid));
Linus Nilssone4716f22020-07-10 16:07:57 -0700202}
203
Linus Nilsson0da327a2020-01-31 16:22:18 -0800204VideoTrackTranscoder::~VideoTrackTranscoder() {
205 if (mDecoder != nullptr) {
206 AMediaCodec_delete(mDecoder);
207 }
208
Linus Nilsson0da327a2020-01-31 16:22:18 -0800209 if (mSurface != nullptr) {
210 ANativeWindow_release(mSurface);
211 }
212}
213
hkuangbcb9ec12020-12-14 22:16:09 -0800214// Search the default operating rate based on resolution.
215static int32_t getDefaultOperatingRate(AMediaFormat* encoderFormat) {
216 int32_t width, height;
217 if (AMediaFormat_getInt32(encoderFormat, AMEDIAFORMAT_KEY_WIDTH, &width) && (width > 0) &&
218 AMediaFormat_getInt32(encoderFormat, AMEDIAFORMAT_KEY_HEIGHT, &height) && (height > 0)) {
219 if ((width == 1280 && height == 720) || (width == 720 && height == 1280)) {
Linus Nilsson504394a2020-12-16 11:10:10 -0800220 return kDefaultCodecOperatingRate720P;
hkuangbcb9ec12020-12-14 22:16:09 -0800221 } else if ((width == 1920 && height == 1080) || (width == 1080 && height == 1920)) {
222 return kDefaultCodecOperatingRate1080P;
hkuangbcb9ec12020-12-14 22:16:09 -0800223 } else {
224 LOG(WARNING) << "Could not find default operating rate: " << width << " " << height;
Linus Nilsson504394a2020-12-16 11:10:10 -0800225 // Don't set operating rate if the correct dimensions are not found.
hkuangbcb9ec12020-12-14 22:16:09 -0800226 }
227 } else {
228 LOG(ERROR) << "Failed to get default operating rate due to missing resolution";
229 }
230 return -1;
231}
232
Linus Nilsson0da327a2020-01-31 16:22:18 -0800233// Creates and configures the codecs.
234media_status_t VideoTrackTranscoder::configureDestinationFormat(
235 const std::shared_ptr<AMediaFormat>& destinationFormat) {
236 media_status_t status = AMEDIA_OK;
237
238 if (destinationFormat == nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700239 LOG(ERROR) << "Destination format is null, use passthrough transcoder";
Linus Nilsson0da327a2020-01-31 16:22:18 -0800240 return AMEDIA_ERROR_INVALID_PARAMETER;
241 }
242
Linus Nilssoncab39d82020-05-14 16:32:21 -0700243 AMediaFormat* encoderFormat = AMediaFormat_new();
244 if (!encoderFormat || AMediaFormat_copy(encoderFormat, destinationFormat.get()) != AMEDIA_OK) {
245 LOG(ERROR) << "Unable to copy destination format";
246 return AMEDIA_ERROR_INVALID_PARAMETER;
247 }
248
Linus Nilsson800793f2020-07-31 16:16:38 -0700249 int32_t bitrate;
250 if (!AMediaFormat_getInt32(encoderFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrate)) {
251 status = mMediaSampleReader->getEstimatedBitrateForTrack(mTrackIndex, &bitrate);
252 if (status != AMEDIA_OK) {
253 LOG(ERROR) << "Unable to estimate bitrate. Using default " << kDefaultBitrateMbps;
254 bitrate = kDefaultBitrateMbps;
255 }
256
257 LOG(INFO) << "Configuring bitrate " << bitrate;
258 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_BIT_RATE, bitrate);
259 }
260
Linus Nilsson7a127b22020-10-15 16:23:54 -0700261 SetDefaultFormatValueFloat(AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, encoderFormat,
262 kDefaultKeyFrameIntervalSeconds);
hkuangbcb9ec12020-12-14 22:16:09 -0800263
264 int32_t operatingRate = getDefaultOperatingRate(encoderFormat);
265
266 if (operatingRate != -1) {
267 SetDefaultFormatValueInt32(AMEDIAFORMAT_KEY_OPERATING_RATE, encoderFormat, operatingRate);
268 }
269
Linus Nilsson7a127b22020-10-15 16:23:54 -0700270 SetDefaultFormatValueInt32(AMEDIAFORMAT_KEY_PRIORITY, encoderFormat, kDefaultCodecPriority);
Linus Nilssonaf4a3212020-12-15 08:18:25 -0800271 SetDefaultFormatValueInt32(AMEDIAFORMAT_KEY_FRAME_RATE, encoderFormat, kDefaultFrameRate);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700272 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
273
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700274 // Always encode without rotation. The rotation degree will be transferred directly to
275 // MediaSampleWriter track format, and MediaSampleWriter will call AMediaMuxer_setOrientationHint.
276 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_ROTATION, 0);
277
Linus Nilssoncab39d82020-05-14 16:32:21 -0700278 mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800279
280 // Create and configure the encoder.
281 const char* destinationMime = nullptr;
282 bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
283 &destinationMime);
284 if (!ok) {
285 LOG(ERROR) << "Destination MIME type is required for transcoding.";
286 return AMEDIA_ERROR_INVALID_PARAMETER;
287 }
288
Linus Nilssonf4641542021-02-04 15:12:26 -0800289// TODO: replace __ANDROID_API_FUTURE__with 31 when it's official (b/178144708)
290#define __TRANSCODING_MIN_API__ __ANDROID_API_FUTURE__
Jiyong Park26ddfc52021-01-22 16:26:40 +0900291
292 AMediaCodec* encoder;
293 if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
294 encoder = AMediaCodec_createEncoderByTypeForClient(destinationMime, mPid, mUid);
295 } else {
296 encoder = AMediaCodec_createEncoderByType(destinationMime);
297 }
Linus Nilssonc6221db2020-03-18 14:46:22 -0700298 if (encoder == nullptr) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800299 LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
300 return AMEDIA_ERROR_UNSUPPORTED;
301 }
Linus Nilssone4716f22020-07-10 16:07:57 -0700302 mEncoder = std::make_shared<CodecWrapper>(encoder, shared_from_this());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800303
Linus Nilssonaf4a3212020-12-15 08:18:25 -0800304 LOG(DEBUG) << "Configuring encoder with: " << AMediaFormat_toString(mDestinationFormat.get());
Linus Nilssone4716f22020-07-10 16:07:57 -0700305 status = AMediaCodec_configure(mEncoder->getCodec(), mDestinationFormat.get(),
306 NULL /* surface */, NULL /* crypto */,
307 AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800308 if (status != AMEDIA_OK) {
309 LOG(ERROR) << "Unable to configure video encoder: " << status;
310 return status;
311 }
312
Linus Nilssone4716f22020-07-10 16:07:57 -0700313 status = AMediaCodec_createInputSurface(mEncoder->getCodec(), &mSurface);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800314 if (status != AMEDIA_OK) {
315 LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
316 return status;
317 }
318
319 // Create and configure the decoder.
320 const char* sourceMime = nullptr;
321 ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
322 if (!ok) {
323 LOG(ERROR) << "Source MIME type is required for transcoding.";
324 return AMEDIA_ERROR_INVALID_PARAMETER;
325 }
326
Jiyong Park26ddfc52021-01-22 16:26:40 +0900327 if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
328 mDecoder = AMediaCodec_createDecoderByTypeForClient(sourceMime, mPid, mUid);
329 } else {
330 mDecoder = AMediaCodec_createDecoderByType(sourceMime);
331 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800332 if (mDecoder == nullptr) {
333 LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
334 return AMEDIA_ERROR_UNSUPPORTED;
335 }
336
Linus Nilsson93591892020-08-03 18:56:55 -0700337 auto decoderFormat = std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
338 if (!decoderFormat ||
339 AMediaFormat_copy(decoderFormat.get(), mSourceFormat.get()) != AMEDIA_OK) {
340 LOG(ERROR) << "Unable to copy source format";
341 return AMEDIA_ERROR_INVALID_PARAMETER;
342 }
343
Linus Nilssonf4641542021-02-04 15:12:26 -0800344 // Request decoder to convert HDR content to SDR.
345 const bool sourceIsHdr = isHdr(mSourceFormat.get());
346 if (sourceIsHdr) {
347 AMediaFormat_setInt32(decoderFormat.get(),
348 TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
349 COLOR_TRANSFER_SDR_VIDEO);
350 }
351
Linus Nilsson93591892020-08-03 18:56:55 -0700352 // Prevent decoder from overwriting frames that the encoder has not yet consumed.
353 AMediaFormat_setInt32(decoderFormat.get(), TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP, 0);
354
Linus Nilsson16d772b2020-09-29 19:21:11 -0700355 // Copy over configurations that apply to both encoder and decoder.
Linus Nilsson7a127b22020-10-15 16:23:54 -0700356 static const EntryCopier kEncoderEntriesToCopy[] = {
Linus Nilsson16d772b2020-09-29 19:21:11 -0700357 ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
358 ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
359 };
360 const size_t entryCount = sizeof(kEncoderEntriesToCopy) / sizeof(kEncoderEntriesToCopy[0]);
Linus Nilsson7a127b22020-10-15 16:23:54 -0700361 CopyFormatEntries(mDestinationFormat.get(), decoderFormat.get(), kEncoderEntriesToCopy,
362 entryCount);
Linus Nilsson16d772b2020-09-29 19:21:11 -0700363
Linus Nilssonaf4a3212020-12-15 08:18:25 -0800364 LOG(DEBUG) << "Configuring decoder with: " << AMediaFormat_toString(decoderFormat.get());
Linus Nilsson93591892020-08-03 18:56:55 -0700365 status = AMediaCodec_configure(mDecoder, decoderFormat.get(), mSurface, NULL /* crypto */,
Linus Nilsson0da327a2020-01-31 16:22:18 -0800366 0 /* flags */);
367 if (status != AMEDIA_OK) {
368 LOG(ERROR) << "Unable to configure video decoder: " << status;
369 return status;
370 }
371
Linus Nilssonf4641542021-02-04 15:12:26 -0800372 if (sourceIsHdr) {
373 bool supported = false;
374 AMediaFormat* inputFormat = AMediaCodec_getInputFormat(mDecoder);
375
376 if (inputFormat != nullptr) {
377 int32_t transferFunc;
378 supported = AMediaFormat_getInt32(inputFormat,
379 TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
380 &transferFunc) &&
381 transferFunc == COLOR_TRANSFER_SDR_VIDEO;
382 AMediaFormat_delete(inputFormat);
383 }
384
385 if (!supported) {
386 LOG(ERROR) << "HDR to SDR conversion unsupported by the codec";
387 return AMEDIA_ERROR_UNSUPPORTED;
388 }
389 }
390
Linus Nilsson0da327a2020-01-31 16:22:18 -0800391 // Configure codecs to run in async mode.
392 AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
393 .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
394 .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
395 .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
396 .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
397
Linus Nilssone4716f22020-07-10 16:07:57 -0700398 // Note: The decoder does not need its own wrapper because its lifetime is tied to the
399 // transcoder. But the same callbacks are reused for decoder and encoder so we pass the encoder
400 // wrapper as userdata here but never read the codec from it in the callback.
401 status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800402 if (status != AMEDIA_OK) {
403 LOG(ERROR) << "Unable to set decoder to async mode: " << status;
404 return status;
405 }
406
Linus Nilssone4716f22020-07-10 16:07:57 -0700407 status = AMediaCodec_setAsyncNotifyCallback(mEncoder->getCodec(), asyncCodecCallbacks,
408 mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800409 if (status != AMEDIA_OK) {
410 LOG(ERROR) << "Unable to set encoder to async mode: " << status;
411 return status;
412 }
413
414 return AMEDIA_OK;
415}
416
417void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
418 media_status_t status = AMEDIA_OK;
419
Linus Nilssonc6221db2020-03-18 14:46:22 -0700420 if (mEosFromSource) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800421 return;
422 }
423
424 status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
425 if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
426 LOG(ERROR) << "Error getting next sample info: " << status;
427 mStatus = status;
428 return;
429 }
430 const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
431
432 if (!endOfStream) {
433 size_t bufferSize = 0;
434 uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
435 if (sourceBuffer == nullptr) {
436 LOG(ERROR) << "Decoder returned a NULL input buffer.";
437 mStatus = AMEDIA_ERROR_UNKNOWN;
438 return;
439 } else if (bufferSize < mSampleInfo.size) {
440 LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
441 mStatus = AMEDIA_ERROR_UNKNOWN;
442 return;
443 }
444
445 status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
446 mSampleInfo.size);
447 if (status != AMEDIA_OK) {
448 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
449 mStatus = status;
450 return;
451 }
Linus Nilsson47352412020-12-16 12:21:26 -0800452
453 if (mSampleInfo.size) {
454 ++mInputFrameCount;
455 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800456 } else {
457 LOG(DEBUG) << "EOS from source.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700458 mEosFromSource = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800459 }
460
461 status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
462 mSampleInfo.presentationTimeUs, mSampleInfo.flags);
463 if (status != AMEDIA_OK) {
464 LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
465 mStatus = status;
466 return;
467 }
468}
469
470void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
471 if (bufferIndex >= 0) {
472 bool needsRender = bufferInfo.size > 0;
473 AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
474 }
475
476 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
477 LOG(DEBUG) << "EOS from decoder.";
Linus Nilssone4716f22020-07-10 16:07:57 -0700478 media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder->getCodec());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800479 if (status != AMEDIA_OK) {
480 LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
481 mStatus = status;
482 }
483 }
484}
485
486void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
487 AMediaCodecBufferInfo bufferInfo) {
488 if (bufferIndex >= 0) {
489 size_t sampleSize = 0;
Linus Nilssone4716f22020-07-10 16:07:57 -0700490 uint8_t* buffer =
491 AMediaCodec_getOutputBuffer(mEncoder->getCodec(), bufferIndex, &sampleSize);
Linus Nilssonc6221db2020-03-18 14:46:22 -0700492
Linus Nilssone4716f22020-07-10 16:07:57 -0700493 MediaSample::OnSampleReleasedCallback bufferReleaseCallback =
494 [encoder = mEncoder](MediaSample* sample) {
495 AMediaCodec_releaseOutputBuffer(encoder->getCodec(), sample->bufferId,
496 false /* render */);
497 };
Linus Nilsson0da327a2020-01-31 16:22:18 -0800498
499 std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
Linus Nilssonc6221db2020-03-18 14:46:22 -0700500 buffer, bufferInfo.offset, bufferIndex, bufferReleaseCallback);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800501 sample->info.size = bufferInfo.size;
502 sample->info.flags = bufferInfo.flags;
503 sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
504
Linus Nilsson47352412020-12-16 12:21:26 -0800505 if (bufferInfo.size > 0 && (bufferInfo.flags & SAMPLE_FLAG_CODEC_CONFIG) == 0) {
506 ++mOutputFrameCount;
507 }
Linus Nilssonc31d2492020-09-23 12:30:00 -0700508 onOutputSampleAvailable(sample);
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700509
510 mLastSampleWasSync = sample->info.flags & SAMPLE_FLAG_SYNC_SAMPLE;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800511 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700512 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mEncoder->getCodec());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800513 LOG(DEBUG) << "Encoder output format changed: " << AMediaFormat_toString(newFormat);
514 }
515
516 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
517 LOG(DEBUG) << "EOS from encoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700518 mEosFromEncoder = true;
Linus Nilsson47352412020-12-16 12:21:26 -0800519
520 if (mInputFrameCount != mOutputFrameCount) {
521 LOG(WARNING) << "Input / Output frame count mismatch: " << mInputFrameCount << " vs "
522 << mOutputFrameCount;
523 if (mInputFrameCount > 0 && mOutputFrameCount == 0) {
524 LOG(ERROR) << "Encoder did not produce any output frames.";
525 mStatus = AMEDIA_ERROR_UNKNOWN;
526 }
527 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800528 }
529}
530
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700531void VideoTrackTranscoder::updateTrackFormat(AMediaFormat* outputFormat) {
532 if (mActualOutputFormat != nullptr) {
533 LOG(WARNING) << "Ignoring duplicate format change.";
534 return;
535 }
536
537 AMediaFormat* formatCopy = AMediaFormat_new();
538 if (!formatCopy || AMediaFormat_copy(formatCopy, outputFormat) != AMEDIA_OK) {
539 LOG(ERROR) << "Unable to copy outputFormat";
540 AMediaFormat_delete(formatCopy);
541 mStatus = AMEDIA_ERROR_INVALID_PARAMETER;
542 return;
543 }
544
545 // Generate the actual track format for muxer based on the encoder output format,
546 // since many vital information comes in the encoder format (eg. CSD).
547 // Transfer necessary fields from the user-configured track format (derived from
548 // source track format and user transcoding request) where needed.
549
550 // Transfer SAR settings:
551 // If mDestinationFormat has SAR set, it means the original source has SAR specified
552 // at container level. This is supposed to override any SAR settings in the bitstream,
553 // thus should always be transferred to the container of the transcoded file.
554 int32_t sarWidth, sarHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700555 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_WIDTH, &sarWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700556 (sarWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700557 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_HEIGHT, &sarHeight) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700558 (sarHeight > 0)) {
559 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
560 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
561 }
562 // Transfer DAR settings.
563 int32_t displayWidth, displayHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700564 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_WIDTH, &displayWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700565 (displayWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700566 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700567 &displayHeight) &&
568 (displayHeight > 0)) {
569 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, displayWidth);
570 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, displayHeight);
571 }
572
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700573 // Transfer rotation settings.
574 // Note that muxer itself doesn't take rotation from the track format. It requires
575 // AMediaMuxer_setOrientationHint to set the rotation. Here we pass the rotation to
576 // MediaSampleWriter using the track format. MediaSampleWriter will then call
577 // AMediaMuxer_setOrientationHint as needed.
578 int32_t rotation;
579 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_ROTATION, &rotation) &&
580 (rotation != 0)) {
581 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_ROTATION, rotation);
582 }
583
Linus Nilsson42a971b2020-07-01 16:41:11 -0700584 // Transfer track duration.
585 // Preserve the source track duration by sending it to MediaSampleWriter.
586 int64_t durationUs;
587 if (AMediaFormat_getInt64(mSourceFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs) &&
588 durationUs > 0) {
589 AMediaFormat_setInt64(formatCopy, AMEDIAFORMAT_KEY_DURATION, durationUs);
590 }
591
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700592 // TODO: transfer other fields as required.
593
594 mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
595
596 notifyTrackFormatAvailable();
597}
598
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700599media_status_t VideoTrackTranscoder::runTranscodeLoop(bool* stopped) {
Linus Nilssonb09aac22020-07-29 11:56:53 -0700600 androidSetThreadPriority(0 /* tid (0 = current) */, ANDROID_PRIORITY_VIDEO);
601
Chong Zhangb55c5452020-06-26 14:32:12 -0700602 // Push start decoder and encoder as two messages, so that these are subject to the
Chong Zhangbc062482020-10-14 16:43:53 -0700603 // stop request as well. If the session is cancelled (or paused) immediately after start,
Chong Zhangb55c5452020-06-26 14:32:12 -0700604 // we don't need to waste time start then stop the codecs.
605 mCodecMessageQueue.push([this] {
606 media_status_t status = AMediaCodec_start(mDecoder);
607 if (status != AMEDIA_OK) {
608 LOG(ERROR) << "Unable to start video decoder: " << status;
609 mStatus = status;
610 }
611 });
Linus Nilsson0da327a2020-01-31 16:22:18 -0800612
Chong Zhangb55c5452020-06-26 14:32:12 -0700613 mCodecMessageQueue.push([this] {
Linus Nilssone4716f22020-07-10 16:07:57 -0700614 media_status_t status = AMediaCodec_start(mEncoder->getCodec());
Chong Zhangb55c5452020-06-26 14:32:12 -0700615 if (status != AMEDIA_OK) {
616 LOG(ERROR) << "Unable to start video encoder: " << status;
617 mStatus = status;
618 }
Linus Nilssone4716f22020-07-10 16:07:57 -0700619 mEncoder->setStarted();
Chong Zhangb55c5452020-06-26 14:32:12 -0700620 });
Linus Nilsson0da327a2020-01-31 16:22:18 -0800621
622 // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700623 while (mStopRequest != STOP_NOW && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800624 std::function<void()> message = mCodecMessageQueue.pop();
625 message();
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700626
627 if (mStopRequest == STOP_ON_SYNC && mLastSampleWasSync) {
628 break;
629 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800630 }
631
Linus Nilsson93cf9132020-09-24 12:12:48 -0700632 mCodecMessageQueue.abort();
633 AMediaCodec_stop(mDecoder);
634
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700635 // Signal if transcoding was stopped before it finished.
636 if (mStopRequest != NONE && !mEosFromEncoder && mStatus == AMEDIA_OK) {
637 *stopped = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800638 }
639
Linus Nilsson0da327a2020-01-31 16:22:18 -0800640 return mStatus;
641}
642
643void VideoTrackTranscoder::abortTranscodeLoop() {
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700644 if (mStopRequest == STOP_NOW) {
645 // Wake up transcoder thread.
646 mCodecMessageQueue.push([] {}, true /* front */);
647 }
Linus Nilsson0da327a2020-01-31 16:22:18 -0800648}
649
Linus Nilssoncab39d82020-05-14 16:32:21 -0700650std::shared_ptr<AMediaFormat> VideoTrackTranscoder::getOutputFormat() const {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700651 return mActualOutputFormat;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700652}
653
Linus Nilsson0da327a2020-01-31 16:22:18 -0800654} // namespace android