blob: 311e9be739fdb0f134fb2c192142320c240bbcc1 [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
33template <typename T>
34void VideoTrackTranscoder::BlockingQueue<T>::push(T const& value, bool front) {
35 {
36 std::unique_lock<std::mutex> lock(mMutex);
37 if (front) {
38 mQueue.push_front(value);
39 } else {
40 mQueue.push_back(value);
41 }
42 }
43 mCondition.notify_one();
44}
45
46template <typename T>
47T VideoTrackTranscoder::BlockingQueue<T>::pop() {
48 std::unique_lock<std::mutex> lock(mMutex);
49 while (mQueue.empty()) {
50 mCondition.wait(lock);
51 }
52 T value = mQueue.front();
53 mQueue.pop_front();
54 return value;
55}
56
57// Dispatch responses to codec callbacks onto the message queue.
58struct AsyncCodecCallbackDispatch {
59 static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
60 VideoTrackTranscoder* transcoder = static_cast<VideoTrackTranscoder*>(userdata);
61 if (codec == transcoder->mDecoder) {
62 transcoder->mCodecMessageQueue.push(
63 [transcoder, index] { transcoder->enqueueInputSample(index); });
64 }
65 }
66
67 static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
68 AMediaCodecBufferInfo* bufferInfoPtr) {
69 VideoTrackTranscoder* transcoder = static_cast<VideoTrackTranscoder*>(userdata);
70 AMediaCodecBufferInfo bufferInfo = *bufferInfoPtr;
71 transcoder->mCodecMessageQueue.push([transcoder, index, codec, bufferInfo] {
72 if (codec == transcoder->mDecoder) {
73 transcoder->transferBuffer(index, bufferInfo);
Linus Nilssonc6221db2020-03-18 14:46:22 -070074 } else if (codec == transcoder->mEncoder.get()) {
Linus Nilsson0da327a2020-01-31 16:22:18 -080075 transcoder->dequeueOutputSample(index, bufferInfo);
76 }
77 });
78 }
79
80 static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
81 VideoTrackTranscoder* transcoder = static_cast<VideoTrackTranscoder*>(userdata);
82 const char* kCodecName = (codec == transcoder->mDecoder ? "Decoder" : "Encoder");
83 LOG(DEBUG) << kCodecName << " format changed: " << AMediaFormat_toString(format);
84 }
85
86 static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t error,
87 int32_t actionCode, const char* detail) {
88 LOG(ERROR) << "Error from codec " << codec << ", userdata " << userdata << ", error "
89 << error << ", action " << actionCode << ", detail " << detail;
90 VideoTrackTranscoder* transcoder = static_cast<VideoTrackTranscoder*>(userdata);
91 transcoder->mCodecMessageQueue.push(
92 [transcoder, error] {
93 transcoder->mStatus = error;
94 transcoder->mStopRequested = true;
95 },
96 true);
97 }
98};
99
100VideoTrackTranscoder::~VideoTrackTranscoder() {
101 if (mDecoder != nullptr) {
102 AMediaCodec_delete(mDecoder);
103 }
104
Linus Nilsson0da327a2020-01-31 16:22:18 -0800105 if (mSurface != nullptr) {
106 ANativeWindow_release(mSurface);
107 }
108}
109
110// Creates and configures the codecs.
111media_status_t VideoTrackTranscoder::configureDestinationFormat(
112 const std::shared_ptr<AMediaFormat>& destinationFormat) {
113 media_status_t status = AMEDIA_OK;
114
115 if (destinationFormat == nullptr) {
116 LOG(ERROR) << "Destination format is null";
117 return AMEDIA_ERROR_INVALID_PARAMETER;
118 }
119
120 mDestinationFormat = destinationFormat;
121
122 // Create and configure the encoder.
123 const char* destinationMime = nullptr;
124 bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
125 &destinationMime);
126 if (!ok) {
127 LOG(ERROR) << "Destination MIME type is required for transcoding.";
128 return AMEDIA_ERROR_INVALID_PARAMETER;
129 }
130
Linus Nilssonc6221db2020-03-18 14:46:22 -0700131 AMediaCodec* encoder = AMediaCodec_createEncoderByType(destinationMime);
132 if (encoder == nullptr) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800133 LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
134 return AMEDIA_ERROR_UNSUPPORTED;
135 }
Linus Nilssonc6221db2020-03-18 14:46:22 -0700136 mEncoder = std::shared_ptr<AMediaCodec>(encoder,
137 std::bind(AMediaCodec_delete, std::placeholders::_1));
Linus Nilsson0da327a2020-01-31 16:22:18 -0800138
Linus Nilssonc6221db2020-03-18 14:46:22 -0700139 status = AMediaCodec_configure(mEncoder.get(), mDestinationFormat.get(), NULL /* surface */,
Linus Nilsson0da327a2020-01-31 16:22:18 -0800140 NULL /* crypto */, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
141 if (status != AMEDIA_OK) {
142 LOG(ERROR) << "Unable to configure video encoder: " << status;
143 return status;
144 }
145
Linus Nilssonc6221db2020-03-18 14:46:22 -0700146 status = AMediaCodec_createInputSurface(mEncoder.get(), &mSurface);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800147 if (status != AMEDIA_OK) {
148 LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
149 return status;
150 }
151
152 // Create and configure the decoder.
153 const char* sourceMime = nullptr;
154 ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
155 if (!ok) {
156 LOG(ERROR) << "Source MIME type is required for transcoding.";
157 return AMEDIA_ERROR_INVALID_PARAMETER;
158 }
159
160 mDecoder = AMediaCodec_createDecoderByType(sourceMime);
161 if (mDecoder == nullptr) {
162 LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
163 return AMEDIA_ERROR_UNSUPPORTED;
164 }
165
166 status = AMediaCodec_configure(mDecoder, mSourceFormat.get(), mSurface, NULL /* crypto */,
167 0 /* flags */);
168 if (status != AMEDIA_OK) {
169 LOG(ERROR) << "Unable to configure video decoder: " << status;
170 return status;
171 }
172
173 // Configure codecs to run in async mode.
174 AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
175 .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
176 .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
177 .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
178 .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
179
180 status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, this);
181 if (status != AMEDIA_OK) {
182 LOG(ERROR) << "Unable to set decoder to async mode: " << status;
183 return status;
184 }
185
Linus Nilssonc6221db2020-03-18 14:46:22 -0700186 status = AMediaCodec_setAsyncNotifyCallback(mEncoder.get(), asyncCodecCallbacks, this);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800187 if (status != AMEDIA_OK) {
188 LOG(ERROR) << "Unable to set encoder to async mode: " << status;
189 return status;
190 }
191
192 return AMEDIA_OK;
193}
194
195void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
196 media_status_t status = AMEDIA_OK;
197
Linus Nilssonc6221db2020-03-18 14:46:22 -0700198 if (mEosFromSource) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800199 return;
200 }
201
202 status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
203 if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
204 LOG(ERROR) << "Error getting next sample info: " << status;
205 mStatus = status;
206 return;
207 }
208 const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
209
210 if (!endOfStream) {
211 size_t bufferSize = 0;
212 uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
213 if (sourceBuffer == nullptr) {
214 LOG(ERROR) << "Decoder returned a NULL input buffer.";
215 mStatus = AMEDIA_ERROR_UNKNOWN;
216 return;
217 } else if (bufferSize < mSampleInfo.size) {
218 LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
219 mStatus = AMEDIA_ERROR_UNKNOWN;
220 return;
221 }
222
223 status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
224 mSampleInfo.size);
225 if (status != AMEDIA_OK) {
226 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
227 mStatus = status;
228 return;
229 }
230
231 mMediaSampleReader->advanceTrack(mTrackIndex);
232 } else {
233 LOG(DEBUG) << "EOS from source.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700234 mEosFromSource = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800235 }
236
237 status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
238 mSampleInfo.presentationTimeUs, mSampleInfo.flags);
239 if (status != AMEDIA_OK) {
240 LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
241 mStatus = status;
242 return;
243 }
244}
245
246void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
247 if (bufferIndex >= 0) {
248 bool needsRender = bufferInfo.size > 0;
249 AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
250 }
251
252 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
253 LOG(DEBUG) << "EOS from decoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700254 media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800255 if (status != AMEDIA_OK) {
256 LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
257 mStatus = status;
258 }
259 }
260}
261
262void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
263 AMediaCodecBufferInfo bufferInfo) {
264 if (bufferIndex >= 0) {
265 size_t sampleSize = 0;
Linus Nilssonc6221db2020-03-18 14:46:22 -0700266 uint8_t* buffer = AMediaCodec_getOutputBuffer(mEncoder.get(), bufferIndex, &sampleSize);
267
268 MediaSample::OnSampleReleasedCallback bufferReleaseCallback = [encoder = mEncoder](
269 MediaSample* sample) {
270 AMediaCodec_releaseOutputBuffer(encoder.get(), sample->bufferId, false /* render */);
271 };
Linus Nilsson0da327a2020-01-31 16:22:18 -0800272
273 std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
Linus Nilssonc6221db2020-03-18 14:46:22 -0700274 buffer, bufferInfo.offset, bufferIndex, bufferReleaseCallback);
Linus Nilsson0da327a2020-01-31 16:22:18 -0800275 sample->info.size = bufferInfo.size;
276 sample->info.flags = bufferInfo.flags;
277 sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
278
279 const bool aborted = mOutputQueue.enqueue(sample);
280 if (aborted) {
281 LOG(ERROR) << "Output sample queue was aborted. Stopping transcode.";
282 mStatus = AMEDIA_ERROR_IO; // TODO: Define custom error codes?
283 return;
284 }
285 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
Linus Nilssonc6221db2020-03-18 14:46:22 -0700286 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800287 LOG(DEBUG) << "Encoder output format changed: " << AMediaFormat_toString(newFormat);
288 }
289
290 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
291 LOG(DEBUG) << "EOS from encoder.";
Linus Nilssonc6221db2020-03-18 14:46:22 -0700292 mEosFromEncoder = true;
Linus Nilsson0da327a2020-01-31 16:22:18 -0800293 }
294}
295
Linus Nilsson0da327a2020-01-31 16:22:18 -0800296media_status_t VideoTrackTranscoder::runTranscodeLoop() {
297 media_status_t status = AMEDIA_OK;
298
299 status = AMediaCodec_start(mDecoder);
300 if (status != AMEDIA_OK) {
301 LOG(ERROR) << "Unable to start video decoder: " << status;
302 return status;
303 }
304
Linus Nilssonc6221db2020-03-18 14:46:22 -0700305 status = AMediaCodec_start(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800306 if (status != AMEDIA_OK) {
307 LOG(ERROR) << "Unable to start video encoder: " << status;
308 AMediaCodec_stop(mDecoder);
309 return status;
310 }
311
312 // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700313 while (!mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800314 std::function<void()> message = mCodecMessageQueue.pop();
315 message();
316 }
317
318 // Return error if transcoding was stopped before it finished.
Linus Nilssonc6221db2020-03-18 14:46:22 -0700319 if (mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
Linus Nilsson0da327a2020-01-31 16:22:18 -0800320 mStatus = AMEDIA_ERROR_UNKNOWN; // TODO: Define custom error codes?
321 }
322
323 AMediaCodec_stop(mDecoder);
Linus Nilssonc6221db2020-03-18 14:46:22 -0700324 AMediaCodec_stop(mEncoder.get());
Linus Nilsson0da327a2020-01-31 16:22:18 -0800325 return mStatus;
326}
327
328void VideoTrackTranscoder::abortTranscodeLoop() {
329 // Push abort message to the front of the codec event queue.
330 mCodecMessageQueue.push([this] { mStopRequested = true; }, true /* front */);
331}
332
333} // namespace android