Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d1c4c46 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "transcoding: hooking up real transcoder in service"

parents b55b1586 66469276
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ cc_library_shared {
        "TranscodingClientManager.cpp",
        "TranscodingJobScheduler.cpp",
        "TranscodingUidPolicy.cpp",
        "TranscoderWrapper.cpp",
        "NdkCommon.cpp",
    ],

    shared_libs: [
@@ -57,6 +59,7 @@ cc_library_shared {
        "libutils",
        "libmediatranscoder",
        "libbinder",
        "libmediandk",
    ],

    export_include_dirs: ["include"],
+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";
+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
+4 −2
Original line number Diff line number Diff line
@@ -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);
            }
@@ -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*/}));
            }
        }

+8 −7
Original line number Diff line number Diff line
@@ -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