blob: 38185459a98d4fdf2feebf1e38bb6660ca9eada4 [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);
89 }
90
91 static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t error,
92 int32_t actionCode, const char* detail) {
93 LOG(ERROR) << "Error from codec " << codec << ", userdata " << userdata << ", error "
94 << error << ", action " << actionCode << ", detail " << detail;
95 VideoTrackTranscoder* transcoder = static_cast<VideoTrackTranscoder*>(userdata);
96 transcoder->mCodecMessageQueue.push(
97 [transcoder, error] {
98 transcoder->mStatus = error;
99 transcoder->mStopRequested = true;
100 },
101 true);
102 }
103};
104
105VideoTrackTranscoder::~VideoTrackTranscoder() {
106 if (mDecoder != nullptr) {
107 AMediaCodec_delete(mDecoder);
108 }
109
Linus Nilsson0da327a2020-01-31 16:22:18 -0800110 if (mSurface != nullptr) {
111 ANativeWindow_release(mSurface);
112 }
113}
114
115// Creates and configures the codecs.
116media_status_t VideoTrackTranscoder::configureDestinationFormat(
117 const std::shared_ptr<AMediaFormat>& destinationFormat) {
118 media_status_t status = AMEDIA_OK;
119
120 if (destinationFormat == nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700121 LOG(ERROR) << "Destination format is null, use passthrough transcoder";
Linus Nilsson0da327a2020-01-31 16:22:18 -0800122 return AMEDIA_ERROR_INVALID_PARAMETER;
123 }
124
Linus Nilssoncab39d82020-05-14 16:32:21 -0700125 AMediaFormat* encoderFormat = AMediaFormat_new();
126 if (!encoderFormat || AMediaFormat_copy(encoderFormat, destinationFormat.get()) != AMEDIA_OK) {
127 LOG(ERROR) << "Unable to copy destination format";
128 return AMEDIA_ERROR_INVALID_PARAMETER;
129 }
130
131 float tmp;
132 if (!AMediaFormat_getFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, &tmp)) {
133 AMediaFormat_setFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
134 kDefaultKeyFrameIntervalSeconds);
135 }
136 AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
137
138 mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800139
140 // Create and configure the encoder.
141 const char* destinationMime = nullptr;
142 bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
143 &destinationMime);
144 if (!ok) {
145 LOG(ERROR) << "Destination MIME type is required for transcoding.";
146 return AMEDIA_ERROR_INVALID_PARAMETER;
147 }
148
Linus Nilssonc6221db2020-03-18 14:46:22 -0700149 AMediaCodec* encoder = AMediaCodec_createEncoderByType(destinationMime);
150 if (encoder == nullptr) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800151 LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
152 return AMEDIA_ERROR_UNSUPPORTED;
153 }
Linus Nilssonc6221db2020-03-18 14:46:22 -0700154 mEncoder = std::shared_ptr<AMediaCodec>(encoder,
155 std::bind(AMediaCodec_delete, std::placeholders::_1));
Linus Nilsson0da327a2020-01-31 16:22:18 -0800156
Linus Nilssonc6221db2020-03-18 14:46:22 -0700157 status = AMediaCodec_configure(mEncoder.get(), mDestinationFormat.get(), NULL /* surface */,
Linus Nilsson0da327a2020-01-31 16:22:18 -0800158 NULL /* crypto */, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
159 if (status != AMEDIA_OK) {
160 LOG(ERROR) << "Unable to configure video encoder: " << status;
161 return status;
162 }
163
Linus Nilssonc6221db2020-03-18 14:46:22 -0700164 status = AMediaCodec_createInputSurface(mEncoder.get(), &mSurface);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800165 if (status != AMEDIA_OK) {
166 LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
167 return status;
168 }
169
170 // Create and configure the decoder.
171 const char* sourceMime = nullptr;
172 ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
173 if (!ok) {
174 LOG(ERROR) << "Source MIME type is required for transcoding.";
175 return AMEDIA_ERROR_INVALID_PARAMETER;
176 }
177
178 mDecoder = AMediaCodec_createDecoderByType(sourceMime);
179 if (mDecoder == nullptr) {
180 LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
181 return AMEDIA_ERROR_UNSUPPORTED;
182 }
183
184 status = AMediaCodec_configure(mDecoder, mSourceFormat.get(), mSurface, NULL /* crypto */,
185 0 /* flags */);
186 if (status != AMEDIA_OK) {
187 LOG(ERROR) << "Unable to configure video decoder: " << status;
188 return status;
189 }
190
191 // Configure codecs to run in async mode.
192 AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
193 .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
194 .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
195 .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
196 .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
197
198 status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, this);
199 if (status != AMEDIA_OK) {
200 LOG(ERROR) << "Unable to set decoder to async mode: " << status;
201 return status;
202 }
203
Linus Nilssonc6221db2020-03-18 14:46:22 -0700204 status = AMediaCodec_setAsyncNotifyCallback(mEncoder.get(), asyncCodecCallbacks, this);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800205 if (status != AMEDIA_OK) {
206 LOG(ERROR) << "Unable to set encoder to async mode: " << status;
207 return status;
208 }
209
210 return AMEDIA_OK;
211}
212
213void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
214 media_status_t status = AMEDIA_OK;
215
Linus Nilssonc6221db2020-03-18 14:46:22 -0700216 if (mEosFromSource) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800217 return;
218 }
219
220 status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
221 if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
222 LOG(ERROR) << "Error getting next sample info: " << status;
223 mStatus = status;
224 return;
225 }
226 const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
227
228 if (!endOfStream) {
229 size_t bufferSize = 0;
230 uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
231 if (sourceBuffer == nullptr) {
232 LOG(ERROR) << "Decoder returned a NULL input buffer.";
233 mStatus = AMEDIA_ERROR_UNKNOWN;
234 return;
235 } else if (bufferSize < mSampleInfo.size) {
236 LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
237 mStatus = AMEDIA_ERROR_UNKNOWN;
238 return;
239 }
240
241 status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
242 mSampleInfo.size);
243 if (status != AMEDIA_OK) {
244 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
245 mStatus = status;
246 return;
247 }
248
249 mMediaSampleReader->advanceTrack(mTrackIndex);
250 } else {
251 LOG(DEBUG) << "EOS from source.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700252 mEosFromSource = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800253 }
254
255 status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
256 mSampleInfo.presentationTimeUs, mSampleInfo.flags);
257 if (status != AMEDIA_OK) {
258 LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
259 mStatus = status;
260 return;
261 }
262}
263
264void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
265 if (bufferIndex >= 0) {
266 bool needsRender = bufferInfo.size > 0;
267 AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
268 }
269
270 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
271 LOG(DEBUG) << "EOS from decoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700272 media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800273 if (status != AMEDIA_OK) {
274 LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
275 mStatus = status;
276 }
277 }
278}
279
280void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
281 AMediaCodecBufferInfo bufferInfo) {
282 if (bufferIndex >= 0) {
283 size_t sampleSize = 0;
Linus Nilssonc6221db2020-03-18 14:46:22 -0700284 uint8_t* buffer = AMediaCodec_getOutputBuffer(mEncoder.get(), bufferIndex, &sampleSize);
285
286 MediaSample::OnSampleReleasedCallback bufferReleaseCallback = [encoder = mEncoder](
287 MediaSample* sample) {
288 AMediaCodec_releaseOutputBuffer(encoder.get(), sample->bufferId, false /* render */);
289 };
Linus Nilsson0da327a2020-01-31 16:22:18 -0800290
291 std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
Linus Nilssonc6221db2020-03-18 14:46:22 -0700292 buffer, bufferInfo.offset, bufferIndex, bufferReleaseCallback);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800293 sample->info.size = bufferInfo.size;
294 sample->info.flags = bufferInfo.flags;
295 sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
296
Linus Nilssoncab39d82020-05-14 16:32:21 -0700297 const bool aborted = mOutputQueue->enqueue(sample);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800298 if (aborted) {
299 LOG(ERROR) << "Output sample queue was aborted. Stopping transcode.";
300 mStatus = AMEDIA_ERROR_IO; // TODO: Define custom error codes?
301 return;
302 }
303 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
Linus Nilssonc6221db2020-03-18 14:46:22 -0700304 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800305 LOG(DEBUG) << "Encoder output format changed: " << AMediaFormat_toString(newFormat);
306 }
307
308 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
309 LOG(DEBUG) << "EOS from encoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700310 mEosFromEncoder = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800311 }
312}
313
Linus Nilsson0da327a2020-01-31 16:22:18 -0800314media_status_t VideoTrackTranscoder::runTranscodeLoop() {
315 media_status_t status = AMEDIA_OK;
316
317 status = AMediaCodec_start(mDecoder);
318 if (status != AMEDIA_OK) {
319 LOG(ERROR) << "Unable to start video decoder: " << status;
320 return status;
321 }
322
Linus Nilssonc6221db2020-03-18 14:46:22 -0700323 status = AMediaCodec_start(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800324 if (status != AMEDIA_OK) {
325 LOG(ERROR) << "Unable to start video encoder: " << status;
326 AMediaCodec_stop(mDecoder);
327 return status;
328 }
329
330 // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700331 while (!mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800332 std::function<void()> message = mCodecMessageQueue.pop();
333 message();
334 }
335
336 // Return error if transcoding was stopped before it finished.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700337 if (mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800338 mStatus = AMEDIA_ERROR_UNKNOWN; // TODO: Define custom error codes?
339 }
340
341 AMediaCodec_stop(mDecoder);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700342 // TODO: Stop invalidates all buffers. Stop encoder when last buffer is released.
343 // AMediaCodec_stop(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800344 return mStatus;
345}
346
347void VideoTrackTranscoder::abortTranscodeLoop() {
348 // Push abort message to the front of the codec event queue.
349 mCodecMessageQueue.push([this] { mStopRequested = true; }, true /* front */);
350}
351
Linus Nilssoncab39d82020-05-14 16:32:21 -0700352std::shared_ptr<AMediaFormat> VideoTrackTranscoder::getOutputFormat() const {
353 return mDestinationFormat;
354}
355
Linus Nilsson0da327a2020-01-31 16:22:18 -0800356} // namespace android