blob: 4df32961103b69ee3b7dda49917c83d9a3bfb00a [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
62// Dispatch responses to codec callbacks onto the message queue.
63struct AsyncCodecCallbackDispatch {
64 static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
65 VideoTrackTranscoder* transcoder = static_cast<VideoTrackTranscoder*>(userdata);
66 if (codec == transcoder->mDecoder) {
67 transcoder->mCodecMessageQueue.push(
68 [transcoder, index] { transcoder->enqueueInputSample(index); });
69 }
70 }
71
72 static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
73 AMediaCodecBufferInfo* bufferInfoPtr) {
74 VideoTrackTranscoder* transcoder = static_cast<VideoTrackTranscoder*>(userdata);
75 AMediaCodecBufferInfo bufferInfo = *bufferInfoPtr;
76 transcoder->mCodecMessageQueue.push([transcoder, index, codec, bufferInfo] {
77 if (codec == transcoder->mDecoder) {
78 transcoder->transferBuffer(index, bufferInfo);
Linus Nilssonc6221db2020-03-18 14:46:22 -070079 } else if (codec == transcoder->mEncoder.get()) {
Linus Nilsson0da327a2020-01-31 16:22:18 -080080 transcoder->dequeueOutputSample(index, bufferInfo);
81 }
82 });
83 }
84
85 static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
86 VideoTrackTranscoder* transcoder = static_cast<VideoTrackTranscoder*>(userdata);
87 const char* kCodecName = (codec == transcoder->mDecoder ? "Decoder" : "Encoder");
88 LOG(DEBUG) << kCodecName << " format changed: " << AMediaFormat_toString(format);
Chong Zhanga2cc86b2020-06-17 16:56:49 -070089 if (codec == transcoder->mEncoder.get()) {
90 transcoder->mCodecMessageQueue.push(
91 [transcoder, format] { transcoder->updateTrackFormat(format); });
92 }
Linus Nilsson0da327a2020-01-31 16:22:18 -080093 }
94
95 static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t error,
96 int32_t actionCode, const char* detail) {
97 LOG(ERROR) << "Error from codec " << codec << ", userdata " << userdata << ", error "
98 << error << ", action " << actionCode << ", detail " << detail;
99 VideoTrackTranscoder* transcoder = static_cast<VideoTrackTranscoder*>(userdata);
100 transcoder->mCodecMessageQueue.push(
101 [transcoder, error] {
102 transcoder->mStatus = error;
103 transcoder->mStopRequested = true;
104 },
105 true);
106 }
107};
108
109VideoTrackTranscoder::~VideoTrackTranscoder() {
110 if (mDecoder != nullptr) {
111 AMediaCodec_delete(mDecoder);
112 }
113
Linus Nilsson0da327a2020-01-31 16:22:18 -0800114 if (mSurface != nullptr) {
115 ANativeWindow_release(mSurface);
116 }
117}
118
119// Creates and configures the codecs.
120media_status_t VideoTrackTranscoder::configureDestinationFormat(
121 const std::shared_ptr<AMediaFormat>& destinationFormat) {
122 media_status_t status = AMEDIA_OK;
123
124 if (destinationFormat == nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700125 LOG(ERROR) << "Destination format is null, use passthrough transcoder";
Linus Nilsson0da327a2020-01-31 16:22:18 -0800126 return AMEDIA_ERROR_INVALID_PARAMETER;
127 }
128
Linus Nilssoncab39d82020-05-14 16:32:21 -0700129 AMediaFormat* encoderFormat = AMediaFormat_new();
130 if (!encoderFormat || AMediaFormat_copy(encoderFormat, destinationFormat.get()) != AMEDIA_OK) {
131 LOG(ERROR) << "Unable to copy destination format";
132 return AMEDIA_ERROR_INVALID_PARAMETER;
133 }
134
135 float tmp;
136 if (!AMediaFormat_getFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, &tmp)) {
137 AMediaFormat_setFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
138 kDefaultKeyFrameIntervalSeconds);
139 }
140 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
141
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700142 // Always encode without rotation. The rotation degree will be transferred directly to
143 // MediaSampleWriter track format, and MediaSampleWriter will call AMediaMuxer_setOrientationHint.
144 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_ROTATION, 0);
145
Linus Nilssoncab39d82020-05-14 16:32:21 -0700146 mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800147
148 // Create and configure the encoder.
149 const char* destinationMime = nullptr;
150 bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
151 &destinationMime);
152 if (!ok) {
153 LOG(ERROR) << "Destination MIME type is required for transcoding.";
154 return AMEDIA_ERROR_INVALID_PARAMETER;
155 }
156
Linus Nilssonc6221db2020-03-18 14:46:22 -0700157 AMediaCodec* encoder = AMediaCodec_createEncoderByType(destinationMime);
158 if (encoder == nullptr) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800159 LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
160 return AMEDIA_ERROR_UNSUPPORTED;
161 }
Linus Nilssonc6221db2020-03-18 14:46:22 -0700162 mEncoder = std::shared_ptr<AMediaCodec>(encoder,
163 std::bind(AMediaCodec_delete, std::placeholders::_1));
Linus Nilsson0da327a2020-01-31 16:22:18 -0800164
Linus Nilssonc6221db2020-03-18 14:46:22 -0700165 status = AMediaCodec_configure(mEncoder.get(), mDestinationFormat.get(), NULL /* surface */,
Linus Nilsson0da327a2020-01-31 16:22:18 -0800166 NULL /* crypto */, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
167 if (status != AMEDIA_OK) {
168 LOG(ERROR) << "Unable to configure video encoder: " << status;
169 return status;
170 }
171
Linus Nilssonc6221db2020-03-18 14:46:22 -0700172 status = AMediaCodec_createInputSurface(mEncoder.get(), &mSurface);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800173 if (status != AMEDIA_OK) {
174 LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
175 return status;
176 }
177
178 // Create and configure the decoder.
179 const char* sourceMime = nullptr;
180 ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
181 if (!ok) {
182 LOG(ERROR) << "Source MIME type is required for transcoding.";
183 return AMEDIA_ERROR_INVALID_PARAMETER;
184 }
185
186 mDecoder = AMediaCodec_createDecoderByType(sourceMime);
187 if (mDecoder == nullptr) {
188 LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
189 return AMEDIA_ERROR_UNSUPPORTED;
190 }
191
192 status = AMediaCodec_configure(mDecoder, mSourceFormat.get(), mSurface, NULL /* crypto */,
193 0 /* flags */);
194 if (status != AMEDIA_OK) {
195 LOG(ERROR) << "Unable to configure video decoder: " << status;
196 return status;
197 }
198
199 // Configure codecs to run in async mode.
200 AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
201 .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
202 .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
203 .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
204 .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
205
206 status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, this);
207 if (status != AMEDIA_OK) {
208 LOG(ERROR) << "Unable to set decoder to async mode: " << status;
209 return status;
210 }
211
Linus Nilssonc6221db2020-03-18 14:46:22 -0700212 status = AMediaCodec_setAsyncNotifyCallback(mEncoder.get(), asyncCodecCallbacks, this);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800213 if (status != AMEDIA_OK) {
214 LOG(ERROR) << "Unable to set encoder to async mode: " << status;
215 return status;
216 }
217
218 return AMEDIA_OK;
219}
220
221void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
222 media_status_t status = AMEDIA_OK;
223
Linus Nilssonc6221db2020-03-18 14:46:22 -0700224 if (mEosFromSource) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800225 return;
226 }
227
228 status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
229 if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
230 LOG(ERROR) << "Error getting next sample info: " << status;
231 mStatus = status;
232 return;
233 }
234 const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
235
236 if (!endOfStream) {
237 size_t bufferSize = 0;
238 uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
239 if (sourceBuffer == nullptr) {
240 LOG(ERROR) << "Decoder returned a NULL input buffer.";
241 mStatus = AMEDIA_ERROR_UNKNOWN;
242 return;
243 } else if (bufferSize < mSampleInfo.size) {
244 LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
245 mStatus = AMEDIA_ERROR_UNKNOWN;
246 return;
247 }
248
249 status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
250 mSampleInfo.size);
251 if (status != AMEDIA_OK) {
252 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
253 mStatus = status;
254 return;
255 }
256
257 mMediaSampleReader->advanceTrack(mTrackIndex);
258 } else {
259 LOG(DEBUG) << "EOS from source.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700260 mEosFromSource = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800261 }
262
263 status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
264 mSampleInfo.presentationTimeUs, mSampleInfo.flags);
265 if (status != AMEDIA_OK) {
266 LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
267 mStatus = status;
268 return;
269 }
270}
271
272void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
273 if (bufferIndex >= 0) {
274 bool needsRender = bufferInfo.size > 0;
275 AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
276 }
277
278 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
279 LOG(DEBUG) << "EOS from decoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700280 media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800281 if (status != AMEDIA_OK) {
282 LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
283 mStatus = status;
284 }
285 }
286}
287
288void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
289 AMediaCodecBufferInfo bufferInfo) {
290 if (bufferIndex >= 0) {
291 size_t sampleSize = 0;
Linus Nilssonc6221db2020-03-18 14:46:22 -0700292 uint8_t* buffer = AMediaCodec_getOutputBuffer(mEncoder.get(), bufferIndex, &sampleSize);
293
294 MediaSample::OnSampleReleasedCallback bufferReleaseCallback = [encoder = mEncoder](
295 MediaSample* sample) {
296 AMediaCodec_releaseOutputBuffer(encoder.get(), sample->bufferId, false /* render */);
297 };
Linus Nilsson0da327a2020-01-31 16:22:18 -0800298
299 std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
Linus Nilssonc6221db2020-03-18 14:46:22 -0700300 buffer, bufferInfo.offset, bufferIndex, bufferReleaseCallback);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800301 sample->info.size = bufferInfo.size;
302 sample->info.flags = bufferInfo.flags;
303 sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
304
Linus Nilssoncab39d82020-05-14 16:32:21 -0700305 const bool aborted = mOutputQueue->enqueue(sample);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800306 if (aborted) {
307 LOG(ERROR) << "Output sample queue was aborted. Stopping transcode.";
308 mStatus = AMEDIA_ERROR_IO; // TODO: Define custom error codes?
309 return;
310 }
311 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
Linus Nilssonc6221db2020-03-18 14:46:22 -0700312 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800313 LOG(DEBUG) << "Encoder output format changed: " << AMediaFormat_toString(newFormat);
314 }
315
316 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
317 LOG(DEBUG) << "EOS from encoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700318 mEosFromEncoder = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800319 }
320}
321
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700322void VideoTrackTranscoder::updateTrackFormat(AMediaFormat* outputFormat) {
323 if (mActualOutputFormat != nullptr) {
324 LOG(WARNING) << "Ignoring duplicate format change.";
325 return;
326 }
327
328 AMediaFormat* formatCopy = AMediaFormat_new();
329 if (!formatCopy || AMediaFormat_copy(formatCopy, outputFormat) != AMEDIA_OK) {
330 LOG(ERROR) << "Unable to copy outputFormat";
331 AMediaFormat_delete(formatCopy);
332 mStatus = AMEDIA_ERROR_INVALID_PARAMETER;
333 return;
334 }
335
336 // Generate the actual track format for muxer based on the encoder output format,
337 // since many vital information comes in the encoder format (eg. CSD).
338 // Transfer necessary fields from the user-configured track format (derived from
339 // source track format and user transcoding request) where needed.
340
341 // Transfer SAR settings:
342 // If mDestinationFormat has SAR set, it means the original source has SAR specified
343 // at container level. This is supposed to override any SAR settings in the bitstream,
344 // thus should always be transferred to the container of the transcoded file.
345 int32_t sarWidth, sarHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700346 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_WIDTH, &sarWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700347 (sarWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700348 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_HEIGHT, &sarHeight) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700349 (sarHeight > 0)) {
350 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
351 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
352 }
353 // Transfer DAR settings.
354 int32_t displayWidth, displayHeight;
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700355 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_WIDTH, &displayWidth) &&
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700356 (displayWidth > 0) &&
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700357 AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700358 &displayHeight) &&
359 (displayHeight > 0)) {
360 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, displayWidth);
361 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, displayHeight);
362 }
363
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700364 // Transfer rotation settings.
365 // Note that muxer itself doesn't take rotation from the track format. It requires
366 // AMediaMuxer_setOrientationHint to set the rotation. Here we pass the rotation to
367 // MediaSampleWriter using the track format. MediaSampleWriter will then call
368 // AMediaMuxer_setOrientationHint as needed.
369 int32_t rotation;
370 if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_ROTATION, &rotation) &&
371 (rotation != 0)) {
372 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_ROTATION, rotation);
373 }
374
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700375 // TODO: transfer other fields as required.
376
377 mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
378
379 notifyTrackFormatAvailable();
380}
381
Linus Nilsson0da327a2020-01-31 16:22:18 -0800382media_status_t VideoTrackTranscoder::runTranscodeLoop() {
383 media_status_t status = AMEDIA_OK;
384
385 status = AMediaCodec_start(mDecoder);
386 if (status != AMEDIA_OK) {
387 LOG(ERROR) << "Unable to start video decoder: " << status;
388 return status;
389 }
390
Linus Nilssonc6221db2020-03-18 14:46:22 -0700391 status = AMediaCodec_start(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800392 if (status != AMEDIA_OK) {
393 LOG(ERROR) << "Unable to start video encoder: " << status;
394 AMediaCodec_stop(mDecoder);
395 return status;
396 }
397
398 // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700399 while (!mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800400 std::function<void()> message = mCodecMessageQueue.pop();
401 message();
402 }
403
404 // Return error if transcoding was stopped before it finished.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700405 if (mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800406 mStatus = AMEDIA_ERROR_UNKNOWN; // TODO: Define custom error codes?
407 }
408
409 AMediaCodec_stop(mDecoder);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700410 // TODO: Stop invalidates all buffers. Stop encoder when last buffer is released.
411 // AMediaCodec_stop(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800412 return mStatus;
413}
414
415void VideoTrackTranscoder::abortTranscodeLoop() {
416 // Push abort message to the front of the codec event queue.
417 mCodecMessageQueue.push([this] { mStopRequested = true; }, true /* front */);
418}
419
Linus Nilssoncab39d82020-05-14 16:32:21 -0700420std::shared_ptr<AMediaFormat> VideoTrackTranscoder::getOutputFormat() const {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700421 return mActualOutputFormat;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700422}
423
Linus Nilsson0da327a2020-01-31 16:22:18 -0800424} // namespace android