1 /*
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 "TranscoderWrapper"
19
20 #include <aidl/android/media/TranscodingErrorCode.h>
21 #include <aidl/android/media/TranscodingRequestParcel.h>
22 #include <media/MediaTranscoder.h>
23 #include <media/NdkCommon.h>
24 #include <media/TranscoderWrapper.h>
25 #include <media/TranscodingRequest.h>
26 #include <utils/AndroidThreads.h>
27 #include <utils/Log.h>
28
29 #include <thread>
30
31 namespace android {
32 using Status = ::ndk::ScopedAStatus;
33 using ::aidl::android::media::TranscodingErrorCode;
34 using ::aidl::android::media::TranscodingVideoCodecType;
35 using ::aidl::android::media::TranscodingVideoTrackFormat;
36
toTranscodingError(media_status_t status)37 static TranscodingErrorCode toTranscodingError(media_status_t status) {
38 switch (status) {
39 case AMEDIA_OK:
40 return TranscodingErrorCode::kNoError;
41 case AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE: // FALLTHRU
42 case AMEDIACODEC_ERROR_RECLAIMED:
43 return TranscodingErrorCode::kInsufficientResources;
44 case AMEDIA_ERROR_MALFORMED:
45 return TranscodingErrorCode::kMalformed;
46 case AMEDIA_ERROR_UNSUPPORTED:
47 return TranscodingErrorCode::kUnsupported;
48 case AMEDIA_ERROR_INVALID_OBJECT: // FALLTHRU
49 case AMEDIA_ERROR_INVALID_PARAMETER:
50 return TranscodingErrorCode::kInvalidParameter;
51 case AMEDIA_ERROR_INVALID_OPERATION:
52 return TranscodingErrorCode::kInvalidOperation;
53 case AMEDIA_ERROR_IO:
54 return TranscodingErrorCode::kErrorIO;
55 case AMEDIA_ERROR_UNKNOWN: // FALLTHRU
56 default:
57 return TranscodingErrorCode::kUnknown;
58 }
59 }
60
getVideoFormat(const char * originalMime,const std::optional<TranscodingVideoTrackFormat> & requestedFormat)61 static std::shared_ptr<AMediaFormat> getVideoFormat(
62 const char* originalMime,
63 const std::optional<TranscodingVideoTrackFormat>& requestedFormat) {
64 if (requestedFormat == std::nullopt) {
65 return nullptr;
66 }
67
68 std::shared_ptr<AMediaFormat> format =
69 std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
70 bool changed = false;
71 if (requestedFormat->codecType == TranscodingVideoCodecType::kHevc &&
72 strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_HEVC)) {
73 AMediaFormat_setString(format.get(), AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_HEVC);
74 changed = true;
75 } else if (requestedFormat->codecType == TranscodingVideoCodecType::kAvc &&
76 strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_AVC)) {
77 AMediaFormat_setString(format.get(), AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
78 changed = true;
79 }
80 if (requestedFormat->bitrateBps > 0) {
81 AMediaFormat_setInt32(format.get(), AMEDIAFORMAT_KEY_BIT_RATE, requestedFormat->bitrateBps);
82 changed = true;
83 }
84 // TODO: translate other fields from requestedFormat to the format for MediaTranscoder.
85 // Also need to determine more settings to expose in TranscodingVideoTrackFormat.
86 if (!changed) {
87 // Use null format for passthru.
88 format.reset();
89 }
90 return format;
91 }
92
93 //static
toString(const Event & event)94 std::string TranscoderWrapper::toString(const Event& event) {
95 std::string typeStr;
96 switch (event.type) {
97 case Event::Start:
98 typeStr = "Start";
99 break;
100 case Event::Pause:
101 typeStr = "Pause";
102 break;
103 case Event::Resume:
104 typeStr = "Resume";
105 break;
106 case Event::Stop:
107 typeStr = "Stop";
108 break;
109 case Event::Finish:
110 typeStr = "Finish";
111 break;
112 case Event::Error:
113 typeStr = "Error";
114 break;
115 case Event::Progress:
116 typeStr = "Progress";
117 break;
118 case Event::HeartBeat:
119 typeStr = "HeartBeat";
120 break;
121 case Event::Abandon:
122 typeStr = "Abandon";
123 break;
124 default:
125 return "(unknown)";
126 }
127 std::string result;
128 result = "session {" + std::to_string(event.clientId) + "," + std::to_string(event.sessionId) +
129 "}: " + typeStr;
130 if (event.type == Event::Error || event.type == Event::Progress) {
131 result += " " + std::to_string(event.arg);
132 }
133 return result;
134 }
135
136 class TranscoderWrapper::CallbackImpl : public MediaTranscoder::CallbackInterface {
137 public:
CallbackImpl(const std::shared_ptr<TranscoderWrapper> & owner,ClientIdType clientId,SessionIdType sessionId)138 CallbackImpl(const std::shared_ptr<TranscoderWrapper>& owner, ClientIdType clientId,
139 SessionIdType sessionId)
140 : mOwner(owner), mClientId(clientId), mSessionId(sessionId) {}
141
onFinished(const MediaTranscoder * transcoder __unused)142 virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
143 auto owner = mOwner.lock();
144 if (owner != nullptr) {
145 owner->onFinish(mClientId, mSessionId);
146 }
147 }
148
onError(const MediaTranscoder * transcoder __unused,media_status_t error)149 virtual void onError(const MediaTranscoder* transcoder __unused,
150 media_status_t error) override {
151 auto owner = mOwner.lock();
152 if (owner != nullptr) {
153 owner->onError(mClientId, mSessionId, error);
154 }
155 }
156
onProgressUpdate(const MediaTranscoder * transcoder __unused,int32_t progress)157 virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
158 int32_t progress) override {
159 auto owner = mOwner.lock();
160 if (owner != nullptr) {
161 owner->onProgress(mClientId, mSessionId, progress);
162 }
163 }
164
onHeartBeat(const MediaTranscoder * transcoder __unused)165 virtual void onHeartBeat(const MediaTranscoder* transcoder __unused) override {
166 auto owner = mOwner.lock();
167 if (owner != nullptr) {
168 owner->onHeartBeat(mClientId, mSessionId);
169 }
170 }
171
onCodecResourceLost(const MediaTranscoder * transcoder __unused,const std::shared_ptr<ndk::ScopedAParcel> & pausedState __unused)172 virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
173 const std::shared_ptr<ndk::ScopedAParcel>& pausedState
174 __unused) override {
175 ALOGV("%s: session {%lld, %d}", __FUNCTION__, (long long)mClientId, mSessionId);
176 }
177
178 private:
179 std::weak_ptr<TranscoderWrapper> mOwner;
180 ClientIdType mClientId;
181 SessionIdType mSessionId;
182 };
183
TranscoderWrapper(const std::shared_ptr<TranscoderCallbackInterface> & cb,const std::shared_ptr<TranscodingLogger> & logger,int64_t heartBeatIntervalUs)184 TranscoderWrapper::TranscoderWrapper(const std::shared_ptr<TranscoderCallbackInterface>& cb,
185 const std::shared_ptr<TranscodingLogger>& logger,
186 int64_t heartBeatIntervalUs)
187 : mCallback(cb),
188 mLogger(logger),
189 mHeartBeatIntervalUs(heartBeatIntervalUs),
190 mCurrentClientId(0),
191 mCurrentSessionId(-1),
192 mLooperReady(false) {
193 ALOGV("TranscoderWrapper CTOR: %p", this);
194 }
195
~TranscoderWrapper()196 TranscoderWrapper::~TranscoderWrapper() {
197 ALOGV("TranscoderWrapper DTOR: %p", this);
198 }
199
isResourceError(media_status_t err)200 static bool isResourceError(media_status_t err) {
201 return err == AMEDIACODEC_ERROR_RECLAIMED || err == AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE;
202 }
203
reportError(ClientIdType clientId,SessionIdType sessionId,media_status_t err)204 void TranscoderWrapper::reportError(ClientIdType clientId, SessionIdType sessionId,
205 media_status_t err) {
206 auto callback = mCallback.lock();
207 if (callback != nullptr) {
208 if (isResourceError(err)) {
209 // Add a placeholder pause state to mPausedStateMap. This is required when resuming.
210 // TODO: remove this when transcoder pause/resume logic is ready. New logic will
211 // no longer use the pause states.
212 auto it = mPausedStateMap.find(SessionKeyType(clientId, sessionId));
213 if (it == mPausedStateMap.end()) {
214 mPausedStateMap.emplace(SessionKeyType(clientId, sessionId),
215 new ndk::ScopedAParcel());
216 }
217
218 callback->onResourceLost(clientId, sessionId);
219 } else {
220 callback->onError(clientId, sessionId, toTranscodingError(err));
221 }
222 }
223 }
224
start(ClientIdType clientId,SessionIdType sessionId,const TranscodingRequestParcel & requestParcel,uid_t callingUid,const std::shared_ptr<ITranscodingClientCallback> & clientCb)225 void TranscoderWrapper::start(ClientIdType clientId, SessionIdType sessionId,
226 const TranscodingRequestParcel& requestParcel, uid_t callingUid,
227 const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
228 TranscodingRequest request{requestParcel};
229 queueEvent(Event::Start, clientId, sessionId, [=] {
230 media_status_t err = handleStart(clientId, sessionId, request, callingUid, clientCb);
231 if (err != AMEDIA_OK) {
232 cleanup();
233 reportError(clientId, sessionId, err);
234 } else {
235 auto callback = mCallback.lock();
236 if (callback != nullptr) {
237 callback->onStarted(clientId, sessionId);
238 }
239 }
240 });
241 }
242
pause(ClientIdType clientId,SessionIdType sessionId)243 void TranscoderWrapper::pause(ClientIdType clientId, SessionIdType sessionId) {
244 queueEvent(Event::Pause, clientId, sessionId, [=] {
245 media_status_t err = handlePause(clientId, sessionId);
246
247 cleanup();
248
249 if (err != AMEDIA_OK) {
250 reportError(clientId, sessionId, err);
251 } else {
252 auto callback = mCallback.lock();
253 if (callback != nullptr) {
254 callback->onPaused(clientId, sessionId);
255 }
256 }
257 });
258 }
259
resume(ClientIdType clientId,SessionIdType sessionId,const TranscodingRequestParcel & requestParcel,uid_t callingUid,const std::shared_ptr<ITranscodingClientCallback> & clientCb)260 void TranscoderWrapper::resume(ClientIdType clientId, SessionIdType sessionId,
261 const TranscodingRequestParcel& requestParcel, uid_t callingUid,
262 const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
263 TranscodingRequest request{requestParcel};
264 queueEvent(Event::Resume, clientId, sessionId, [=] {
265 media_status_t err = handleResume(clientId, sessionId, request, callingUid, clientCb);
266 if (err != AMEDIA_OK) {
267 cleanup();
268 reportError(clientId, sessionId, err);
269 } else {
270 auto callback = mCallback.lock();
271 if (callback != nullptr) {
272 callback->onResumed(clientId, sessionId);
273 }
274 }
275 });
276 }
277
stop(ClientIdType clientId,SessionIdType sessionId,bool abandon)278 void TranscoderWrapper::stop(ClientIdType clientId, SessionIdType sessionId, bool abandon) {
279 queueEvent(Event::Stop, clientId, sessionId, [=] {
280 if (mTranscoder != nullptr && clientId == mCurrentClientId &&
281 sessionId == mCurrentSessionId) {
282 // Cancelling the currently running session.
283 media_status_t err = mTranscoder->cancel();
284 if (err != AMEDIA_OK) {
285 ALOGW("failed to stop transcoder: %d", err);
286 } else {
287 ALOGI("transcoder stopped");
288 }
289 logSessionEnded(TranscodingLogger::SessionEndedReason::CANCELLED, err);
290 cleanup();
291 } else {
292 // For sessions that's not currently running, release any pausedState for the session.
293 mPausedStateMap.erase(SessionKeyType(clientId, sessionId));
294 }
295 // No callback needed for stop.
296 });
297
298 if (abandon) {
299 queueEvent(Event::Abandon, 0, 0, nullptr);
300 }
301 }
302
onFinish(ClientIdType clientId,SessionIdType sessionId)303 void TranscoderWrapper::onFinish(ClientIdType clientId, SessionIdType sessionId) {
304 queueEvent(Event::Finish, clientId, sessionId, [=] {
305 if (mTranscoder != nullptr && clientId == mCurrentClientId &&
306 sessionId == mCurrentSessionId) {
307 logSessionEnded(TranscodingLogger::SessionEndedReason::FINISHED, AMEDIA_OK);
308 cleanup();
309 }
310
311 auto callback = mCallback.lock();
312 if (callback != nullptr) {
313 callback->onFinish(clientId, sessionId);
314 }
315 });
316 }
317
onError(ClientIdType clientId,SessionIdType sessionId,media_status_t error)318 void TranscoderWrapper::onError(ClientIdType clientId, SessionIdType sessionId,
319 media_status_t error) {
320 queueEvent(
321 Event::Error, clientId, sessionId,
322 [=] {
323 if (mTranscoder != nullptr && clientId == mCurrentClientId &&
324 sessionId == mCurrentSessionId) {
325 logSessionEnded(TranscodingLogger::SessionEndedReason::ERROR, error);
326 cleanup();
327 }
328 reportError(clientId, sessionId, error);
329 },
330 error);
331 }
332
onProgress(ClientIdType clientId,SessionIdType sessionId,int32_t progress)333 void TranscoderWrapper::onProgress(ClientIdType clientId, SessionIdType sessionId,
334 int32_t progress) {
335 queueEvent(
336 Event::Progress, clientId, sessionId,
337 [=] {
338 auto callback = mCallback.lock();
339 if (callback != nullptr) {
340 callback->onProgressUpdate(clientId, sessionId, progress);
341 }
342 },
343 progress);
344 }
345
onHeartBeat(ClientIdType clientId,SessionIdType sessionId)346 void TranscoderWrapper::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
347 queueEvent(Event::HeartBeat, clientId, sessionId, [=] {
348 auto callback = mCallback.lock();
349 if (callback != nullptr) {
350 callback->onHeartBeat(clientId, sessionId);
351 }
352 });
353 }
354
setupTranscoder(ClientIdType clientId,SessionIdType sessionId,const TranscodingRequestParcel & request,uid_t callingUid,const std::shared_ptr<ITranscodingClientCallback> & clientCb,TranscodingLogger::SessionEndedReason * failureReason,const std::shared_ptr<ndk::ScopedAParcel> & pausedState)355 media_status_t TranscoderWrapper::setupTranscoder(
356 ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
357 uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& clientCb,
358 TranscodingLogger::SessionEndedReason* failureReason,
359 const std::shared_ptr<ndk::ScopedAParcel>& pausedState) {
360 if (clientCb == nullptr) {
361 ALOGE("client callback is null");
362 return AMEDIA_ERROR_INVALID_PARAMETER;
363 }
364
365 if (mTranscoder != nullptr) {
366 ALOGE("transcoder already running");
367 return AMEDIA_ERROR_INVALID_OPERATION;
368 }
369
370 // Unwrap the callback and send heartbeats to the client after each operation during setup.
371 auto callback = mCallback.lock();
372 if (callback == nullptr) {
373 return AMEDIA_ERROR_INVALID_OPERATION;
374 }
375
376 Status status;
377 ::ndk::ScopedFileDescriptor srcFd, dstFd;
378 int srcFdInt = request.sourceFd.get();
379 if (srcFdInt < 0) {
380 status = clientCb->openFileDescriptor(request.sourceFilePath, "r", &srcFd);
381 if (!status.isOk() || srcFd.get() < 0) {
382 ALOGE("failed to open source");
383 *failureReason = TranscodingLogger::SessionEndedReason::OPEN_SRC_FD_FAILED;
384 return AMEDIA_ERROR_IO;
385 }
386 srcFdInt = srcFd.get();
387 }
388
389 callback->onHeartBeat(clientId, sessionId);
390
391 int dstFdInt = request.destinationFd.get();
392 if (dstFdInt < 0) {
393 // Open dest file with "rw", as the transcoder could potentially reuse part of it
394 // for resume case. We might want the further differentiate and open with "w" only
395 // for start.
396 status = clientCb->openFileDescriptor(request.destinationFilePath, "rw", &dstFd);
397 if (!status.isOk() || dstFd.get() < 0) {
398 ALOGE("failed to open destination");
399 *failureReason = TranscodingLogger::SessionEndedReason::OPEN_DST_FD_FAILED;
400 return AMEDIA_ERROR_IO;
401 }
402 dstFdInt = dstFd.get();
403 }
404
405 callback->onHeartBeat(clientId, sessionId);
406
407 mCurrentClientId = clientId;
408 mCurrentSessionId = sessionId;
409 mCurrentCallingUid = callingUid;
410 mTranscoderCb = std::make_shared<CallbackImpl>(shared_from_this(), clientId, sessionId);
411 mTranscoder = MediaTranscoder::create(mTranscoderCb, mHeartBeatIntervalUs, request.clientPid,
412 request.clientUid, pausedState);
413 if (mTranscoder == nullptr) {
414 ALOGE("failed to create transcoder");
415 *failureReason = TranscodingLogger::SessionEndedReason::CREATE_FAILED;
416 return AMEDIA_ERROR_UNKNOWN;
417 }
418
419 callback->onHeartBeat(clientId, sessionId);
420
421 media_status_t err = mTranscoder->configureSource(srcFdInt);
422 if (err != AMEDIA_OK) {
423 ALOGE("failed to configure source: %d", err);
424 *failureReason = TranscodingLogger::SessionEndedReason::CONFIG_SRC_FAILED;
425 return err;
426 }
427
428 callback->onHeartBeat(clientId, sessionId);
429
430 std::vector<std::shared_ptr<AMediaFormat>> trackFormats = mTranscoder->getTrackFormats();
431 if (trackFormats.size() == 0) {
432 ALOGE("failed to get track formats!");
433 *failureReason = TranscodingLogger::SessionEndedReason::NO_TRACKS;
434 return AMEDIA_ERROR_MALFORMED;
435 }
436
437 callback->onHeartBeat(clientId, sessionId);
438
439 for (int i = 0; i < trackFormats.size(); ++i) {
440 std::shared_ptr<AMediaFormat> format;
441 const char* mime = nullptr;
442 AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime);
443
444 if (!strncmp(mime, "video/", 6)) {
445 format = getVideoFormat(mime, request.requestedVideoTrackFormat);
446
447 mSrcFormat = trackFormats[i];
448 mDstFormat = format;
449 }
450
451 err = mTranscoder->configureTrackFormat(i, format.get());
452 if (err != AMEDIA_OK) {
453 ALOGE("failed to configure track format for track %d: %d", i, err);
454 *failureReason = TranscodingLogger::SessionEndedReason::CONFIG_TRACK_FAILED;
455 return err;
456 }
457
458 callback->onHeartBeat(clientId, sessionId);
459 }
460
461 err = mTranscoder->configureDestination(dstFdInt);
462 if (err != AMEDIA_OK) {
463 ALOGE("failed to configure dest: %d", err);
464 *failureReason = TranscodingLogger::SessionEndedReason::CONFIG_DST_FAILED;
465 return err;
466 }
467
468 callback->onHeartBeat(clientId, sessionId);
469
470 return AMEDIA_OK;
471 }
472
handleStart(ClientIdType clientId,SessionIdType sessionId,const TranscodingRequestParcel & request,uid_t callingUid,const std::shared_ptr<ITranscodingClientCallback> & clientCb)473 media_status_t TranscoderWrapper::handleStart(
474 ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
475 uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
476 ALOGI("%s: setting up transcoder for start", __FUNCTION__);
477 TranscodingLogger::SessionEndedReason reason = TranscodingLogger::SessionEndedReason::UNKNOWN;
478 media_status_t err =
479 setupTranscoder(clientId, sessionId, request, callingUid, clientCb, &reason);
480 if (err != AMEDIA_OK) {
481 ALOGE("%s: failed to setup transcoder", __FUNCTION__);
482 logSessionEnded(reason, err);
483 return err;
484 }
485
486 mTranscodeStartTime = std::chrono::steady_clock::now();
487
488 err = mTranscoder->start();
489 if (err != AMEDIA_OK) {
490 ALOGE("%s: failed to start transcoder: %d", __FUNCTION__, err);
491 logSessionEnded(TranscodingLogger::SessionEndedReason::START_FAILED, err);
492 return err;
493 }
494
495 ALOGI("%s: transcoder started", __FUNCTION__);
496 return AMEDIA_OK;
497 }
498
handlePause(ClientIdType clientId,SessionIdType sessionId)499 media_status_t TranscoderWrapper::handlePause(ClientIdType clientId, SessionIdType sessionId) {
500 if (mTranscoder == nullptr) {
501 ALOGE("%s: transcoder is not running", __FUNCTION__);
502 return AMEDIA_ERROR_INVALID_OPERATION;
503 }
504
505 if (clientId != mCurrentClientId || sessionId != mCurrentSessionId) {
506 ALOGW("%s: stopping session {%lld, %d} that's not current session {%lld, %d}", __FUNCTION__,
507 (long long)clientId, sessionId, (long long)mCurrentClientId, mCurrentSessionId);
508 }
509
510 ALOGI("%s: pausing transcoder", __FUNCTION__);
511
512 std::shared_ptr<ndk::ScopedAParcel> pauseStates;
513 media_status_t err = mTranscoder->pause(&pauseStates);
514 logSessionEnded(TranscodingLogger::SessionEndedReason::PAUSED, err);
515 if (err != AMEDIA_OK) {
516 ALOGE("%s: failed to pause transcoder: %d", __FUNCTION__, err);
517 return err;
518 }
519 mPausedStateMap[SessionKeyType(clientId, sessionId)] = pauseStates;
520
521 ALOGI("%s: transcoder paused", __FUNCTION__);
522 return AMEDIA_OK;
523 }
524
handleResume(ClientIdType clientId,SessionIdType sessionId,const TranscodingRequestParcel & request,uid_t callingUid,const std::shared_ptr<ITranscodingClientCallback> & clientCb)525 media_status_t TranscoderWrapper::handleResume(
526 ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
527 uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
528 std::shared_ptr<ndk::ScopedAParcel> pausedState;
529 auto it = mPausedStateMap.find(SessionKeyType(clientId, sessionId));
530 if (it != mPausedStateMap.end()) {
531 pausedState = it->second;
532 mPausedStateMap.erase(it);
533 } else {
534 ALOGE("%s: can't find paused state", __FUNCTION__);
535 return AMEDIA_ERROR_INVALID_OPERATION;
536 }
537
538 ALOGI("%s: setting up transcoder for resume", __FUNCTION__);
539 TranscodingLogger::SessionEndedReason reason = TranscodingLogger::SessionEndedReason::UNKNOWN;
540 media_status_t err = setupTranscoder(clientId, sessionId, request, callingUid, clientCb,
541 &reason, pausedState);
542 if (err != AMEDIA_OK) {
543 ALOGE("%s: failed to setup transcoder: %d", __FUNCTION__, err);
544 logSessionEnded(reason, err);
545 return err;
546 }
547
548 // Note: For now resume() will just restart transcoding from the beginning, so there is no need
549 // to distinguish between resume and start from a performance perspective.
550 mTranscodeStartTime = std::chrono::steady_clock::now();
551
552 err = mTranscoder->resume();
553 if (err != AMEDIA_OK) {
554 ALOGE("%s: failed to resume transcoder: %d", __FUNCTION__, err);
555 logSessionEnded(TranscodingLogger::SessionEndedReason::RESUME_FAILED, err);
556 return err;
557 }
558
559 ALOGI("%s: transcoder resumed", __FUNCTION__);
560 return AMEDIA_OK;
561 }
562
cleanup()563 void TranscoderWrapper::cleanup() {
564 mCurrentClientId = 0;
565 mCurrentSessionId = -1;
566 mCurrentCallingUid = -1;
567 mTranscoderCb = nullptr;
568 mTranscoder = nullptr;
569 mSrcFormat = nullptr;
570 mDstFormat = nullptr;
571 }
572
logSessionEnded(const TranscodingLogger::SessionEndedReason & reason,int error)573 void TranscoderWrapper::logSessionEnded(const TranscodingLogger::SessionEndedReason& reason,
574 int error) {
575 std::chrono::microseconds transcodeDuration(-1);
576 if (reason == TranscodingLogger::SessionEndedReason::FINISHED && error == AMEDIA_OK) {
577 transcodeDuration = std::chrono::duration_cast<std::chrono::microseconds>(
578 std::chrono::steady_clock::now() - mTranscodeStartTime);
579 }
580
581 mLogger->logSessionEnded(reason, mCurrentCallingUid, error, transcodeDuration, mSrcFormat.get(),
582 mDstFormat.get());
583 }
584
queueEvent(Event::Type type,ClientIdType clientId,SessionIdType sessionId,const std::function<void ()> runnable,int32_t arg)585 void TranscoderWrapper::queueEvent(Event::Type type, ClientIdType clientId, SessionIdType sessionId,
586 const std::function<void()> runnable, int32_t arg) {
587 std::scoped_lock lock{mLock};
588
589 if (!mLooperReady) {
590 // A shared_ptr to ourselves is given to the thread's stack, so that the TranscoderWrapper
591 // object doesn't go away until the thread exits. When a watchdog timeout happens, this
592 // allows the session controller to release its reference to the TranscoderWrapper object
593 // without blocking on the thread exits.
594 std::thread([owner = shared_from_this()]() { owner->threadLoop(); }).detach();
595 mLooperReady = true;
596 }
597
598 mQueue.push_back({type, clientId, sessionId, runnable, arg});
599 mCondition.notify_one();
600 }
601
threadLoop()602 void TranscoderWrapper::threadLoop() {
603 androidSetThreadPriority(0 /*tid (0 = current) */, ANDROID_PRIORITY_BACKGROUND);
604 std::unique_lock<std::mutex> lock{mLock};
605 // TranscoderWrapper currently lives in the transcoding service, as long as
606 // MediaTranscodingService itself.
607 while (true) {
608 // Wait for the next event.
609 while (mQueue.empty()) {
610 mCondition.wait(lock);
611 }
612
613 Event event = *mQueue.begin();
614 mQueue.pop_front();
615
616 ALOGV("%s: %s", __FUNCTION__, toString(event).c_str());
617
618 if (event.type == Event::Abandon) {
619 break;
620 }
621
622 lock.unlock();
623 event.runnable();
624 lock.lock();
625 }
626 }
627 } // namespace android
628