blob: 74ddce42acab403b781f467d64bebfd216246742 [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
Linus Nilssonfdb3e332020-09-18 17:11:41 -070072void MediaTranscoder::onThreadFinished(const void* thread, media_status_t threadStatus,
73 bool threadStopped) {
74 LOG(DEBUG) << "Thread " << thread << " finished with status " << threadStatus << " stopped "
75 << threadStopped;
76
77 // Stop all threads if one reports an error.
78 if (threadStatus != AMEDIA_OK) {
79 requestStop(false /* stopOnSync */);
80 }
81
82 std::scoped_lock lock{mThreadStateMutex};
83
84 // Record the change.
85 mThreadStates[thread] = DONE;
86 if (threadStatus != AMEDIA_OK && mTranscoderStatus == AMEDIA_OK) {
87 mTranscoderStatus = threadStatus;
88 }
89
90 mTranscoderStopped |= threadStopped;
91
92 // Check if all threads are done. Note that if all transcoders have stopped but the sample
93 // writer has not yet started, it never will.
94 bool transcodersDone = true;
95 ThreadState sampleWriterState = PENDING;
96 for (const auto& it : mThreadStates) {
97 LOG(DEBUG) << " Thread " << it.first << " state" << it.second;
98 if (it.first == static_cast<const void*>(mSampleWriter.get())) {
99 sampleWriterState = it.second;
100 } else {
101 transcodersDone &= (it.second == DONE);
102 }
103 }
104 if (!transcodersDone || sampleWriterState == RUNNING) {
Chong Zhangb55c5452020-06-26 14:32:12 -0700105 return;
106 }
107
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700108 // All done. Send callback asynchronously and wake up threads waiting in cancel/pause.
109 mThreadsDone = true;
110 if (!mCallbackSent) {
111 std::thread asyncNotificationThread{[this, self = shared_from_this(),
112 status = mTranscoderStatus,
113 stopped = mTranscoderStopped] {
114 // If the transcoder was stopped that means a caller is waiting in stop or pause
115 // in which case we don't send a callback.
116 if (status != AMEDIA_OK) {
117 mCallbacks->onError(this, status);
118 } else if (!stopped) {
119 mCallbacks->onFinished(this);
120 }
121 mThreadsDoneSignal.notify_all();
122 }};
123 asyncNotificationThread.detach();
124 mCallbackSent = true;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700125 }
126}
127
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700128void MediaTranscoder::onTrackFormatAvailable(const MediaTrackTranscoder* transcoder) {
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700129 LOG(DEBUG) << "TrackTranscoder " << transcoder << " format available.";
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700130
131 std::scoped_lock lock{mTracksAddedMutex};
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700132 const void* sampleWriterPtr = static_cast<const void*>(mSampleWriter.get());
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700133
134 // Ignore duplicate format change.
135 if (mTracksAdded.count(transcoder) > 0) {
136 return;
137 }
138
139 // Add track to the writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700140 auto consumer = mSampleWriter->addTrack(transcoder->getOutputFormat());
141 if (consumer == nullptr) {
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700142 LOG(ERROR) << "Unable to add track to sample writer.";
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700143 onThreadFinished(sampleWriterPtr, AMEDIA_ERROR_UNKNOWN, false /* stopped */);
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700144 return;
145 }
146
Linus Nilssonc31d2492020-09-23 12:30:00 -0700147 MediaTrackTranscoder* mutableTranscoder = const_cast<MediaTrackTranscoder*>(transcoder);
148 mutableTranscoder->setSampleConsumer(consumer);
149
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700150 mTracksAdded.insert(transcoder);
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700151 bool errorStarting = false;
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700152 if (mTracksAdded.size() == mTrackTranscoders.size()) {
Linus Nilsson6233fed2020-08-13 15:15:14 -0700153 // Enable sequential access mode on the sample reader to achieve optimal read performance.
154 // This has to wait until all tracks have delivered their output formats and the sample
155 // writer is started. Otherwise the tracks will not get their output sample queues drained
156 // and the transcoder could hang due to one track running out of buffers and blocking the
157 // other tracks from reading source samples before they could output their formats.
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700158
159 std::scoped_lock lock{mThreadStateMutex};
160 // Don't start the sample writer if a stop already has been requested.
161 if (!mSampleWriterStopped) {
162 if (!mCancelled) {
163 mSampleReader->setEnforceSequentialAccess(true);
164 }
165 LOG(DEBUG) << "Starting sample writer.";
166 errorStarting = !mSampleWriter->start();
167 if (!errorStarting) {
168 mThreadStates[sampleWriterPtr] = RUNNING;
169 }
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700170 }
171 }
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700172
173 if (errorStarting) {
174 LOG(ERROR) << "Unable to start sample writer.";
175 onThreadFinished(sampleWriterPtr, AMEDIA_ERROR_UNKNOWN, false /* stopped */);
176 }
Chong Zhanga2cc86b2020-06-17 16:56:49 -0700177}
178
Linus Nilssoncab39d82020-05-14 16:32:21 -0700179void MediaTranscoder::onTrackFinished(const MediaTrackTranscoder* transcoder) {
180 LOG(DEBUG) << "TrackTranscoder " << transcoder << " finished";
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700181 onThreadFinished(static_cast<const void*>(transcoder), AMEDIA_OK, false /* stopped */);
182}
183
184void MediaTranscoder::onTrackStopped(const MediaTrackTranscoder* transcoder) {
185 LOG(DEBUG) << "TrackTranscoder " << transcoder << " stopped";
186 onThreadFinished(static_cast<const void*>(transcoder), AMEDIA_OK, true /* stopped */);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700187}
188
189void MediaTranscoder::onTrackError(const MediaTrackTranscoder* transcoder, media_status_t status) {
Linus Nilsson93cf9132020-09-24 12:12:48 -0700190 LOG(ERROR) << "TrackTranscoder " << transcoder << " returned error " << status;
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700191 onThreadFinished(static_cast<const void*>(transcoder), status, false /* stopped */);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700192}
193
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700194void MediaTranscoder::onFinished(const MediaSampleWriter* writer, media_status_t status) {
195 LOG(status == AMEDIA_OK ? DEBUG : ERROR) << "Sample writer finished with status " << status;
196 onThreadFinished(static_cast<const void*>(writer), status, false /* stopped */);
197}
198
199void MediaTranscoder::onStopped(const MediaSampleWriter* writer) {
200 LOG(DEBUG) << "Sample writer " << writer << " stopped";
201 onThreadFinished(static_cast<const void*>(writer), AMEDIA_OK, true /* stopped */);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700202}
203
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700204void MediaTranscoder::onProgressUpdate(const MediaSampleWriter* writer __unused, int32_t progress) {
205 // Dispatch progress updated to the client.
206 mCallbacks->onProgressUpdate(this, progress);
207}
208
Chong Zhang457c6892021-02-01 15:34:20 -0800209void MediaTranscoder::onHeartBeat(const MediaSampleWriter* writer __unused) {
210 // Signal heart-beat to the client.
211 mCallbacks->onHeartBeat(this);
212}
213
214MediaTranscoder::MediaTranscoder(const std::shared_ptr<CallbackInterface>& callbacks,
215 int64_t heartBeatIntervalUs, pid_t pid, uid_t uid)
216 : mCallbacks(callbacks), mHeartBeatIntervalUs(heartBeatIntervalUs), mPid(pid), mUid(uid) {}
Chong Zhang308e91f2020-06-10 15:27:56 -0700217
Linus Nilssoncab39d82020-05-14 16:32:21 -0700218std::shared_ptr<MediaTranscoder> MediaTranscoder::create(
Chong Zhang457c6892021-02-01 15:34:20 -0800219 const std::shared_ptr<CallbackInterface>& callbacks, int64_t heartBeatIntervalUs, pid_t pid,
220 uid_t uid, const std::shared_ptr<ndk::ScopedAParcel>& pausedState) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700221 if (pausedState != nullptr) {
Chong Zhangb55c5452020-06-26 14:32:12 -0700222 LOG(INFO) << "Initializing from paused state.";
223 }
224 if (callbacks == nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700225 LOG(ERROR) << "Callbacks cannot be null";
226 return nullptr;
227 }
228
Chong Zhang457c6892021-02-01 15:34:20 -0800229 return std::shared_ptr<MediaTranscoder>(
230 new MediaTranscoder(callbacks, heartBeatIntervalUs, pid, uid));
Linus Nilssoncab39d82020-05-14 16:32:21 -0700231}
232
Chong Zhang308e91f2020-06-10 15:27:56 -0700233media_status_t MediaTranscoder::configureSource(int fd) {
234 if (fd < 0) {
235 LOG(ERROR) << "Invalid source fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700236 return AMEDIA_ERROR_INVALID_PARAMETER;
237 }
238
239 const size_t fileSize = lseek(fd, 0, SEEK_END);
240 lseek(fd, 0, SEEK_SET);
241
242 mSampleReader = MediaSampleReaderNDK::createFromFd(fd, 0 /* offset */, fileSize);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700243 if (mSampleReader == nullptr) {
Chong Zhang308e91f2020-06-10 15:27:56 -0700244 LOG(ERROR) << "Unable to parse source fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700245 return AMEDIA_ERROR_UNSUPPORTED;
246 }
247
248 const size_t trackCount = mSampleReader->getTrackCount();
249 for (size_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
250 AMediaFormat* trackFormat = mSampleReader->getTrackFormat(static_cast<int>(trackIndex));
251 if (trackFormat == nullptr) {
252 LOG(ERROR) << "Track #" << trackIndex << " has no format";
253 return AMEDIA_ERROR_MALFORMED;
254 }
255
256 mSourceTrackFormats.emplace_back(trackFormat, &AMediaFormat_delete);
257 }
258
259 return AMEDIA_OK;
260}
261
262std::vector<std::shared_ptr<AMediaFormat>> MediaTranscoder::getTrackFormats() const {
263 // Return a deep copy of the formats to avoid the caller modifying our internal formats.
264 std::vector<std::shared_ptr<AMediaFormat>> trackFormats;
265 for (const std::shared_ptr<AMediaFormat>& sourceFormat : mSourceTrackFormats) {
266 AMediaFormat* copy = AMediaFormat_new();
267 AMediaFormat_copy(copy, sourceFormat.get());
268 trackFormats.emplace_back(copy, &AMediaFormat_delete);
269 }
270 return trackFormats;
271}
272
273media_status_t MediaTranscoder::configureTrackFormat(size_t trackIndex, AMediaFormat* trackFormat) {
274 if (mSampleReader == nullptr) {
275 LOG(ERROR) << "Source must be configured before tracks";
276 return AMEDIA_ERROR_INVALID_OPERATION;
277 } else if (trackIndex >= mSourceTrackFormats.size()) {
278 LOG(ERROR) << "Track index " << trackIndex
279 << " is out of bounds. Track count: " << mSourceTrackFormats.size();
280 return AMEDIA_ERROR_INVALID_PARAMETER;
281 }
282
Linus Nilssone4716f22020-07-10 16:07:57 -0700283 std::shared_ptr<MediaTrackTranscoder> transcoder;
284 std::shared_ptr<AMediaFormat> format;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700285
286 if (trackFormat == nullptr) {
Linus Nilssone4716f22020-07-10 16:07:57 -0700287 transcoder = std::make_shared<PassthroughTrackTranscoder>(shared_from_this());
Linus Nilssoncab39d82020-05-14 16:32:21 -0700288 } else {
289 const char* srcMime = nullptr;
290 if (!AMediaFormat_getString(mSourceTrackFormats[trackIndex].get(), AMEDIAFORMAT_KEY_MIME,
291 &srcMime)) {
292 LOG(ERROR) << "Source track #" << trackIndex << " has no mime type";
293 return AMEDIA_ERROR_MALFORMED;
294 }
295
296 if (strncmp(srcMime, "video/", 6) != 0) {
297 LOG(ERROR) << "Only video tracks are supported for transcoding. Unable to configure "
298 "track #"
299 << trackIndex << " with mime " << srcMime;
300 return AMEDIA_ERROR_UNSUPPORTED;
301 }
302
303 const char* dstMime = nullptr;
304 if (AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &dstMime)) {
305 if (strncmp(dstMime, "video/", 6) != 0) {
306 LOG(ERROR) << "Unable to convert media types for track #" << trackIndex << ", from "
307 << srcMime << " to " << dstMime;
308 return AMEDIA_ERROR_UNSUPPORTED;
309 }
310 }
311
Chong Zhangbbb4eac2020-11-18 11:12:06 -0800312 transcoder = VideoTrackTranscoder::create(shared_from_this(), mPid, mUid);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700313
314 AMediaFormat* mergedFormat =
315 mergeMediaFormats(mSourceTrackFormats[trackIndex].get(), trackFormat);
316 if (mergedFormat == nullptr) {
317 LOG(ERROR) << "Unable to merge source and destination formats";
318 return AMEDIA_ERROR_UNKNOWN;
319 }
320
321 format = std::shared_ptr<AMediaFormat>(mergedFormat, &AMediaFormat_delete);
322 }
323
Linus Nilssonaf4a3212020-12-15 08:18:25 -0800324 media_status_t status = mSampleReader->selectTrack(trackIndex);
325 if (status != AMEDIA_OK) {
326 LOG(ERROR) << "Unable to select track " << trackIndex;
327 return status;
328 }
329
Chong Zhangc8c88cc2020-09-17 12:50:49 -0700330 status = transcoder->configure(mSampleReader, trackIndex, format);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700331 if (status != AMEDIA_OK) {
332 LOG(ERROR) << "Configure track transcoder for track #" << trackIndex << " returned error "
333 << status;
Linus Nilssonaf4a3212020-12-15 08:18:25 -0800334 mSampleReader->unselectTrack(trackIndex);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700335 return status;
336 }
337
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700338 std::scoped_lock lock{mThreadStateMutex};
339 mThreadStates[static_cast<const void*>(transcoder.get())] = PENDING;
340
Linus Nilssoncab39d82020-05-14 16:32:21 -0700341 mTrackTranscoders.emplace_back(std::move(transcoder));
342 return AMEDIA_OK;
343}
344
Chong Zhang308e91f2020-06-10 15:27:56 -0700345media_status_t MediaTranscoder::configureDestination(int fd) {
346 if (fd < 0) {
347 LOG(ERROR) << "Invalid destination fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700348 return AMEDIA_ERROR_INVALID_PARAMETER;
Chong Zhang308e91f2020-06-10 15:27:56 -0700349 }
350
351 if (mSampleWriter != nullptr) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700352 LOG(ERROR) << "Destination is already configured.";
353 return AMEDIA_ERROR_INVALID_OPERATION;
354 }
355
Linus Nilssonc31d2492020-09-23 12:30:00 -0700356 mSampleWriter = MediaSampleWriter::Create();
Chong Zhang457c6892021-02-01 15:34:20 -0800357 const bool initOk = mSampleWriter->init(fd, shared_from_this(), mHeartBeatIntervalUs);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700358
359 if (!initOk) {
Chong Zhang308e91f2020-06-10 15:27:56 -0700360 LOG(ERROR) << "Unable to initialize sample writer with destination fd: " << fd;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700361 mSampleWriter.reset();
362 return AMEDIA_ERROR_UNKNOWN;
363 }
364
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700365 std::scoped_lock lock{mThreadStateMutex};
366 mThreadStates[static_cast<const void*>(mSampleWriter.get())] = PENDING;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700367 return AMEDIA_OK;
368}
369
370media_status_t MediaTranscoder::start() {
371 if (mTrackTranscoders.size() < 1) {
372 LOG(ERROR) << "Unable to start, no tracks are configured.";
373 return AMEDIA_ERROR_INVALID_OPERATION;
374 } else if (mSampleWriter == nullptr) {
375 LOG(ERROR) << "Unable to start, destination is not configured";
376 return AMEDIA_ERROR_INVALID_OPERATION;
377 }
378
Linus Nilssoncab39d82020-05-14 16:32:21 -0700379 // Start transcoders
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700380 bool started = true;
381 {
382 std::scoped_lock lock{mThreadStateMutex};
383 for (auto& transcoder : mTrackTranscoders) {
384 if (!(started = transcoder->start())) {
385 break;
386 }
387 mThreadStates[static_cast<const void*>(transcoder.get())] = RUNNING;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700388 }
389 }
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700390 if (!started) {
391 LOG(ERROR) << "Unable to start track transcoder.";
392 cancel();
393 return AMEDIA_ERROR_UNKNOWN;
394 }
Linus Nilssoncab39d82020-05-14 16:32:21 -0700395 return AMEDIA_OK;
396}
397
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700398media_status_t MediaTranscoder::requestStop(bool stopOnSync) {
399 std::scoped_lock lock{mThreadStateMutex};
400 if (mCancelled) {
401 LOG(DEBUG) << "MediaTranscoder already cancelled";
402 return AMEDIA_ERROR_UNSUPPORTED;
403 }
404
405 if (!stopOnSync) {
406 mSampleWriterStopped = true;
407 mSampleWriter->stop();
408 }
409
410 mSampleReader->setEnforceSequentialAccess(false);
411 for (auto& transcoder : mTrackTranscoders) {
412 transcoder->stop(stopOnSync);
413 }
414
415 mCancelled = true;
416 return AMEDIA_OK;
417}
418
419void MediaTranscoder::waitForThreads() NO_THREAD_SAFETY_ANALYSIS {
420 std::unique_lock lock{mThreadStateMutex};
421 while (!mThreadsDone) {
422 mThreadsDoneSignal.wait(lock);
423 }
424}
425
Chong Zhange4e088f2020-10-21 19:10:42 -0700426media_status_t MediaTranscoder::pause(std::shared_ptr<ndk::ScopedAParcel>* pausedState) {
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700427 media_status_t status = requestStop(true /* stopOnSync */);
428 if (status != AMEDIA_OK) {
429 return status;
430 }
431
432 waitForThreads();
433
Chong Zhangb55c5452020-06-26 14:32:12 -0700434 // TODO: write internal states to parcel.
Chong Zhange4e088f2020-10-21 19:10:42 -0700435 *pausedState = std::shared_ptr<::ndk::ScopedAParcel>(new ::ndk::ScopedAParcel());
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700436 return AMEDIA_OK;
437}
438
439media_status_t MediaTranscoder::cancel() {
440 media_status_t status = requestStop(false /* stopOnSync */);
441 if (status != AMEDIA_OK) {
442 return status;
443 }
444
445 waitForThreads();
446
447 // TODO: Release transcoders?
448 return AMEDIA_OK;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700449}
450
451media_status_t MediaTranscoder::resume() {
Chong Zhangb55c5452020-06-26 14:32:12 -0700452 // TODO: restore internal states from parcel.
453 return start();
Linus Nilssoncab39d82020-05-14 16:32:21 -0700454}
455
Linus Nilssoncab39d82020-05-14 16:32:21 -0700456} // namespace android