• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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