blob: 96e2e6161499b7a560ce37676f10277cd196a6a4 [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
142 mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800143
144 // Create and configure the encoder.
145 const char* destinationMime = nullptr;
146 bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
147 &destinationMime);
148 if (!ok) {
149 LOG(ERROR) << "Destination MIME type is required for transcoding.";
150 return AMEDIA_ERROR_INVALID_PARAMETER;
151 }
152
Linus Nilssonc6221db2020-03-18 14:46:22 -0700153 AMediaCodec* encoder = AMediaCodec_createEncoderByType(destinationMime);
154 if (encoder == nullptr) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800155 LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
156 return AMEDIA_ERROR_UNSUPPORTED;
157 }
Linus Nilssonc6221db2020-03-18 14:46:22 -0700158 mEncoder = std::shared_ptr<AMediaCodec>(encoder,
159 std::bind(AMediaCodec_delete, std::placeholders::_1));
Linus Nilsson0da327a2020-01-31 16:22:18 -0800160
Linus Nilssonc6221db2020-03-18 14:46:22 -0700161 status = AMediaCodec_configure(mEncoder.get(), mDestinationFormat.get(), NULL /* surface */,
Linus Nilsson0da327a2020-01-31 16:22:18 -0800162 NULL /* crypto */, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
163 if (status != AMEDIA_OK) {
164 LOG(ERROR) << "Unable to configure video encoder: " << status;
165 return status;
166 }
167
Linus Nilssonc6221db2020-03-18 14:46:22 -0700168 status = AMediaCodec_createInputSurface(mEncoder.get(), &mSurface);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800169 if (status != AMEDIA_OK) {
170 LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
171 return status;
172 }
173
174 // Create and configure the decoder.
175 const char* sourceMime = nullptr;
176 ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
177 if (!ok) {
178 LOG(ERROR) << "Source MIME type is required for transcoding.";
179 return AMEDIA_ERROR_INVALID_PARAMETER;
180 }
181
182 mDecoder = AMediaCodec_createDecoderByType(sourceMime);
183 if (mDecoder == nullptr) {
184 LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
185 return AMEDIA_ERROR_UNSUPPORTED;
186 }
187
188 status = AMediaCodec_configure(mDecoder, mSourceFormat.get(), mSurface, NULL /* crypto */,
189 0 /* flags */);
190 if (status != AMEDIA_OK) {
191 LOG(ERROR) << "Unable to configure video decoder: " << status;
192 return status;
193 }
194
195 // Configure codecs to run in async mode.
196 AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
197 .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
198 .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
199 .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
200 .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
201
202 status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, this);
203 if (status != AMEDIA_OK) {
204 LOG(ERROR) << "Unable to set decoder to async mode: " << status;
205 return status;
206 }
207
Linus Nilssonc6221db2020-03-18 14:46:22 -0700208 status = AMediaCodec_setAsyncNotifyCallback(mEncoder.get(), asyncCodecCallbacks, this);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800209 if (status != AMEDIA_OK) {
210 LOG(ERROR) << "Unable to set encoder to async mode: " << status;
211 return status;
212 }
213
214 return AMEDIA_OK;
215}
216
217void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
218 media_status_t status = AMEDIA_OK;
219
Linus Nilssonc6221db2020-03-18 14:46:22 -0700220 if (mEosFromSource) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800221 return;
222 }
223
224 status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
225 if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
226 LOG(ERROR) << "Error getting next sample info: " << status;
227 mStatus = status;
228 return;
229 }
230 const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
231
232 if (!endOfStream) {
233 size_t bufferSize = 0;
234 uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
235 if (sourceBuffer == nullptr) {
236 LOG(ERROR) << "Decoder returned a NULL input buffer.";
237 mStatus = AMEDIA_ERROR_UNKNOWN;
238 return;
239 } else if (bufferSize < mSampleInfo.size) {
240 LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
241 mStatus = AMEDIA_ERROR_UNKNOWN;
242 return;
243 }
244
245 status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
246 mSampleInfo.size);
247 if (status != AMEDIA_OK) {
248 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
249 mStatus = status;
250 return;
251 }
252
253 mMediaSampleReader->advanceTrack(mTrackIndex);
254 } else {
255 LOG(DEBUG) << "EOS from source.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700256 mEosFromSource = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800257 }
258
259 status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
260 mSampleInfo.presentationTimeUs, mSampleInfo.flags);
261 if (status != AMEDIA_OK) {
262 LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
263 mStatus = status;
264 return;
265 }
266}
267
268void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
269 if (bufferIndex >= 0) {
270 bool needsRender = bufferInfo.size > 0;
271 AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
272 }
273
274 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
275 LOG(DEBUG) << "EOS from decoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700276 media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800277 if (status != AMEDIA_OK) {
278 LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
279 mStatus = status;
280 }
281 }
282}
283
284void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
285 AMediaCodecBufferInfo bufferInfo) {
286 if (bufferIndex >= 0) {
287 size_t sampleSize = 0;
Linus Nilssonc6221db2020-03-18 14:46:22 -0700288 uint8_t* buffer = AMediaCodec_getOutputBuffer(mEncoder.get(), bufferIndex, &sampleSize);
289
290 MediaSample::OnSampleReleasedCallback bufferReleaseCallback = [encoder = mEncoder](
291 MediaSample* sample) {
292 AMediaCodec_releaseOutputBuffer(encoder.get(), sample->bufferId, false /* render */);
293 };
Linus Nilsson0da327a2020-01-31 16:22:18 -0800294
295 std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
Linus Nilssonc6221db2020-03-18 14:46:22 -0700296 buffer, bufferInfo.offset, bufferIndex, bufferReleaseCallback);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800297 sample->info.size = bufferInfo.size;
298 sample->info.flags = bufferInfo.flags;
299 sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
300
Linus Nilssoncab39d82020-05-14 16:32:21 -0700301 const bool aborted = mOutputQueue->enqueue(sample);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800302 if (aborted) {
303 LOG(ERROR) << "Output sample queue was aborted. Stopping transcode.";
304 mStatus = AMEDIA_ERROR_IO; // TODO: Define custom error codes?
305 return;
306 }
307 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
Linus Nilssonc6221db2020-03-18 14:46:22 -0700308 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800309 LOG(DEBUG) << "Encoder output format changed: " << AMediaFormat_toString(newFormat);
310 }
311
312 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
313 LOG(DEBUG) << "EOS from encoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700314 mEosFromEncoder = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800315 }
316}
317
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700318void VideoTrackTranscoder::updateTrackFormat(AMediaFormat* outputFormat) {
319 if (mActualOutputFormat != nullptr) {
320 LOG(WARNING) << "Ignoring duplicate format change.";
321 return;
322 }
323
324 AMediaFormat* formatCopy = AMediaFormat_new();
325 if (!formatCopy || AMediaFormat_copy(formatCopy, outputFormat) != AMEDIA_OK) {
326 LOG(ERROR) << "Unable to copy outputFormat";
327 AMediaFormat_delete(formatCopy);
328 mStatus = AMEDIA_ERROR_INVALID_PARAMETER;
329 return;
330 }
331
332 // Generate the actual track format for muxer based on the encoder output format,
333 // since many vital information comes in the encoder format (eg. CSD).
334 // Transfer necessary fields from the user-configured track format (derived from
335 // source track format and user transcoding request) where needed.
336
337 // Transfer SAR settings:
338 // If mDestinationFormat has SAR set, it means the original source has SAR specified
339 // at container level. This is supposed to override any SAR settings in the bitstream,
340 // thus should always be transferred to the container of the transcoded file.
341 int32_t sarWidth, sarHeight;
342 if (AMediaFormat_getInt32(mDestinationFormat.get(), AMEDIAFORMAT_KEY_SAR_WIDTH, &sarWidth) &&
343 (sarWidth > 0) &&
344 AMediaFormat_getInt32(mDestinationFormat.get(), AMEDIAFORMAT_KEY_SAR_HEIGHT, &sarHeight) &&
345 (sarHeight > 0)) {
346 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
347 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
348 }
349 // Transfer DAR settings.
350 int32_t displayWidth, displayHeight;
351 if (AMediaFormat_getInt32(mDestinationFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_WIDTH,
352 &displayWidth) &&
353 (displayWidth > 0) &&
354 AMediaFormat_getInt32(mDestinationFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
355 &displayHeight) &&
356 (displayHeight > 0)) {
357 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, displayWidth);
358 AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, displayHeight);
359 }
360
361 // TODO: transfer other fields as required.
362
363 mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
364
365 notifyTrackFormatAvailable();
366}
367
Linus Nilsson0da327a2020-01-31 16:22:18 -0800368media_status_t VideoTrackTranscoder::runTranscodeLoop() {
369 media_status_t status = AMEDIA_OK;
370
371 status = AMediaCodec_start(mDecoder);
372 if (status != AMEDIA_OK) {
373 LOG(ERROR) << "Unable to start video decoder: " << status;
374 return status;
375 }
376
Linus Nilssonc6221db2020-03-18 14:46:22 -0700377 status = AMediaCodec_start(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800378 if (status != AMEDIA_OK) {
379 LOG(ERROR) << "Unable to start video encoder: " << status;
380 AMediaCodec_stop(mDecoder);
381 return status;
382 }
383
384 // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700385 while (!mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800386 std::function<void()> message = mCodecMessageQueue.pop();
387 message();
388 }
389
390 // Return error if transcoding was stopped before it finished.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700391 if (mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800392 mStatus = AMEDIA_ERROR_UNKNOWN; // TODO: Define custom error codes?
393 }
394
395 AMediaCodec_stop(mDecoder);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700396 // TODO: Stop invalidates all buffers. Stop encoder when last buffer is released.
397 // AMediaCodec_stop(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800398 return mStatus;
399}
400
401void VideoTrackTranscoder::abortTranscodeLoop() {
402 // Push abort message to the front of the codec event queue.
403 mCodecMessageQueue.push([this] { mStopRequested = true; }, true /* front */);
404}
405
Linus Nilssoncab39d82020-05-14 16:32:21 -0700406std::shared_ptr<AMediaFormat> VideoTrackTranscoder::getOutputFormat() const {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700407 return mActualOutputFormat;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700408}
409
Linus Nilsson0da327a2020-01-31 16:22:18 -0800410} // namespace android