Loading media/libmediatranscoding/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,8 @@ cc_library_shared { "TranscodingClientManager.cpp", "TranscodingJobScheduler.cpp", "TranscodingUidPolicy.cpp", "TranscoderWrapper.cpp", "NdkCommon.cpp", ], shared_libs: [ Loading @@ -57,6 +59,7 @@ cc_library_shared { "libutils", "libmediatranscoder", "libbinder", "libmediandk", ], export_include_dirs: ["include"], Loading media/libmediatranscoding/NdkCommon.cpp 0 → 100644 +40 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "NdkCommon" #include <log/log.h> #include <media/NdkCommon.h> #include <cstdio> #include <cstring> #include <utility> /* TODO(b/153592281) * Note: constants used by the native media tests but not available in media ndk api */ const char* AMEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; const char* AMEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9"; const char* AMEDIA_MIMETYPE_VIDEO_AV1 = "video/av01"; const char* AMEDIA_MIMETYPE_VIDEO_AVC = "video/avc"; const char* AMEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc"; const char* AMEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es"; const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp"; /* TODO(b/153592281) */ const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync"; const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate"; const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes"; const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode"; media/libmediatranscoding/TranscoderWrapper.cpp 0 → 100644 +346 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "TranscoderWrapper" #include <aidl/android/media/TranscodingErrorCode.h> #include <aidl/android/media/TranscodingRequestParcel.h> #include <media/MediaTranscoder.h> #include <media/NdkCommon.h> #include <media/TranscoderWrapper.h> #include <utils/Log.h> #include <thread> namespace android { using Status = ::ndk::ScopedAStatus; using ::aidl::android::media::TranscodingErrorCode; using ::aidl::android::media::TranscodingVideoCodecType; using ::aidl::android::media::TranscodingVideoTrackFormat; static TranscodingErrorCode toTranscodingError(media_status_t status) { switch (status) { case AMEDIA_OK: return TranscodingErrorCode::kNoError; case AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE: // FALLTHRU case AMEDIACODEC_ERROR_RECLAIMED: return TranscodingErrorCode::kInsufficientResources; case AMEDIA_ERROR_MALFORMED: return TranscodingErrorCode::kMalformed; case AMEDIA_ERROR_UNSUPPORTED: return TranscodingErrorCode::kUnsupported; case AMEDIA_ERROR_INVALID_OBJECT: // FALLTHRU case AMEDIA_ERROR_INVALID_PARAMETER: return TranscodingErrorCode::kInvalidParameter; case AMEDIA_ERROR_INVALID_OPERATION: return TranscodingErrorCode::kInvalidOperation; case AMEDIA_ERROR_IO: return TranscodingErrorCode::kErrorIO; case AMEDIA_ERROR_UNKNOWN: // FALLTHRU default: return TranscodingErrorCode::kUnknown; } } //static const char* TranscoderWrapper::toString(Event::Type type) { switch (type) { case Event::Start: return "Start"; case Event::Pause: return "Pause"; case Event::Resume: return "Resume"; case Event::Stop: return "Stop"; case Event::Finish: return "Finish"; case Event::Error: return "Error"; default: break; } return "(unknown)"; } class TranscoderWrapper::CallbackImpl : public MediaTranscoder::CallbackInterface { public: CallbackImpl(const std::shared_ptr<TranscoderWrapper>& owner, ClientIdType clientId, JobIdType jobId) : mOwner(owner), mClientId(clientId), mJobId(jobId) {} virtual void onFinished(const MediaTranscoder* transcoder __unused) override { auto owner = mOwner.lock(); if (owner != nullptr) { owner->onFinish(mClientId, mJobId); } } virtual void onError(const MediaTranscoder* transcoder __unused, media_status_t error) override { auto owner = mOwner.lock(); if (owner != nullptr) { owner->onError(mClientId, mJobId, toTranscodingError(error)); } } virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused, int32_t progress) override { ALOGV("%s: job {%lld, %d}, progress %d", __FUNCTION__, (long long)mClientId, mJobId, progress); } virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused, const std::shared_ptr<const Parcelable>& pausedState __unused) override { ALOGV("%s: job {%lld, %d}", __FUNCTION__, (long long)mClientId, mJobId); } private: std::weak_ptr<TranscoderWrapper> mOwner; ClientIdType mClientId; JobIdType mJobId; }; TranscoderWrapper::TranscoderWrapper() : mCurrentClientId(0), mCurrentJobId(-1) { std::thread(&TranscoderWrapper::threadLoop, this).detach(); } void TranscoderWrapper::setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) { mCallback = cb; } void TranscoderWrapper::start(ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request, const std::shared_ptr<ITranscodingClientCallback>& callback) { queueEvent(Event::Start, clientId, jobId, [=] { TranscodingErrorCode err = handleStart(clientId, jobId, request, callback); if (err != TranscodingErrorCode::kNoError) { cleanup(); auto callback = mCallback.lock(); if (callback != nullptr) { callback->onError(clientId, jobId, err); } } }); } void TranscoderWrapper::pause(ClientIdType clientId, JobIdType jobId) { queueEvent(Event::Pause, clientId, jobId, [] {}); } void TranscoderWrapper::resume(ClientIdType clientId, JobIdType jobId) { queueEvent(Event::Resume, clientId, jobId, [] {}); } void TranscoderWrapper::stop(ClientIdType clientId, JobIdType jobId) { queueEvent(Event::Stop, clientId, jobId, [=] { if (clientId != mCurrentClientId || jobId != mCurrentJobId) { ALOGW("Stopping job {%lld, %d} that's not current job {%lld, %d}", (long long)clientId, jobId, (long long)mCurrentClientId, mCurrentJobId); } // stop transcoder. media_status_t err = mTranscoder->cancel(); if (err != AMEDIA_OK) { ALOGE("failed to stop transcoder: %d", err); } else { ALOGI("transcoder stopped"); } cleanup(); }); } void TranscoderWrapper::onFinish(ClientIdType clientId, JobIdType jobId) { queueEvent(Event::Finish, clientId, jobId, [=] { cleanup(); auto callback = mCallback.lock(); if (callback != nullptr) { callback->onFinish(clientId, jobId); } }); } void TranscoderWrapper::onError(ClientIdType clientId, JobIdType jobId, TranscodingErrorCode error) { queueEvent(Event::Error, clientId, jobId, [=] { cleanup(); auto callback = mCallback.lock(); if (callback != nullptr) { callback->onError(clientId, jobId, error); } }); } static AMediaFormat* getVideoFormat( const char* originalMime, const std::optional<TranscodingVideoTrackFormat>& requestedFormat) { if (requestedFormat == std::nullopt) { return nullptr; } AMediaFormat* format = AMediaFormat_new(); bool changed = false; if (requestedFormat->codecType == TranscodingVideoCodecType::kHevc && strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_HEVC)) { AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_HEVC); changed = true; } else if (requestedFormat->codecType == TranscodingVideoCodecType::kAvc && strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_AVC)) { AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC); changed = true; } if (requestedFormat->bitrateBps > 0) { AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, requestedFormat->bitrateBps); changed = true; } // TODO: translate other fields from requestedFormat to the format for MediaTranscoder. // Also need to determine more settings to expose in TranscodingVideoTrackFormat. if (!changed) { AMediaFormat_delete(format); // Use null format for passthru. format = nullptr; } return format; } TranscodingErrorCode TranscoderWrapper::handleStart( ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request, const std::shared_ptr<ITranscodingClientCallback>& clientCb) { if (clientCb == nullptr) { ALOGE("client callback is null"); return TranscodingErrorCode::kInvalidParameter; } if (mTranscoder != nullptr) { ALOGE("transcoder already running"); return TranscodingErrorCode::kInvalidOperation; } Status status; ::ndk::ScopedFileDescriptor srcFd, dstFd; status = clientCb->openFileDescriptor(request.sourceFilePath, "r", &srcFd); if (!status.isOk() || srcFd.get() < 0) { ALOGE("failed to open source"); return TranscodingErrorCode::kErrorIO; } status = clientCb->openFileDescriptor(request.destinationFilePath, "w", &dstFd); if (!status.isOk() || dstFd.get() < 0) { ALOGE("failed to open destination"); return TranscodingErrorCode::kErrorIO; } mCurrentClientId = clientId; mCurrentJobId = jobId; mTranscoderCb = std::make_shared<CallbackImpl>(shared_from_this(), clientId, jobId); mTranscoder = MediaTranscoder::create(mTranscoderCb, nullptr); if (mTranscoder == nullptr) { ALOGE("failed to create transcoder"); return TranscodingErrorCode::kUnknown; } media_status_t err = mTranscoder->configureSource(srcFd.get()); if (err != AMEDIA_OK) { ALOGE("failed to configure source: %d", err); return toTranscodingError(err); } std::vector<std::shared_ptr<AMediaFormat>> trackFormats = mTranscoder->getTrackFormats(); if (trackFormats.size() == 0) { ALOGE("failed to get track formats!"); return TranscodingErrorCode::kMalformed; } for (int i = 0; i < trackFormats.size(); ++i) { AMediaFormat* format = nullptr; const char* mime = nullptr; AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime); if (!strncmp(mime, "video/", 6)) { format = getVideoFormat(mime, request.requestedVideoTrackFormat); } err = mTranscoder->configureTrackFormat(i, format); if (format != nullptr) { AMediaFormat_delete(format); } if (err != AMEDIA_OK) { ALOGE("failed to configure track format for track %d: %d", i, err); return toTranscodingError(err); } } err = mTranscoder->configureDestination(dstFd.get()); if (err != AMEDIA_OK) { ALOGE("failed to configure dest: %d", err); return toTranscodingError(err); } err = mTranscoder->start(); if (err != AMEDIA_OK) { ALOGE("failed to start transcoder: %d", err); return toTranscodingError(err); } ALOGI("transcoder started"); return TranscodingErrorCode::kNoError; } void TranscoderWrapper::cleanup() { mCurrentClientId = 0; mCurrentJobId = -1; mTranscoderCb = nullptr; mTranscoder = nullptr; } void TranscoderWrapper::queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId, const std::function<void()> runnable) { ALOGV("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)clientId, jobId, toString(type)); std::scoped_lock lock{mLock}; mQueue.push_back({type, clientId, jobId, runnable}); mCondition.notify_one(); } void TranscoderWrapper::threadLoop() { std::unique_lock<std::mutex> lock{mLock}; // TranscoderWrapper currently lives in the transcoding service, as long as // MediaTranscodingService itself. while (true) { // Wait for the next event. while (mQueue.empty()) { mCondition.wait(lock); } Event event = *mQueue.begin(); mQueue.pop_front(); ALOGD("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)event.clientId, event.jobId, toString(event.type)); event.runnable(); } } } // namespace android media/libmediatranscoding/TranscodingJobScheduler.cpp +4 −2 Original line number Diff line number Diff line Loading @@ -76,7 +76,8 @@ void TranscodingJobScheduler::updateCurrentJob_l() { // the topJob now. if (!mResourceLost) { if (topJob->state == Job::NOT_STARTED) { mTranscoder->start(topJob->key.first, topJob->key.second, topJob->request); mTranscoder->start(topJob->key.first, topJob->key.second, topJob->request, topJob->callback.lock()); } else if (topJob->state == Job::PAUSED) { mTranscoder->resume(topJob->key.first, topJob->key.second); } Loading Loading @@ -349,7 +350,8 @@ void TranscodingJobScheduler::onFinish(ClientIdType clientId, JobIdType jobId) { { auto clientCallback = mJobMap[jobKey].callback.lock(); if (clientCallback != nullptr) { clientCallback->onTranscodingFinished(jobId, TranscodingResultParcel({jobId, -1 /*actualBitrateBps*/})); clientCallback->onTranscodingFinished( jobId, TranscodingResultParcel({jobId, -1 /*actualBitrateBps*/})); } } Loading media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl +8 −7 Original line number Diff line number Diff line Loading @@ -23,11 +23,12 @@ package android.media; */ @Backing(type = "int") enum TranscodingErrorCode { kUnknown = 0, kUnsupported = 1, kDecoderError = 2, kEncoderError = 3, kExtractorError = 4, kMuxerError = 5, kInvalidBitstream = 6 kNoError = 0, kUnknown = 1, kMalformed = 2, kUnsupported = 3, kInvalidParameter = 4, kInvalidOperation = 5, kErrorIO = 6, kInsufficientResources = 7, } No newline at end of file Loading
media/libmediatranscoding/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,8 @@ cc_library_shared { "TranscodingClientManager.cpp", "TranscodingJobScheduler.cpp", "TranscodingUidPolicy.cpp", "TranscoderWrapper.cpp", "NdkCommon.cpp", ], shared_libs: [ Loading @@ -57,6 +59,7 @@ cc_library_shared { "libutils", "libmediatranscoder", "libbinder", "libmediandk", ], export_include_dirs: ["include"], Loading
media/libmediatranscoding/NdkCommon.cpp 0 → 100644 +40 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "NdkCommon" #include <log/log.h> #include <media/NdkCommon.h> #include <cstdio> #include <cstring> #include <utility> /* TODO(b/153592281) * Note: constants used by the native media tests but not available in media ndk api */ const char* AMEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; const char* AMEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9"; const char* AMEDIA_MIMETYPE_VIDEO_AV1 = "video/av01"; const char* AMEDIA_MIMETYPE_VIDEO_AVC = "video/avc"; const char* AMEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc"; const char* AMEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es"; const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp"; /* TODO(b/153592281) */ const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync"; const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate"; const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes"; const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
media/libmediatranscoding/TranscoderWrapper.cpp 0 → 100644 +346 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "TranscoderWrapper" #include <aidl/android/media/TranscodingErrorCode.h> #include <aidl/android/media/TranscodingRequestParcel.h> #include <media/MediaTranscoder.h> #include <media/NdkCommon.h> #include <media/TranscoderWrapper.h> #include <utils/Log.h> #include <thread> namespace android { using Status = ::ndk::ScopedAStatus; using ::aidl::android::media::TranscodingErrorCode; using ::aidl::android::media::TranscodingVideoCodecType; using ::aidl::android::media::TranscodingVideoTrackFormat; static TranscodingErrorCode toTranscodingError(media_status_t status) { switch (status) { case AMEDIA_OK: return TranscodingErrorCode::kNoError; case AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE: // FALLTHRU case AMEDIACODEC_ERROR_RECLAIMED: return TranscodingErrorCode::kInsufficientResources; case AMEDIA_ERROR_MALFORMED: return TranscodingErrorCode::kMalformed; case AMEDIA_ERROR_UNSUPPORTED: return TranscodingErrorCode::kUnsupported; case AMEDIA_ERROR_INVALID_OBJECT: // FALLTHRU case AMEDIA_ERROR_INVALID_PARAMETER: return TranscodingErrorCode::kInvalidParameter; case AMEDIA_ERROR_INVALID_OPERATION: return TranscodingErrorCode::kInvalidOperation; case AMEDIA_ERROR_IO: return TranscodingErrorCode::kErrorIO; case AMEDIA_ERROR_UNKNOWN: // FALLTHRU default: return TranscodingErrorCode::kUnknown; } } //static const char* TranscoderWrapper::toString(Event::Type type) { switch (type) { case Event::Start: return "Start"; case Event::Pause: return "Pause"; case Event::Resume: return "Resume"; case Event::Stop: return "Stop"; case Event::Finish: return "Finish"; case Event::Error: return "Error"; default: break; } return "(unknown)"; } class TranscoderWrapper::CallbackImpl : public MediaTranscoder::CallbackInterface { public: CallbackImpl(const std::shared_ptr<TranscoderWrapper>& owner, ClientIdType clientId, JobIdType jobId) : mOwner(owner), mClientId(clientId), mJobId(jobId) {} virtual void onFinished(const MediaTranscoder* transcoder __unused) override { auto owner = mOwner.lock(); if (owner != nullptr) { owner->onFinish(mClientId, mJobId); } } virtual void onError(const MediaTranscoder* transcoder __unused, media_status_t error) override { auto owner = mOwner.lock(); if (owner != nullptr) { owner->onError(mClientId, mJobId, toTranscodingError(error)); } } virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused, int32_t progress) override { ALOGV("%s: job {%lld, %d}, progress %d", __FUNCTION__, (long long)mClientId, mJobId, progress); } virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused, const std::shared_ptr<const Parcelable>& pausedState __unused) override { ALOGV("%s: job {%lld, %d}", __FUNCTION__, (long long)mClientId, mJobId); } private: std::weak_ptr<TranscoderWrapper> mOwner; ClientIdType mClientId; JobIdType mJobId; }; TranscoderWrapper::TranscoderWrapper() : mCurrentClientId(0), mCurrentJobId(-1) { std::thread(&TranscoderWrapper::threadLoop, this).detach(); } void TranscoderWrapper::setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) { mCallback = cb; } void TranscoderWrapper::start(ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request, const std::shared_ptr<ITranscodingClientCallback>& callback) { queueEvent(Event::Start, clientId, jobId, [=] { TranscodingErrorCode err = handleStart(clientId, jobId, request, callback); if (err != TranscodingErrorCode::kNoError) { cleanup(); auto callback = mCallback.lock(); if (callback != nullptr) { callback->onError(clientId, jobId, err); } } }); } void TranscoderWrapper::pause(ClientIdType clientId, JobIdType jobId) { queueEvent(Event::Pause, clientId, jobId, [] {}); } void TranscoderWrapper::resume(ClientIdType clientId, JobIdType jobId) { queueEvent(Event::Resume, clientId, jobId, [] {}); } void TranscoderWrapper::stop(ClientIdType clientId, JobIdType jobId) { queueEvent(Event::Stop, clientId, jobId, [=] { if (clientId != mCurrentClientId || jobId != mCurrentJobId) { ALOGW("Stopping job {%lld, %d} that's not current job {%lld, %d}", (long long)clientId, jobId, (long long)mCurrentClientId, mCurrentJobId); } // stop transcoder. media_status_t err = mTranscoder->cancel(); if (err != AMEDIA_OK) { ALOGE("failed to stop transcoder: %d", err); } else { ALOGI("transcoder stopped"); } cleanup(); }); } void TranscoderWrapper::onFinish(ClientIdType clientId, JobIdType jobId) { queueEvent(Event::Finish, clientId, jobId, [=] { cleanup(); auto callback = mCallback.lock(); if (callback != nullptr) { callback->onFinish(clientId, jobId); } }); } void TranscoderWrapper::onError(ClientIdType clientId, JobIdType jobId, TranscodingErrorCode error) { queueEvent(Event::Error, clientId, jobId, [=] { cleanup(); auto callback = mCallback.lock(); if (callback != nullptr) { callback->onError(clientId, jobId, error); } }); } static AMediaFormat* getVideoFormat( const char* originalMime, const std::optional<TranscodingVideoTrackFormat>& requestedFormat) { if (requestedFormat == std::nullopt) { return nullptr; } AMediaFormat* format = AMediaFormat_new(); bool changed = false; if (requestedFormat->codecType == TranscodingVideoCodecType::kHevc && strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_HEVC)) { AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_HEVC); changed = true; } else if (requestedFormat->codecType == TranscodingVideoCodecType::kAvc && strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_AVC)) { AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC); changed = true; } if (requestedFormat->bitrateBps > 0) { AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, requestedFormat->bitrateBps); changed = true; } // TODO: translate other fields from requestedFormat to the format for MediaTranscoder. // Also need to determine more settings to expose in TranscodingVideoTrackFormat. if (!changed) { AMediaFormat_delete(format); // Use null format for passthru. format = nullptr; } return format; } TranscodingErrorCode TranscoderWrapper::handleStart( ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request, const std::shared_ptr<ITranscodingClientCallback>& clientCb) { if (clientCb == nullptr) { ALOGE("client callback is null"); return TranscodingErrorCode::kInvalidParameter; } if (mTranscoder != nullptr) { ALOGE("transcoder already running"); return TranscodingErrorCode::kInvalidOperation; } Status status; ::ndk::ScopedFileDescriptor srcFd, dstFd; status = clientCb->openFileDescriptor(request.sourceFilePath, "r", &srcFd); if (!status.isOk() || srcFd.get() < 0) { ALOGE("failed to open source"); return TranscodingErrorCode::kErrorIO; } status = clientCb->openFileDescriptor(request.destinationFilePath, "w", &dstFd); if (!status.isOk() || dstFd.get() < 0) { ALOGE("failed to open destination"); return TranscodingErrorCode::kErrorIO; } mCurrentClientId = clientId; mCurrentJobId = jobId; mTranscoderCb = std::make_shared<CallbackImpl>(shared_from_this(), clientId, jobId); mTranscoder = MediaTranscoder::create(mTranscoderCb, nullptr); if (mTranscoder == nullptr) { ALOGE("failed to create transcoder"); return TranscodingErrorCode::kUnknown; } media_status_t err = mTranscoder->configureSource(srcFd.get()); if (err != AMEDIA_OK) { ALOGE("failed to configure source: %d", err); return toTranscodingError(err); } std::vector<std::shared_ptr<AMediaFormat>> trackFormats = mTranscoder->getTrackFormats(); if (trackFormats.size() == 0) { ALOGE("failed to get track formats!"); return TranscodingErrorCode::kMalformed; } for (int i = 0; i < trackFormats.size(); ++i) { AMediaFormat* format = nullptr; const char* mime = nullptr; AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime); if (!strncmp(mime, "video/", 6)) { format = getVideoFormat(mime, request.requestedVideoTrackFormat); } err = mTranscoder->configureTrackFormat(i, format); if (format != nullptr) { AMediaFormat_delete(format); } if (err != AMEDIA_OK) { ALOGE("failed to configure track format for track %d: %d", i, err); return toTranscodingError(err); } } err = mTranscoder->configureDestination(dstFd.get()); if (err != AMEDIA_OK) { ALOGE("failed to configure dest: %d", err); return toTranscodingError(err); } err = mTranscoder->start(); if (err != AMEDIA_OK) { ALOGE("failed to start transcoder: %d", err); return toTranscodingError(err); } ALOGI("transcoder started"); return TranscodingErrorCode::kNoError; } void TranscoderWrapper::cleanup() { mCurrentClientId = 0; mCurrentJobId = -1; mTranscoderCb = nullptr; mTranscoder = nullptr; } void TranscoderWrapper::queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId, const std::function<void()> runnable) { ALOGV("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)clientId, jobId, toString(type)); std::scoped_lock lock{mLock}; mQueue.push_back({type, clientId, jobId, runnable}); mCondition.notify_one(); } void TranscoderWrapper::threadLoop() { std::unique_lock<std::mutex> lock{mLock}; // TranscoderWrapper currently lives in the transcoding service, as long as // MediaTranscodingService itself. while (true) { // Wait for the next event. while (mQueue.empty()) { mCondition.wait(lock); } Event event = *mQueue.begin(); mQueue.pop_front(); ALOGD("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)event.clientId, event.jobId, toString(event.type)); event.runnable(); } } } // namespace android
media/libmediatranscoding/TranscodingJobScheduler.cpp +4 −2 Original line number Diff line number Diff line Loading @@ -76,7 +76,8 @@ void TranscodingJobScheduler::updateCurrentJob_l() { // the topJob now. if (!mResourceLost) { if (topJob->state == Job::NOT_STARTED) { mTranscoder->start(topJob->key.first, topJob->key.second, topJob->request); mTranscoder->start(topJob->key.first, topJob->key.second, topJob->request, topJob->callback.lock()); } else if (topJob->state == Job::PAUSED) { mTranscoder->resume(topJob->key.first, topJob->key.second); } Loading Loading @@ -349,7 +350,8 @@ void TranscodingJobScheduler::onFinish(ClientIdType clientId, JobIdType jobId) { { auto clientCallback = mJobMap[jobKey].callback.lock(); if (clientCallback != nullptr) { clientCallback->onTranscodingFinished(jobId, TranscodingResultParcel({jobId, -1 /*actualBitrateBps*/})); clientCallback->onTranscodingFinished( jobId, TranscodingResultParcel({jobId, -1 /*actualBitrateBps*/})); } } Loading
media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl +8 −7 Original line number Diff line number Diff line Loading @@ -23,11 +23,12 @@ package android.media; */ @Backing(type = "int") enum TranscodingErrorCode { kUnknown = 0, kUnsupported = 1, kDecoderError = 2, kEncoderError = 3, kExtractorError = 4, kMuxerError = 5, kInvalidBitstream = 6 kNoError = 0, kUnknown = 1, kMalformed = 2, kUnsupported = 3, kInvalidParameter = 4, kInvalidOperation = 5, kErrorIO = 6, kInsufficientResources = 7, } No newline at end of file