blob: 9cd36cf5cd1a5d77388fc46cb456a10e81bf870a [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);
74 } else if (codec == transcoder->mEncoder) {
75 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
105 if (mEncoder != nullptr) {
106 AMediaCodec_delete(mEncoder);
107 }
108
109 if (mSurface != nullptr) {
110 ANativeWindow_release(mSurface);
111 }
112}
113
114// Creates and configures the codecs.
115media_status_t VideoTrackTranscoder::configureDestinationFormat(
116 const std::shared_ptr<AMediaFormat>& destinationFormat) {
117 media_status_t status = AMEDIA_OK;
118
119 if (destinationFormat == nullptr) {
120 LOG(ERROR) << "Destination format is null";
121 return AMEDIA_ERROR_INVALID_PARAMETER;
122 }
123
124 mDestinationFormat = destinationFormat;
125
126 // Create and configure the encoder.
127 const char* destinationMime = nullptr;
128 bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
129 &destinationMime);
130 if (!ok) {
131 LOG(ERROR) << "Destination MIME type is required for transcoding.";
132 return AMEDIA_ERROR_INVALID_PARAMETER;
133 }
134
135 mEncoder = AMediaCodec_createEncoderByType(destinationMime);
136 if (mEncoder == nullptr) {
137 LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
138 return AMEDIA_ERROR_UNSUPPORTED;
139 }
140
141 status = AMediaCodec_configure(mEncoder, mDestinationFormat.get(), NULL /* surface */,
142 NULL /* crypto */, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
143 if (status != AMEDIA_OK) {
144 LOG(ERROR) << "Unable to configure video encoder: " << status;
145 return status;
146 }
147
148 status = AMediaCodec_createInputSurface(mEncoder, &mSurface);
149 if (status != AMEDIA_OK) {
150 LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
151 return status;
152 }
153
154 // Create and configure the decoder.
155 const char* sourceMime = nullptr;
156 ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
157 if (!ok) {
158 LOG(ERROR) << "Source MIME type is required for transcoding.";
159 return AMEDIA_ERROR_INVALID_PARAMETER;
160 }
161
162 mDecoder = AMediaCodec_createDecoderByType(sourceMime);
163 if (mDecoder == nullptr) {
164 LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
165 return AMEDIA_ERROR_UNSUPPORTED;
166 }
167
168 status = AMediaCodec_configure(mDecoder, mSourceFormat.get(), mSurface, NULL /* crypto */,
169 0 /* flags */);
170 if (status != AMEDIA_OK) {
171 LOG(ERROR) << "Unable to configure video decoder: " << status;
172 return status;
173 }
174
175 // Configure codecs to run in async mode.
176 AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
177 .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
178 .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
179 .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
180 .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
181
182 status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, this);
183 if (status != AMEDIA_OK) {
184 LOG(ERROR) << "Unable to set decoder to async mode: " << status;
185 return status;
186 }
187
188 status = AMediaCodec_setAsyncNotifyCallback(mEncoder, asyncCodecCallbacks, this);
189 if (status != AMEDIA_OK) {
190 LOG(ERROR) << "Unable to set encoder to async mode: " << status;
191 return status;
192 }
193
194 return AMEDIA_OK;
195}
196
197void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
198 media_status_t status = AMEDIA_OK;
199
200 if (mEOSFromSource) {
201 return;
202 }
203
204 status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
205 if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
206 LOG(ERROR) << "Error getting next sample info: " << status;
207 mStatus = status;
208 return;
209 }
210 const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
211
212 if (!endOfStream) {
213 size_t bufferSize = 0;
214 uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
215 if (sourceBuffer == nullptr) {
216 LOG(ERROR) << "Decoder returned a NULL input buffer.";
217 mStatus = AMEDIA_ERROR_UNKNOWN;
218 return;
219 } else if (bufferSize < mSampleInfo.size) {
220 LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
221 mStatus = AMEDIA_ERROR_UNKNOWN;
222 return;
223 }
224
225 status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
226 mSampleInfo.size);
227 if (status != AMEDIA_OK) {
228 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
229 mStatus = status;
230 return;
231 }
232
233 mMediaSampleReader->advanceTrack(mTrackIndex);
234 } else {
235 LOG(DEBUG) << "EOS from source.";
236 mEOSFromSource = true;
237 }
238
239 status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
240 mSampleInfo.presentationTimeUs, mSampleInfo.flags);
241 if (status != AMEDIA_OK) {
242 LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
243 mStatus = status;
244 return;
245 }
246}
247
248void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
249 if (bufferIndex >= 0) {
250 bool needsRender = bufferInfo.size > 0;
251 AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
252 }
253
254 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
255 LOG(DEBUG) << "EOS from decoder.";
256 media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder);
257 if (status != AMEDIA_OK) {
258 LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
259 mStatus = status;
260 }
261 }
262}
263
264void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
265 AMediaCodecBufferInfo bufferInfo) {
266 if (bufferIndex >= 0) {
267 size_t sampleSize = 0;
268 uint8_t* buffer = AMediaCodec_getOutputBuffer(mEncoder, bufferIndex, &sampleSize);
269
270 std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
271 buffer, bufferInfo.offset, bufferIndex,
272 std::bind(&VideoTrackTranscoder::releaseOutputSample, this, std::placeholders::_1));
273 sample->info.size = bufferInfo.size;
274 sample->info.flags = bufferInfo.flags;
275 sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
276
277 const bool aborted = mOutputQueue.enqueue(sample);
278 if (aborted) {
279 LOG(ERROR) << "Output sample queue was aborted. Stopping transcode.";
280 mStatus = AMEDIA_ERROR_IO; // TODO: Define custom error codes?
281 return;
282 }
283 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
284 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mEncoder);
285 LOG(DEBUG) << "Encoder output format changed: " << AMediaFormat_toString(newFormat);
286 }
287
288 if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
289 LOG(DEBUG) << "EOS from encoder.";
290 mEOSFromEncoder = true;
291 }
292}
293
294void VideoTrackTranscoder::releaseOutputSample(MediaSample* sample) {
295 AMediaCodec_releaseOutputBuffer(mEncoder, sample->bufferId, false /* render */);
296}
297
298media_status_t VideoTrackTranscoder::runTranscodeLoop() {
299 media_status_t status = AMEDIA_OK;
300
301 status = AMediaCodec_start(mDecoder);
302 if (status != AMEDIA_OK) {
303 LOG(ERROR) << "Unable to start video decoder: " << status;
304 return status;
305 }
306
307 status = AMediaCodec_start(mEncoder);
308 if (status != AMEDIA_OK) {
309 LOG(ERROR) << "Unable to start video encoder: " << status;
310 AMediaCodec_stop(mDecoder);
311 return status;
312 }
313
314 // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
315 while (!mStopRequested && !mEOSFromEncoder && mStatus == AMEDIA_OK) {
316 std::function<void()> message = mCodecMessageQueue.pop();
317 message();
318 }
319
320 // Return error if transcoding was stopped before it finished.
321 if (mStopRequested && !mEOSFromEncoder && mStatus == AMEDIA_OK) {
322 mStatus = AMEDIA_ERROR_UNKNOWN; // TODO: Define custom error codes?
323 }
324
325 AMediaCodec_stop(mDecoder);
326 AMediaCodec_stop(mEncoder);
327 return mStatus;
328}
329
330void VideoTrackTranscoder::abortTranscodeLoop() {
331 // Push abort message to the front of the codec event queue.
332 mCodecMessageQueue.push([this] { mStopRequested = true; }, true /* front */);
333}
334
335} // namespace android