blob: 879241e963ff8505ee9a162d43a271b8e765ac74 [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 Nilssona676a9a2021-03-15 18:22:43 -070032static std::shared_ptr<AMediaFormat> createVideoTrackFormat(AMediaFormat* srcFormat,
33 AMediaFormat* options) {
34 if (srcFormat == nullptr || options == nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -070035 LOG(ERROR) << "Cannot merge null formats";
36 return nullptr;
37 }
38
Linus Nilssona676a9a2021-03-15 18:22:43 -070039 // ------- Define parameters to copy from the source track format -------
40 std::vector<AMediaFormatUtils::EntryCopier> srcParamsToCopy{
41 ENTRY_COPIER(AMEDIAFORMAT_KEY_MIME, String),
42 ENTRY_COPIER(AMEDIAFORMAT_KEY_DURATION, Int64),
43 ENTRY_COPIER(AMEDIAFORMAT_KEY_WIDTH, Int32),
44 ENTRY_COPIER(AMEDIAFORMAT_KEY_HEIGHT, Int32),
45 ENTRY_COPIER(AMEDIAFORMAT_KEY_FRAME_RATE, Int32),
46 ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
47 ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
48 ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
49 };
50
51 // If the destination codec is the same as the source codec, we can preserve profile and level
52 // from the source track as default values. Otherwise leave them unspecified.
53 const char *srcMime, *dstMime;
54 AMediaFormat_getString(srcFormat, AMEDIAFORMAT_KEY_MIME, &srcMime);
55 if (!AMediaFormat_getString(options, AMEDIAFORMAT_KEY_MIME, &dstMime) ||
56 strcmp(srcMime, dstMime) == 0) {
57 srcParamsToCopy.push_back(ENTRY_COPIER(AMEDIAFORMAT_KEY_PROFILE, String));
58 srcParamsToCopy.push_back(ENTRY_COPIER(AMEDIAFORMAT_KEY_LEVEL, String));
Linus Nilssoncab39d82020-05-14 16:32:21 -070059 }
60
Linus Nilssona676a9a2021-03-15 18:22:43 -070061 // ------- Define parameters to copy from the caller's options -------
62 static const std::vector<AMediaFormatUtils::EntryCopier> kSupportedOptions{
Linus Nilsson16d772b2020-09-29 19:21:11 -070063 ENTRY_COPIER(AMEDIAFORMAT_KEY_MIME, String),
64 ENTRY_COPIER(AMEDIAFORMAT_KEY_DURATION, Int64),
65 ENTRY_COPIER(AMEDIAFORMAT_KEY_WIDTH, Int32),
66 ENTRY_COPIER(AMEDIAFORMAT_KEY_HEIGHT, Int32),
67 ENTRY_COPIER(AMEDIAFORMAT_KEY_BIT_RATE, Int32),
68 ENTRY_COPIER(AMEDIAFORMAT_KEY_PROFILE, Int32),
69 ENTRY_COPIER(AMEDIAFORMAT_KEY_LEVEL, Int32),
Linus Nilsson16d772b2020-09-29 19:21:11 -070070 ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
71 ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
72 ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
73 ENTRY_COPIER(AMEDIAFORMAT_KEY_FRAME_RATE, Int32),
74 ENTRY_COPIER(AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, Int32),
75 ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
76 ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
Linus Nilssoncab39d82020-05-14 16:32:21 -070077 };
78
Linus Nilssona676a9a2021-03-15 18:22:43 -070079 // ------- Copy parameters from source and options to the destination -------
80 auto trackFormat = std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
81 AMediaFormatUtils::CopyFormatEntries(srcFormat, trackFormat.get(), srcParamsToCopy);
82 AMediaFormatUtils::CopyFormatEntries(options, trackFormat.get(), kSupportedOptions);
83 return trackFormat;
Linus Nilssoncab39d82020-05-14 16:32:21 -070084}
85
Linus Nilssonfdb3e332020-09-18 17:11:41 -070086void MediaTranscoder::onThreadFinished(const void* thread, media_status_t threadStatus,
87 bool threadStopped) {
88 LOG(DEBUG) << "Thread " << thread << " finished with status " << threadStatus << " stopped "
89 << threadStopped;
90
91 // Stop all threads if one reports an error.
92 if (threadStatus != AMEDIA_OK) {
93 requestStop(false /* stopOnSync */);
94 }
95
96 std::scoped_lock lock{mThreadStateMutex};
97
98 // Record the change.
99 mThreadStates[thread] = DONE;
100 if (threadStatus != AMEDIA_OK && mTranscoderStatus == AMEDIA_OK) {
101 mTranscoderStatus = threadStatus;
102 }
103
104 mTranscoderStopped |= threadStopped;
105
106 // Check if all threads are done. Note that if all transcoders have stopped but the sample
107 // writer has not yet started, it never will.
108 bool transcodersDone = true;
109 ThreadState sampleWriterState = PENDING;
110 for (const auto& it : mThreadStates) {
111 LOG(DEBUG) << " Thread " << it.first << " state" << it.second;
112 if (it.first == static_cast<const void*>(mSampleWriter.get())) {
113 sampleWriterState = it.second;
114 } else {
115 transcodersDone &= (it.second == DONE);
116 }
117 }
118 if (!transcodersDone || sampleWriterState == RUNNING) {
Chong Zhangb55c5452020-06-26 14:32:12 -0700119 return;
120 }
121
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700122 // All done. Send callback asynchronously and wake up threads waiting in cancel/pause.
123 mThreadsDone = true;
124 if (!mCallbackSent) {
125 std::thread asyncNotificationThread{[this, self = shared_from_this(),
126 status = mTranscoderStatus,
127 stopped = mTranscoderStopped] {
128 // If the transcoder was stopped that means a caller is waiting in stop or pause
129 // in which case we don't send a callback.
130 if (status != AMEDIA_OK) {
131 mCallbacks->onError(this, status);
132 } else if (!stopped) {
133 mCallbacks->onFinished(this);
134 }
135 mThreadsDoneSignal.notify_all();
136 }};
137 asyncNotificationThread.detach();
138 mCallbackSent = true;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700139 }
140}
141
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700142void MediaTranscoder::onTrackFormatAvailable(const MediaTrackTranscoder* transcoder) {
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700143 LOG(DEBUG) << "TrackTranscoder " << transcoder << " format available.";
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700144
145 std::scoped_lock lock{mTracksAddedMutex};
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700146 const void* sampleWriterPtr = static_cast<const void*>(mSampleWriter.get());
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700147
148 // Ignore duplicate format change.
149 if (mTracksAdded.count(transcoder) > 0) {
150 return;
151 }
152
153 // Add track to the writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700154 auto consumer = mSampleWriter->addTrack(transcoder->getOutputFormat());
155 if (consumer == nullptr) {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700156 LOG(ERROR) << "Unable to add track to sample writer.";
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700157 onThreadFinished(sampleWriterPtr, AMEDIA_ERROR_UNKNOWN, false /* stopped */);
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700158 return;
159 }
160
Linus Nilssond2bef932021-03-23 21:55:33 -0700161 // The sample writer is not yet started so notify the caller that progress is still made.
162 if (mHeartBeatIntervalUs > 0) {
163 mCallbacks->onHeartBeat(this);
164 }
165
Linus Nilssonc31d2492020-09-23 12:30:00 -0700166 MediaTrackTranscoder* mutableTranscoder = const_cast<MediaTrackTranscoder*>(transcoder);
167 mutableTranscoder->setSampleConsumer(consumer);
168
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700169 mTracksAdded.insert(transcoder);
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700170 bool errorStarting = false;
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700171 if (mTracksAdded.size() == mTrackTranscoders.size()) {
Linus Nilsson6233fed2020-08-13 15:15:14 -0700172 // Enable sequential access mode on the sample reader to achieve optimal read performance.
173 // This has to wait until all tracks have delivered their output formats and the sample
174 // writer is started. Otherwise the tracks will not get their output sample queues drained
175 // and the transcoder could hang due to one track running out of buffers and blocking the
176 // other tracks from reading source samples before they could output their formats.
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700177
178 std::scoped_lock lock{mThreadStateMutex};
179 // Don't start the sample writer if a stop already has been requested.
180 if (!mSampleWriterStopped) {
181 if (!mCancelled) {
182 mSampleReader->setEnforceSequentialAccess(true);
183 }
184 LOG(DEBUG) << "Starting sample writer.";
185 errorStarting = !mSampleWriter->start();
186 if (!errorStarting) {
187 mThreadStates[sampleWriterPtr] = RUNNING;
188 }
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700189 }
190 }
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700191
192 if (errorStarting) {
193 LOG(ERROR) << "Unable to start sample writer.";
194 onThreadFinished(sampleWriterPtr, AMEDIA_ERROR_UNKNOWN, false /* stopped */);
195 }
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700196}
197
Linus Nilssoncab39d82020-05-14 16:32:21 -0700198void MediaTranscoder::onTrackFinished(const MediaTrackTranscoder* transcoder) {
199 LOG(DEBUG) << "TrackTranscoder " << transcoder << " finished";
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700200 onThreadFinished(static_cast<const void*>(transcoder), AMEDIA_OK, false /* stopped */);
201}
202
203void MediaTranscoder::onTrackStopped(const MediaTrackTranscoder* transcoder) {
204 LOG(DEBUG) << "TrackTranscoder " << transcoder << " stopped";
205 onThreadFinished(static_cast<const void*>(transcoder), AMEDIA_OK, true /* stopped */);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700206}
207
208void MediaTranscoder::onTrackError(const MediaTrackTranscoder* transcoder, media_status_t status) {
Linus Nilsson93cf9132020-09-24 12:12:48 -0700209 LOG(ERROR) << "TrackTranscoder " << transcoder << " returned error " << status;
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700210 onThreadFinished(static_cast<const void*>(transcoder), status, false /* stopped */);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700211}
212
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700213void MediaTranscoder::onFinished(const MediaSampleWriter* writer, media_status_t status) {
214 LOG(status == AMEDIA_OK ? DEBUG : ERROR) << "Sample writer finished with status " << status;
215 onThreadFinished(static_cast<const void*>(writer), status, false /* stopped */);
216}
217
218void MediaTranscoder::onStopped(const MediaSampleWriter* writer) {
219 LOG(DEBUG) << "Sample writer " << writer << " stopped";
220 onThreadFinished(static_cast<const void*>(writer), AMEDIA_OK, true /* stopped */);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700221}
222
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700223void MediaTranscoder::onProgressUpdate(const MediaSampleWriter* writer __unused, int32_t progress) {
224 // Dispatch progress updated to the client.
225 mCallbacks->onProgressUpdate(this, progress);
226}
227
Chong Zhang457c6892021-02-01 15:34:20 -0800228void MediaTranscoder::onHeartBeat(const MediaSampleWriter* writer __unused) {
229 // Signal heart-beat to the client.
230 mCallbacks->onHeartBeat(this);
231}
232
233MediaTranscoder::MediaTranscoder(const std::shared_ptr<CallbackInterface>& callbacks,
234 int64_t heartBeatIntervalUs, pid_t pid, uid_t uid)
235 : mCallbacks(callbacks), mHeartBeatIntervalUs(heartBeatIntervalUs), mPid(pid), mUid(uid) {}
Chong Zhang308e91f2020-06-10 15:27:56 -0700236
Linus Nilssoncab39d82020-05-14 16:32:21 -0700237std::shared_ptr<MediaTranscoder> MediaTranscoder::create(
Chong Zhang457c6892021-02-01 15:34:20 -0800238 const std::shared_ptr<CallbackInterface>& callbacks, int64_t heartBeatIntervalUs, pid_t pid,
239 uid_t uid, const std::shared_ptr<ndk::ScopedAParcel>& pausedState) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700240 if (pausedState != nullptr) {
Chong Zhangb55c5452020-06-26 14:32:12 -0700241 LOG(INFO) << "Initializing from paused state.";
242 }
243 if (callbacks == nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700244 LOG(ERROR) << "Callbacks cannot be null";
245 return nullptr;
246 }
247
Chong Zhang457c6892021-02-01 15:34:20 -0800248 return std::shared_ptr<MediaTranscoder>(
249 new MediaTranscoder(callbacks, heartBeatIntervalUs, pid, uid));
Linus Nilssoncab39d82020-05-14 16:32:21 -0700250}
251
Chong Zhang308e91f2020-06-10 15:27:56 -0700252media_status_t MediaTranscoder::configureSource(int fd) {
253 if (fd < 0) {
254 LOG(ERROR) << "Invalid source fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700255 return AMEDIA_ERROR_INVALID_PARAMETER;
256 }
257
258 const size_t fileSize = lseek(fd, 0, SEEK_END);
259 lseek(fd, 0, SEEK_SET);
260
261 mSampleReader = MediaSampleReaderNDK::createFromFd(fd, 0 /* offset */, fileSize);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700262 if (mSampleReader == nullptr) {
Chong Zhang308e91f2020-06-10 15:27:56 -0700263 LOG(ERROR) << "Unable to parse source fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700264 return AMEDIA_ERROR_UNSUPPORTED;
265 }
266
267 const size_t trackCount = mSampleReader->getTrackCount();
268 for (size_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
269 AMediaFormat* trackFormat = mSampleReader->getTrackFormat(static_cast<int>(trackIndex));
270 if (trackFormat == nullptr) {
271 LOG(ERROR) << "Track #" << trackIndex << " has no format";
272 return AMEDIA_ERROR_MALFORMED;
273 }
274
275 mSourceTrackFormats.emplace_back(trackFormat, &AMediaFormat_delete);
276 }
277
278 return AMEDIA_OK;
279}
280
281std::vector<std::shared_ptr<AMediaFormat>> MediaTranscoder::getTrackFormats() const {
282 // Return a deep copy of the formats to avoid the caller modifying our internal formats.
283 std::vector<std::shared_ptr<AMediaFormat>> trackFormats;
284 for (const std::shared_ptr<AMediaFormat>& sourceFormat : mSourceTrackFormats) {
285 AMediaFormat* copy = AMediaFormat_new();
286 AMediaFormat_copy(copy, sourceFormat.get());
287 trackFormats.emplace_back(copy, &AMediaFormat_delete);
288 }
289 return trackFormats;
290}
291
Linus Nilssona676a9a2021-03-15 18:22:43 -0700292media_status_t MediaTranscoder::configureTrackFormat(size_t trackIndex,
293 AMediaFormat* destinationOptions) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700294 if (mSampleReader == nullptr) {
295 LOG(ERROR) << "Source must be configured before tracks";
296 return AMEDIA_ERROR_INVALID_OPERATION;
297 } else if (trackIndex >= mSourceTrackFormats.size()) {
298 LOG(ERROR) << "Track index " << trackIndex
299 << " is out of bounds. Track count: " << mSourceTrackFormats.size();
300 return AMEDIA_ERROR_INVALID_PARAMETER;
301 }
302
Linus Nilssone4716f22020-07-10 16:07:57 -0700303 std::shared_ptr<MediaTrackTranscoder> transcoder;
Linus Nilssona676a9a2021-03-15 18:22:43 -0700304 std::shared_ptr<AMediaFormat> trackFormat;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700305
Linus Nilssona676a9a2021-03-15 18:22:43 -0700306 if (destinationOptions == nullptr) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700307 transcoder = std::make_shared<PassthroughTrackTranscoder>(shared_from_this());
Linus Nilssoncab39d82020-05-14 16:32:21 -0700308 } else {
Linus Nilssona676a9a2021-03-15 18:22:43 -0700309 AMediaFormat* srcTrackFormat = mSourceTrackFormats[trackIndex].get();
310
Linus Nilssoncab39d82020-05-14 16:32:21 -0700311 const char* srcMime = nullptr;
Linus Nilssona676a9a2021-03-15 18:22:43 -0700312 if (!AMediaFormat_getString(srcTrackFormat, AMEDIAFORMAT_KEY_MIME, &srcMime)) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700313 LOG(ERROR) << "Source track #" << trackIndex << " has no mime type";
314 return AMEDIA_ERROR_MALFORMED;
315 }
316
317 if (strncmp(srcMime, "video/", 6) != 0) {
318 LOG(ERROR) << "Only video tracks are supported for transcoding. Unable to configure "
319 "track #"
320 << trackIndex << " with mime " << srcMime;
321 return AMEDIA_ERROR_UNSUPPORTED;
322 }
323
324 const char* dstMime = nullptr;
Linus Nilssona676a9a2021-03-15 18:22:43 -0700325 if (AMediaFormat_getString(destinationOptions, AMEDIAFORMAT_KEY_MIME, &dstMime)) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700326 if (strncmp(dstMime, "video/", 6) != 0) {
327 LOG(ERROR) << "Unable to convert media types for track #" << trackIndex << ", from "
328 << srcMime << " to " << dstMime;
329 return AMEDIA_ERROR_UNSUPPORTED;
330 }
331 }
332
Chong Zhangbbb4eac2020-11-18 11:12:06 -0800333 transcoder = VideoTrackTranscoder::create(shared_from_this(), mPid, mUid);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700334
Linus Nilssona676a9a2021-03-15 18:22:43 -0700335 trackFormat = createVideoTrackFormat(srcTrackFormat, destinationOptions);
336 if (trackFormat == nullptr) {
337 LOG(ERROR) << "Unable to create video track format";
Linus Nilssoncab39d82020-05-14 16:32:21 -0700338 return AMEDIA_ERROR_UNKNOWN;
339 }
Linus Nilssoncab39d82020-05-14 16:32:21 -0700340 }
341
Linus Nilssonaf4a3212020-12-15 08:18:25 -0800342 media_status_t status = mSampleReader->selectTrack(trackIndex);
343 if (status != AMEDIA_OK) {
344 LOG(ERROR) << "Unable to select track " << trackIndex;
345 return status;
346 }
347
Linus Nilssona676a9a2021-03-15 18:22:43 -0700348 status = transcoder->configure(mSampleReader, trackIndex, trackFormat);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700349 if (status != AMEDIA_OK) {
350 LOG(ERROR) << "Configure track transcoder for track #" << trackIndex << " returned error "
351 << status;
Linus Nilssonaf4a3212020-12-15 08:18:25 -0800352 mSampleReader->unselectTrack(trackIndex);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700353 return status;
354 }
355
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700356 std::scoped_lock lock{mThreadStateMutex};
357 mThreadStates[static_cast<const void*>(transcoder.get())] = PENDING;
358
Linus Nilssoncab39d82020-05-14 16:32:21 -0700359 mTrackTranscoders.emplace_back(std::move(transcoder));
360 return AMEDIA_OK;
361}
362
Chong Zhang308e91f2020-06-10 15:27:56 -0700363media_status_t MediaTranscoder::configureDestination(int fd) {
364 if (fd < 0) {
365 LOG(ERROR) << "Invalid destination fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700366 return AMEDIA_ERROR_INVALID_PARAMETER;
Chong Zhang308e91f2020-06-10 15:27:56 -0700367 }
368
369 if (mSampleWriter != nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700370 LOG(ERROR) << "Destination is already configured.";
371 return AMEDIA_ERROR_INVALID_OPERATION;
372 }
373
Linus Nilssonc31d2492020-09-23 12:30:00 -0700374 mSampleWriter = MediaSampleWriter::Create();
Chong Zhang457c6892021-02-01 15:34:20 -0800375 const bool initOk = mSampleWriter->init(fd, shared_from_this(), mHeartBeatIntervalUs);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700376
377 if (!initOk) {
Chong Zhang308e91f2020-06-10 15:27:56 -0700378 LOG(ERROR) << "Unable to initialize sample writer with destination fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700379 mSampleWriter.reset();
380 return AMEDIA_ERROR_UNKNOWN;
381 }
382
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700383 std::scoped_lock lock{mThreadStateMutex};
384 mThreadStates[static_cast<const void*>(mSampleWriter.get())] = PENDING;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700385 return AMEDIA_OK;
386}
387
388media_status_t MediaTranscoder::start() {
389 if (mTrackTranscoders.size() < 1) {
390 LOG(ERROR) << "Unable to start, no tracks are configured.";
391 return AMEDIA_ERROR_INVALID_OPERATION;
392 } else if (mSampleWriter == nullptr) {
393 LOG(ERROR) << "Unable to start, destination is not configured";
394 return AMEDIA_ERROR_INVALID_OPERATION;
395 }
396
Linus Nilssoncab39d82020-05-14 16:32:21 -0700397 // Start transcoders
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700398 bool started = true;
399 {
400 std::scoped_lock lock{mThreadStateMutex};
401 for (auto& transcoder : mTrackTranscoders) {
402 if (!(started = transcoder->start())) {
403 break;
404 }
405 mThreadStates[static_cast<const void*>(transcoder.get())] = RUNNING;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700406 }
407 }
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700408 if (!started) {
409 LOG(ERROR) << "Unable to start track transcoder.";
410 cancel();
411 return AMEDIA_ERROR_UNKNOWN;
412 }
Linus Nilssoncab39d82020-05-14 16:32:21 -0700413 return AMEDIA_OK;
414}
415
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700416media_status_t MediaTranscoder::requestStop(bool stopOnSync) {
417 std::scoped_lock lock{mThreadStateMutex};
418 if (mCancelled) {
419 LOG(DEBUG) << "MediaTranscoder already cancelled";
420 return AMEDIA_ERROR_UNSUPPORTED;
421 }
422
423 if (!stopOnSync) {
424 mSampleWriterStopped = true;
425 mSampleWriter->stop();
426 }
427
428 mSampleReader->setEnforceSequentialAccess(false);
429 for (auto& transcoder : mTrackTranscoders) {
430 transcoder->stop(stopOnSync);
431 }
432
433 mCancelled = true;
434 return AMEDIA_OK;
435}
436
437void MediaTranscoder::waitForThreads() NO_THREAD_SAFETY_ANALYSIS {
438 std::unique_lock lock{mThreadStateMutex};
439 while (!mThreadsDone) {
440 mThreadsDoneSignal.wait(lock);
441 }
442}
443
Chong Zhange4e088f2020-10-21 19:10:42 -0700444media_status_t MediaTranscoder::pause(std::shared_ptr<ndk::ScopedAParcel>* pausedState) {
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700445 media_status_t status = requestStop(true /* stopOnSync */);
446 if (status != AMEDIA_OK) {
447 return status;
448 }
449
450 waitForThreads();
451
Chong Zhangb55c5452020-06-26 14:32:12 -0700452 // TODO: write internal states to parcel.
Chong Zhange4e088f2020-10-21 19:10:42 -0700453 *pausedState = std::shared_ptr<::ndk::ScopedAParcel>(new ::ndk::ScopedAParcel());
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700454 return AMEDIA_OK;
455}
456
457media_status_t MediaTranscoder::cancel() {
458 media_status_t status = requestStop(false /* stopOnSync */);
459 if (status != AMEDIA_OK) {
460 return status;
461 }
462
463 waitForThreads();
464
465 // TODO: Release transcoders?
466 return AMEDIA_OK;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700467}
468
469media_status_t MediaTranscoder::resume() {
Chong Zhangb55c5452020-06-26 14:32:12 -0700470 // TODO: restore internal states from parcel.
471 return start();
Linus Nilssoncab39d82020-05-14 16:32:21 -0700472}
473
Linus Nilssoncab39d82020-05-14 16:32:21 -0700474} // namespace android