blob: 69932f4d325a8b278f55c3d4119c455e1aaebbbb [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>
25#include <media/PassthroughTrackTranscoder.h>
26#include <media/VideoTrackTranscoder.h>
27#include <unistd.h>
28
29namespace android {
30
31#define DEFINE_FORMAT_VALUE_COPY_FUNC(_type, _typeName) \
32 static void copy##_typeName(const char* key, AMediaFormat* to, AMediaFormat* from) { \
33 _type value; \
34 if (AMediaFormat_get##_typeName(from, key, &value)) { \
35 AMediaFormat_set##_typeName(to, key, value); \
36 } \
37 }
38
39DEFINE_FORMAT_VALUE_COPY_FUNC(const char*, String);
40DEFINE_FORMAT_VALUE_COPY_FUNC(int64_t, Int64);
41DEFINE_FORMAT_VALUE_COPY_FUNC(int32_t, Int32);
42
43static AMediaFormat* mergeMediaFormats(AMediaFormat* base, AMediaFormat* overlay) {
44 if (base == nullptr || overlay == nullptr) {
45 LOG(ERROR) << "Cannot merge null formats";
46 return nullptr;
47 }
48
49 AMediaFormat* format = AMediaFormat_new();
50 if (AMediaFormat_copy(format, base) != AMEDIA_OK) {
51 AMediaFormat_delete(format);
52 return nullptr;
53 }
54
55 // Note: AMediaFormat does not expose a function for appending values from another format or for
56 // iterating over all values and keys in a format. Instead we define a static list of known keys
57 // along with their value types and copy the ones that are present. A better solution would be
58 // to either implement required functions in NDK or to parse the overlay format's string
59 // representation and copy all existing keys.
60 static const struct {
61 const char* key;
62 void (*copyValue)(const char* key, AMediaFormat* to, AMediaFormat* from);
63 } kSupportedConfigs[] = {
64 {AMEDIAFORMAT_KEY_MIME, copyString},
65 {AMEDIAFORMAT_KEY_DURATION, copyInt64},
66 {AMEDIAFORMAT_KEY_WIDTH, copyInt32},
67 {AMEDIAFORMAT_KEY_HEIGHT, copyInt32},
68 {AMEDIAFORMAT_KEY_BIT_RATE, copyInt32},
69 {AMEDIAFORMAT_KEY_PROFILE, copyInt32},
70 {AMEDIAFORMAT_KEY_LEVEL, copyInt32},
71 {AMEDIAFORMAT_KEY_COLOR_FORMAT, copyInt32},
72 {AMEDIAFORMAT_KEY_COLOR_RANGE, copyInt32},
73 {AMEDIAFORMAT_KEY_COLOR_STANDARD, copyInt32},
74 {AMEDIAFORMAT_KEY_COLOR_TRANSFER, copyInt32},
75 {AMEDIAFORMAT_KEY_FRAME_RATE, copyInt32},
76 {AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, copyInt32},
77 };
78
79 for (int i = 0; i < (sizeof(kSupportedConfigs) / sizeof(kSupportedConfigs[0])); ++i) {
80 kSupportedConfigs[i].copyValue(kSupportedConfigs[i].key, format, overlay);
81 }
82
83 return format;
84}
85
86void MediaTranscoder::sendCallback(media_status_t status) {
87 bool expected = false;
88 if (mCallbackSent.compare_exchange_strong(expected, true)) {
89 if (status == AMEDIA_OK) {
90 mCallbacks->onFinished(this);
91 } else {
92 mCallbacks->onError(this, status);
93 }
94
95 // Transcoding is done and the callback to the client has been sent, so tear down the
Chong Zhang308e91f2020-06-10 15:27:56 -070096 // pipeline but do it asynchronously to avoid deadlocks. If an error occurred, client
97 // should clean up the file.
Chong Zhanga2cc86b2020-06-17 16:56:49 -070098 std::thread asyncCancelThread{[self = shared_from_this()] { self->cancel(); }};
Linus Nilssoncab39d82020-05-14 16:32:21 -070099 asyncCancelThread.detach();
100 }
101}
102
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700103void MediaTranscoder::onTrackFormatAvailable(const MediaTrackTranscoder* transcoder) {
104 LOG(INFO) << "TrackTranscoder " << transcoder << " format available.";
105
106 std::scoped_lock lock{mTracksAddedMutex};
107
108 // Ignore duplicate format change.
109 if (mTracksAdded.count(transcoder) > 0) {
110 return;
111 }
112
113 // Add track to the writer.
114 const bool ok =
115 mSampleWriter->addTrack(transcoder->getOutputQueue(), transcoder->getOutputFormat());
116 if (!ok) {
117 LOG(ERROR) << "Unable to add track to sample writer.";
118 sendCallback(AMEDIA_ERROR_UNKNOWN);
119 return;
120 }
121
122 mTracksAdded.insert(transcoder);
123 if (mTracksAdded.size() == mTrackTranscoders.size()) {
124 LOG(INFO) << "Starting sample writer.";
125 bool started = mSampleWriter->start();
126 if (!started) {
127 LOG(ERROR) << "Unable to start sample writer.";
128 sendCallback(AMEDIA_ERROR_UNKNOWN);
129 }
130 }
131}
132
Linus Nilssoncab39d82020-05-14 16:32:21 -0700133void MediaTranscoder::onTrackFinished(const MediaTrackTranscoder* transcoder) {
134 LOG(DEBUG) << "TrackTranscoder " << transcoder << " finished";
135}
136
137void MediaTranscoder::onTrackError(const MediaTrackTranscoder* transcoder, media_status_t status) {
138 LOG(DEBUG) << "TrackTranscoder " << transcoder << " returned error " << status;
139 sendCallback(status);
140}
141
142void MediaTranscoder::onSampleWriterFinished(media_status_t status) {
143 LOG((status != AMEDIA_OK) ? ERROR : DEBUG) << "Sample writer finished with status " << status;
144 sendCallback(status);
145}
146
Chong Zhang308e91f2020-06-10 15:27:56 -0700147MediaTranscoder::MediaTranscoder(const std::shared_ptr<CallbackInterface>& callbacks)
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700148 : mCallbacks(callbacks) {}
Chong Zhang308e91f2020-06-10 15:27:56 -0700149
Linus Nilssoncab39d82020-05-14 16:32:21 -0700150std::shared_ptr<MediaTranscoder> MediaTranscoder::create(
151 const std::shared_ptr<CallbackInterface>& callbacks,
152 const std::shared_ptr<Parcel>& pausedState) {
153 if (pausedState != nullptr) {
154 LOG(ERROR) << "Initializing from paused state is currently not supported.";
155 return nullptr;
156 } else if (callbacks == nullptr) {
157 LOG(ERROR) << "Callbacks cannot be null";
158 return nullptr;
159 }
160
161 return std::shared_ptr<MediaTranscoder>(new MediaTranscoder(callbacks));
162}
163
Chong Zhang308e91f2020-06-10 15:27:56 -0700164media_status_t MediaTranscoder::configureSource(int fd) {
165 if (fd < 0) {
166 LOG(ERROR) << "Invalid source fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700167 return AMEDIA_ERROR_INVALID_PARAMETER;
168 }
169
170 const size_t fileSize = lseek(fd, 0, SEEK_END);
171 lseek(fd, 0, SEEK_SET);
172
173 mSampleReader = MediaSampleReaderNDK::createFromFd(fd, 0 /* offset */, fileSize);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700174
175 if (mSampleReader == nullptr) {
Chong Zhang308e91f2020-06-10 15:27:56 -0700176 LOG(ERROR) << "Unable to parse source fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700177 return AMEDIA_ERROR_UNSUPPORTED;
178 }
179
180 const size_t trackCount = mSampleReader->getTrackCount();
181 for (size_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
182 AMediaFormat* trackFormat = mSampleReader->getTrackFormat(static_cast<int>(trackIndex));
183 if (trackFormat == nullptr) {
184 LOG(ERROR) << "Track #" << trackIndex << " has no format";
185 return AMEDIA_ERROR_MALFORMED;
186 }
187
188 mSourceTrackFormats.emplace_back(trackFormat, &AMediaFormat_delete);
189 }
190
191 return AMEDIA_OK;
192}
193
194std::vector<std::shared_ptr<AMediaFormat>> MediaTranscoder::getTrackFormats() const {
195 // Return a deep copy of the formats to avoid the caller modifying our internal formats.
196 std::vector<std::shared_ptr<AMediaFormat>> trackFormats;
197 for (const std::shared_ptr<AMediaFormat>& sourceFormat : mSourceTrackFormats) {
198 AMediaFormat* copy = AMediaFormat_new();
199 AMediaFormat_copy(copy, sourceFormat.get());
200 trackFormats.emplace_back(copy, &AMediaFormat_delete);
201 }
202 return trackFormats;
203}
204
205media_status_t MediaTranscoder::configureTrackFormat(size_t trackIndex, AMediaFormat* trackFormat) {
206 if (mSampleReader == nullptr) {
207 LOG(ERROR) << "Source must be configured before tracks";
208 return AMEDIA_ERROR_INVALID_OPERATION;
209 } else if (trackIndex >= mSourceTrackFormats.size()) {
210 LOG(ERROR) << "Track index " << trackIndex
211 << " is out of bounds. Track count: " << mSourceTrackFormats.size();
212 return AMEDIA_ERROR_INVALID_PARAMETER;
213 }
214
215 std::unique_ptr<MediaTrackTranscoder> transcoder = nullptr;
216 std::shared_ptr<AMediaFormat> format = nullptr;
217
218 if (trackFormat == nullptr) {
219 transcoder = std::make_unique<PassthroughTrackTranscoder>(shared_from_this());
220 } else {
221 const char* srcMime = nullptr;
222 if (!AMediaFormat_getString(mSourceTrackFormats[trackIndex].get(), AMEDIAFORMAT_KEY_MIME,
223 &srcMime)) {
224 LOG(ERROR) << "Source track #" << trackIndex << " has no mime type";
225 return AMEDIA_ERROR_MALFORMED;
226 }
227
228 if (strncmp(srcMime, "video/", 6) != 0) {
229 LOG(ERROR) << "Only video tracks are supported for transcoding. Unable to configure "
230 "track #"
231 << trackIndex << " with mime " << srcMime;
232 return AMEDIA_ERROR_UNSUPPORTED;
233 }
234
235 const char* dstMime = nullptr;
236 if (AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &dstMime)) {
237 if (strncmp(dstMime, "video/", 6) != 0) {
238 LOG(ERROR) << "Unable to convert media types for track #" << trackIndex << ", from "
239 << srcMime << " to " << dstMime;
240 return AMEDIA_ERROR_UNSUPPORTED;
241 }
242 }
243
244 transcoder = std::make_unique<VideoTrackTranscoder>(shared_from_this());
245
246 AMediaFormat* mergedFormat =
247 mergeMediaFormats(mSourceTrackFormats[trackIndex].get(), trackFormat);
248 if (mergedFormat == nullptr) {
249 LOG(ERROR) << "Unable to merge source and destination formats";
250 return AMEDIA_ERROR_UNKNOWN;
251 }
252
253 format = std::shared_ptr<AMediaFormat>(mergedFormat, &AMediaFormat_delete);
254 }
255
256 media_status_t status = transcoder->configure(mSampleReader, trackIndex, format);
257 if (status != AMEDIA_OK) {
258 LOG(ERROR) << "Configure track transcoder for track #" << trackIndex << " returned error "
259 << status;
260 return status;
261 }
262
263 mTrackTranscoders.emplace_back(std::move(transcoder));
264 return AMEDIA_OK;
265}
266
Chong Zhang308e91f2020-06-10 15:27:56 -0700267media_status_t MediaTranscoder::configureDestination(int fd) {
268 if (fd < 0) {
269 LOG(ERROR) << "Invalid destination fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700270 return AMEDIA_ERROR_INVALID_PARAMETER;
Chong Zhang308e91f2020-06-10 15:27:56 -0700271 }
272
273 if (mSampleWriter != nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700274 LOG(ERROR) << "Destination is already configured.";
275 return AMEDIA_ERROR_INVALID_OPERATION;
276 }
277
Linus Nilssoncab39d82020-05-14 16:32:21 -0700278 mSampleWriter = std::make_unique<MediaSampleWriter>();
279 const bool initOk = mSampleWriter->init(
280 fd, std::bind(&MediaTranscoder::onSampleWriterFinished, this, std::placeholders::_1));
Linus Nilssoncab39d82020-05-14 16:32:21 -0700281
282 if (!initOk) {
Chong Zhang308e91f2020-06-10 15:27:56 -0700283 LOG(ERROR) << "Unable to initialize sample writer with destination fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700284 mSampleWriter.reset();
285 return AMEDIA_ERROR_UNKNOWN;
286 }
287
288 return AMEDIA_OK;
289}
290
291media_status_t MediaTranscoder::start() {
292 if (mTrackTranscoders.size() < 1) {
293 LOG(ERROR) << "Unable to start, no tracks are configured.";
294 return AMEDIA_ERROR_INVALID_OPERATION;
295 } else if (mSampleWriter == nullptr) {
296 LOG(ERROR) << "Unable to start, destination is not configured";
297 return AMEDIA_ERROR_INVALID_OPERATION;
298 }
299
Linus Nilssoncab39d82020-05-14 16:32:21 -0700300 // Start transcoders
301 for (auto& transcoder : mTrackTranscoders) {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700302 bool started = transcoder->start();
Linus Nilssoncab39d82020-05-14 16:32:21 -0700303 if (!started) {
304 LOG(ERROR) << "Unable to start track transcoder.";
Chong Zhang308e91f2020-06-10 15:27:56 -0700305 cancel();
Linus Nilssoncab39d82020-05-14 16:32:21 -0700306 return AMEDIA_ERROR_UNKNOWN;
307 }
308 }
309 return AMEDIA_OK;
310}
311
312media_status_t MediaTranscoder::pause(std::shared_ptr<const Parcelable>* pausedState) {
313 (void)pausedState;
314 LOG(ERROR) << "Pause is not currently supported";
315 return AMEDIA_ERROR_UNSUPPORTED;
316}
317
318media_status_t MediaTranscoder::resume() {
319 LOG(ERROR) << "Resume is not currently supported";
320 return AMEDIA_ERROR_UNSUPPORTED;
321}
322
Chong Zhang308e91f2020-06-10 15:27:56 -0700323media_status_t MediaTranscoder::cancel() {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700324 bool expected = false;
325 if (!mCancelled.compare_exchange_strong(expected, true)) {
326 // Already cancelled.
327 return AMEDIA_OK;
328 }
329
330 mSampleWriter->stop();
331 for (auto& transcoder : mTrackTranscoders) {
332 transcoder->stop();
333 }
334
Linus Nilssoncab39d82020-05-14 16:32:21 -0700335 return AMEDIA_OK;
336}
337
338} // namespace android