blob: d89b58f1f881bb5a3b67d98595d81881449c30e3 [file] [log] [blame]
Linus Nilssoncab39d82020-05-14 16:32:21 -07001/*
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 "MediaTranscoder"
19
20#include <android-base/logging.h>
21#include <fcntl.h>
22#include <media/MediaSampleReaderNDK.h>
Chong Zhang308e91f2020-06-10 15:27:56 -070023#include <media/MediaSampleWriter.h>
Linus Nilssoncab39d82020-05-14 16:32:21 -070024#include <media/MediaTranscoder.h>
Linus Nilsson16d772b2020-09-29 19:21:11 -070025#include <media/NdkCommon.h>
Linus Nilssoncab39d82020-05-14 16:32:21 -070026#include <media/PassthroughTrackTranscoder.h>
27#include <media/VideoTrackTranscoder.h>
28#include <unistd.h>
29
30namespace android {
31
Linus Nilssoncab39d82020-05-14 16:32:21 -070032static AMediaFormat* mergeMediaFormats(AMediaFormat* base, AMediaFormat* overlay) {
33 if (base == nullptr || overlay == nullptr) {
34 LOG(ERROR) << "Cannot merge null formats";
35 return nullptr;
36 }
37
38 AMediaFormat* format = AMediaFormat_new();
39 if (AMediaFormat_copy(format, base) != AMEDIA_OK) {
40 AMediaFormat_delete(format);
41 return nullptr;
42 }
43
44 // Note: AMediaFormat does not expose a function for appending values from another format or for
45 // iterating over all values and keys in a format. Instead we define a static list of known keys
46 // along with their value types and copy the ones that are present. A better solution would be
47 // to either implement required functions in NDK or to parse the overlay format's string
48 // representation and copy all existing keys.
Linus Nilsson16d772b2020-09-29 19:21:11 -070049 static const AMediaFormatUtils::EntryCopier kSupportedFormatEntries[] = {
50 ENTRY_COPIER(AMEDIAFORMAT_KEY_MIME, String),
51 ENTRY_COPIER(AMEDIAFORMAT_KEY_DURATION, Int64),
52 ENTRY_COPIER(AMEDIAFORMAT_KEY_WIDTH, Int32),
53 ENTRY_COPIER(AMEDIAFORMAT_KEY_HEIGHT, Int32),
54 ENTRY_COPIER(AMEDIAFORMAT_KEY_BIT_RATE, Int32),
55 ENTRY_COPIER(AMEDIAFORMAT_KEY_PROFILE, Int32),
56 ENTRY_COPIER(AMEDIAFORMAT_KEY_LEVEL, Int32),
57 ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_FORMAT, Int32),
58 ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
59 ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
60 ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
61 ENTRY_COPIER(AMEDIAFORMAT_KEY_FRAME_RATE, Int32),
62 ENTRY_COPIER(AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, Int32),
63 ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
64 ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
Linus Nilssoncab39d82020-05-14 16:32:21 -070065 };
Linus Nilsson16d772b2020-09-29 19:21:11 -070066 const size_t entryCount = sizeof(kSupportedFormatEntries) / sizeof(kSupportedFormatEntries[0]);
Linus Nilssoncab39d82020-05-14 16:32:21 -070067
Linus Nilsson16d772b2020-09-29 19:21:11 -070068 AMediaFormatUtils::CopyFormatEntries(overlay, format, kSupportedFormatEntries, entryCount);
Linus Nilssoncab39d82020-05-14 16:32:21 -070069 return format;
70}
71
72void MediaTranscoder::sendCallback(media_status_t status) {
Chong Zhangb55c5452020-06-26 14:32:12 -070073 // If the transcoder is already cancelled explicitly, don't send any error callbacks.
74 // Tracks and sample writer will report errors for abort. However, currently we can't
75 // tell it apart from real errors. Ideally we still want to report real errors back
76 // to client, as there is a small chance that explicit abort and the real error come
77 // at around the same time, we should report that if abort has a specific error code.
78 // On the other hand, if the transcoder actually finished (status is AMEDIA_OK) at around
79 // the same time of the abort, we should still report the finish back to the client.
80 if (mCancelled && status != AMEDIA_OK) {
81 return;
82 }
83
Linus Nilssoncab39d82020-05-14 16:32:21 -070084 bool expected = false;
85 if (mCallbackSent.compare_exchange_strong(expected, true)) {
86 if (status == AMEDIA_OK) {
87 mCallbacks->onFinished(this);
88 } else {
89 mCallbacks->onError(this, status);
90 }
91
92 // Transcoding is done and the callback to the client has been sent, so tear down the
Chong Zhang308e91f2020-06-10 15:27:56 -070093 // pipeline but do it asynchronously to avoid deadlocks. If an error occurred, client
94 // should clean up the file.
Chong Zhanga2cc86b2020-06-17 16:56:49 -070095 std::thread asyncCancelThread{[self = shared_from_this()] { self->cancel(); }};
Linus Nilssoncab39d82020-05-14 16:32:21 -070096 asyncCancelThread.detach();
97 }
98}
99
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700100void MediaTranscoder::onTrackFormatAvailable(const MediaTrackTranscoder* transcoder) {
101 LOG(INFO) << "TrackTranscoder " << transcoder << " format available.";
102
103 std::scoped_lock lock{mTracksAddedMutex};
104
105 // Ignore duplicate format change.
106 if (mTracksAdded.count(transcoder) > 0) {
107 return;
108 }
109
110 // Add track to the writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700111 auto consumer = mSampleWriter->addTrack(transcoder->getOutputFormat());
112 if (consumer == nullptr) {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700113 LOG(ERROR) << "Unable to add track to sample writer.";
114 sendCallback(AMEDIA_ERROR_UNKNOWN);
115 return;
116 }
117
Linus Nilssonc31d2492020-09-23 12:30:00 -0700118 MediaTrackTranscoder* mutableTranscoder = const_cast<MediaTrackTranscoder*>(transcoder);
119 mutableTranscoder->setSampleConsumer(consumer);
120
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700121 mTracksAdded.insert(transcoder);
122 if (mTracksAdded.size() == mTrackTranscoders.size()) {
Linus Nilsson6233fed2020-08-13 15:15:14 -0700123 // Enable sequential access mode on the sample reader to achieve optimal read performance.
124 // This has to wait until all tracks have delivered their output formats and the sample
125 // writer is started. Otherwise the tracks will not get their output sample queues drained
126 // and the transcoder could hang due to one track running out of buffers and blocking the
127 // other tracks from reading source samples before they could output their formats.
128 mSampleReader->setEnforceSequentialAccess(true);
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700129 LOG(INFO) << "Starting sample writer.";
130 bool started = mSampleWriter->start();
131 if (!started) {
132 LOG(ERROR) << "Unable to start sample writer.";
133 sendCallback(AMEDIA_ERROR_UNKNOWN);
134 }
135 }
136}
137
Linus Nilssoncab39d82020-05-14 16:32:21 -0700138void MediaTranscoder::onTrackFinished(const MediaTrackTranscoder* transcoder) {
139 LOG(DEBUG) << "TrackTranscoder " << transcoder << " finished";
140}
141
142void MediaTranscoder::onTrackError(const MediaTrackTranscoder* transcoder, media_status_t status) {
Linus Nilsson93cf9132020-09-24 12:12:48 -0700143 LOG(ERROR) << "TrackTranscoder " << transcoder << " returned error " << status;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700144 sendCallback(status);
145}
146
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700147void MediaTranscoder::onFinished(const MediaSampleWriter* writer __unused, media_status_t status) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700148 LOG((status != AMEDIA_OK) ? ERROR : DEBUG) << "Sample writer finished with status " << status;
149 sendCallback(status);
150}
151
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700152void MediaTranscoder::onProgressUpdate(const MediaSampleWriter* writer __unused, int32_t progress) {
153 // Dispatch progress updated to the client.
154 mCallbacks->onProgressUpdate(this, progress);
155}
156
Chong Zhang308e91f2020-06-10 15:27:56 -0700157MediaTranscoder::MediaTranscoder(const std::shared_ptr<CallbackInterface>& callbacks)
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700158 : mCallbacks(callbacks) {}
Chong Zhang308e91f2020-06-10 15:27:56 -0700159
Linus Nilssoncab39d82020-05-14 16:32:21 -0700160std::shared_ptr<MediaTranscoder> MediaTranscoder::create(
161 const std::shared_ptr<CallbackInterface>& callbacks,
Chong Zhange4e088f2020-10-21 19:10:42 -0700162 const std::shared_ptr<ndk::ScopedAParcel>& pausedState) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700163 if (pausedState != nullptr) {
Chong Zhangb55c5452020-06-26 14:32:12 -0700164 LOG(INFO) << "Initializing from paused state.";
165 }
166 if (callbacks == nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700167 LOG(ERROR) << "Callbacks cannot be null";
168 return nullptr;
169 }
170
171 return std::shared_ptr<MediaTranscoder>(new MediaTranscoder(callbacks));
172}
173
Chong Zhang308e91f2020-06-10 15:27:56 -0700174media_status_t MediaTranscoder::configureSource(int fd) {
175 if (fd < 0) {
176 LOG(ERROR) << "Invalid source fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700177 return AMEDIA_ERROR_INVALID_PARAMETER;
178 }
179
180 const size_t fileSize = lseek(fd, 0, SEEK_END);
181 lseek(fd, 0, SEEK_SET);
182
183 mSampleReader = MediaSampleReaderNDK::createFromFd(fd, 0 /* offset */, fileSize);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700184
185 if (mSampleReader == nullptr) {
Chong Zhang308e91f2020-06-10 15:27:56 -0700186 LOG(ERROR) << "Unable to parse source fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700187 return AMEDIA_ERROR_UNSUPPORTED;
188 }
189
190 const size_t trackCount = mSampleReader->getTrackCount();
191 for (size_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
192 AMediaFormat* trackFormat = mSampleReader->getTrackFormat(static_cast<int>(trackIndex));
193 if (trackFormat == nullptr) {
194 LOG(ERROR) << "Track #" << trackIndex << " has no format";
195 return AMEDIA_ERROR_MALFORMED;
196 }
197
198 mSourceTrackFormats.emplace_back(trackFormat, &AMediaFormat_delete);
199 }
200
201 return AMEDIA_OK;
202}
203
204std::vector<std::shared_ptr<AMediaFormat>> MediaTranscoder::getTrackFormats() const {
205 // Return a deep copy of the formats to avoid the caller modifying our internal formats.
206 std::vector<std::shared_ptr<AMediaFormat>> trackFormats;
207 for (const std::shared_ptr<AMediaFormat>& sourceFormat : mSourceTrackFormats) {
208 AMediaFormat* copy = AMediaFormat_new();
209 AMediaFormat_copy(copy, sourceFormat.get());
210 trackFormats.emplace_back(copy, &AMediaFormat_delete);
211 }
212 return trackFormats;
213}
214
215media_status_t MediaTranscoder::configureTrackFormat(size_t trackIndex, AMediaFormat* trackFormat) {
216 if (mSampleReader == nullptr) {
217 LOG(ERROR) << "Source must be configured before tracks";
218 return AMEDIA_ERROR_INVALID_OPERATION;
219 } else if (trackIndex >= mSourceTrackFormats.size()) {
220 LOG(ERROR) << "Track index " << trackIndex
221 << " is out of bounds. Track count: " << mSourceTrackFormats.size();
222 return AMEDIA_ERROR_INVALID_PARAMETER;
223 }
224
Linus Nilsson6233fed2020-08-13 15:15:14 -0700225 media_status_t status = mSampleReader->selectTrack(trackIndex);
226 if (status != AMEDIA_OK) {
227 LOG(ERROR) << "Unable to select track " << trackIndex;
228 return status;
229 }
230
Linus Nilssone4716f22020-07-10 16:07:57 -0700231 std::shared_ptr<MediaTrackTranscoder> transcoder;
232 std::shared_ptr<AMediaFormat> format;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700233
234 if (trackFormat == nullptr) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700235 transcoder = std::make_shared<PassthroughTrackTranscoder>(shared_from_this());
Linus Nilssoncab39d82020-05-14 16:32:21 -0700236 } else {
237 const char* srcMime = nullptr;
238 if (!AMediaFormat_getString(mSourceTrackFormats[trackIndex].get(), AMEDIAFORMAT_KEY_MIME,
239 &srcMime)) {
240 LOG(ERROR) << "Source track #" << trackIndex << " has no mime type";
241 return AMEDIA_ERROR_MALFORMED;
242 }
243
244 if (strncmp(srcMime, "video/", 6) != 0) {
245 LOG(ERROR) << "Only video tracks are supported for transcoding. Unable to configure "
246 "track #"
247 << trackIndex << " with mime " << srcMime;
248 return AMEDIA_ERROR_UNSUPPORTED;
249 }
250
251 const char* dstMime = nullptr;
252 if (AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &dstMime)) {
253 if (strncmp(dstMime, "video/", 6) != 0) {
254 LOG(ERROR) << "Unable to convert media types for track #" << trackIndex << ", from "
255 << srcMime << " to " << dstMime;
256 return AMEDIA_ERROR_UNSUPPORTED;
257 }
258 }
259
Linus Nilssone4716f22020-07-10 16:07:57 -0700260 transcoder = VideoTrackTranscoder::create(shared_from_this());
Linus Nilssoncab39d82020-05-14 16:32:21 -0700261
262 AMediaFormat* mergedFormat =
263 mergeMediaFormats(mSourceTrackFormats[trackIndex].get(), trackFormat);
264 if (mergedFormat == nullptr) {
265 LOG(ERROR) << "Unable to merge source and destination formats";
266 return AMEDIA_ERROR_UNKNOWN;
267 }
268
269 format = std::shared_ptr<AMediaFormat>(mergedFormat, &AMediaFormat_delete);
270 }
271
Chong Zhangc8c88cc2020-09-17 12:50:49 -0700272 status = transcoder->configure(mSampleReader, trackIndex, format);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700273 if (status != AMEDIA_OK) {
274 LOG(ERROR) << "Configure track transcoder for track #" << trackIndex << " returned error "
275 << status;
276 return status;
277 }
278
279 mTrackTranscoders.emplace_back(std::move(transcoder));
280 return AMEDIA_OK;
281}
282
Chong Zhang308e91f2020-06-10 15:27:56 -0700283media_status_t MediaTranscoder::configureDestination(int fd) {
284 if (fd < 0) {
285 LOG(ERROR) << "Invalid destination fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700286 return AMEDIA_ERROR_INVALID_PARAMETER;
Chong Zhang308e91f2020-06-10 15:27:56 -0700287 }
288
289 if (mSampleWriter != nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700290 LOG(ERROR) << "Destination is already configured.";
291 return AMEDIA_ERROR_INVALID_OPERATION;
292 }
293
Linus Nilssonc31d2492020-09-23 12:30:00 -0700294 mSampleWriter = MediaSampleWriter::Create();
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700295 const bool initOk = mSampleWriter->init(fd, shared_from_this());
Linus Nilssoncab39d82020-05-14 16:32:21 -0700296
297 if (!initOk) {
Chong Zhang308e91f2020-06-10 15:27:56 -0700298 LOG(ERROR) << "Unable to initialize sample writer with destination fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700299 mSampleWriter.reset();
300 return AMEDIA_ERROR_UNKNOWN;
301 }
302
303 return AMEDIA_OK;
304}
305
306media_status_t MediaTranscoder::start() {
307 if (mTrackTranscoders.size() < 1) {
308 LOG(ERROR) << "Unable to start, no tracks are configured.";
309 return AMEDIA_ERROR_INVALID_OPERATION;
310 } else if (mSampleWriter == nullptr) {
311 LOG(ERROR) << "Unable to start, destination is not configured";
312 return AMEDIA_ERROR_INVALID_OPERATION;
313 }
314
Linus Nilssoncab39d82020-05-14 16:32:21 -0700315 // Start transcoders
316 for (auto& transcoder : mTrackTranscoders) {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700317 bool started = transcoder->start();
Linus Nilssoncab39d82020-05-14 16:32:21 -0700318 if (!started) {
319 LOG(ERROR) << "Unable to start track transcoder.";
Chong Zhang308e91f2020-06-10 15:27:56 -0700320 cancel();
Linus Nilssoncab39d82020-05-14 16:32:21 -0700321 return AMEDIA_ERROR_UNKNOWN;
322 }
323 }
324 return AMEDIA_OK;
325}
326
Chong Zhange4e088f2020-10-21 19:10:42 -0700327media_status_t MediaTranscoder::pause(std::shared_ptr<ndk::ScopedAParcel>* pausedState) {
Chong Zhangb55c5452020-06-26 14:32:12 -0700328 // TODO: write internal states to parcel.
Chong Zhange4e088f2020-10-21 19:10:42 -0700329 *pausedState = std::shared_ptr<::ndk::ScopedAParcel>(new ::ndk::ScopedAParcel());
Chong Zhangb55c5452020-06-26 14:32:12 -0700330 return cancel();
Linus Nilssoncab39d82020-05-14 16:32:21 -0700331}
332
333media_status_t MediaTranscoder::resume() {
Chong Zhangb55c5452020-06-26 14:32:12 -0700334 // TODO: restore internal states from parcel.
335 return start();
Linus Nilssoncab39d82020-05-14 16:32:21 -0700336}
337
Chong Zhang308e91f2020-06-10 15:27:56 -0700338media_status_t MediaTranscoder::cancel() {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700339 bool expected = false;
340 if (!mCancelled.compare_exchange_strong(expected, true)) {
341 // Already cancelled.
342 return AMEDIA_OK;
343 }
344
345 mSampleWriter->stop();
Linus Nilsson6233fed2020-08-13 15:15:14 -0700346 mSampleReader->setEnforceSequentialAccess(false);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700347 for (auto& transcoder : mTrackTranscoders) {
348 transcoder->stop();
349 }
350
Linus Nilssoncab39d82020-05-14 16:32:21 -0700351 return AMEDIA_OK;
352}
353
354} // namespace android