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

Commit ad388871 authored by Linus Nilsson's avatar Linus Nilsson Committed by Android (Google) Code Review
Browse files

Merge "Transcoder: Allow setting operating rate and priority on both encoder and decoder"

parents 289079f0 16d772b3
Loading
Loading
Loading
Loading
+19 −33
Original line number Diff line number Diff line
@@ -23,24 +23,13 @@
#include <media/MediaSampleReaderNDK.h>
#include <media/MediaSampleWriter.h>
#include <media/MediaTranscoder.h>
#include <media/NdkCommon.h>
#include <media/PassthroughTrackTranscoder.h>
#include <media/VideoTrackTranscoder.h>
#include <unistd.h>

namespace android {

#define DEFINE_FORMAT_VALUE_COPY_FUNC(_type, _typeName)                                  \
    static void copy##_typeName(const char* key, AMediaFormat* to, AMediaFormat* from) { \
        _type value;                                                                     \
        if (AMediaFormat_get##_typeName(from, key, &value)) {                            \
            AMediaFormat_set##_typeName(to, key, value);                                 \
        }                                                                                \
    }

DEFINE_FORMAT_VALUE_COPY_FUNC(const char*, String);
DEFINE_FORMAT_VALUE_COPY_FUNC(int64_t, Int64);
DEFINE_FORMAT_VALUE_COPY_FUNC(int32_t, Int32);

static AMediaFormat* mergeMediaFormats(AMediaFormat* base, AMediaFormat* overlay) {
    if (base == nullptr || overlay == nullptr) {
        LOG(ERROR) << "Cannot merge null formats";
@@ -58,29 +47,26 @@ static AMediaFormat* mergeMediaFormats(AMediaFormat* base, AMediaFormat* overlay
    // along with their value types and copy the ones that are present. A better solution would be
    // to either implement required functions in NDK or to parse the overlay format's string
    // representation and copy all existing keys.
    static const struct {
        const char* key;
        void (*copyValue)(const char* key, AMediaFormat* to, AMediaFormat* from);
    } kSupportedConfigs[] = {
            {AMEDIAFORMAT_KEY_MIME, copyString},
            {AMEDIAFORMAT_KEY_DURATION, copyInt64},
            {AMEDIAFORMAT_KEY_WIDTH, copyInt32},
            {AMEDIAFORMAT_KEY_HEIGHT, copyInt32},
            {AMEDIAFORMAT_KEY_BIT_RATE, copyInt32},
            {AMEDIAFORMAT_KEY_PROFILE, copyInt32},
            {AMEDIAFORMAT_KEY_LEVEL, copyInt32},
            {AMEDIAFORMAT_KEY_COLOR_FORMAT, copyInt32},
            {AMEDIAFORMAT_KEY_COLOR_RANGE, copyInt32},
            {AMEDIAFORMAT_KEY_COLOR_STANDARD, copyInt32},
            {AMEDIAFORMAT_KEY_COLOR_TRANSFER, copyInt32},
            {AMEDIAFORMAT_KEY_FRAME_RATE, copyInt32},
            {AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, copyInt32},
    static const AMediaFormatUtils::EntryCopier kSupportedFormatEntries[] = {
            ENTRY_COPIER(AMEDIAFORMAT_KEY_MIME, String),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_DURATION, Int64),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_WIDTH, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_HEIGHT, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_BIT_RATE, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_PROFILE, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_LEVEL, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_FORMAT, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_FRAME_RATE, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
            ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
    };
    const size_t entryCount = sizeof(kSupportedFormatEntries) / sizeof(kSupportedFormatEntries[0]);

    for (int i = 0; i < (sizeof(kSupportedConfigs) / sizeof(kSupportedConfigs[0])); ++i) {
        kSupportedConfigs[i].copyValue(kSupportedConfigs[i].key, format, overlay);
    }

    AMediaFormatUtils::CopyFormatEntries(overlay, format, kSupportedFormatEntries, entryCount);
    return format;
}

+36 −1
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "NdkCommon"

#include <log/log.h>
#include <android-base/logging.h>
#include <media/NdkCommon.h>

#include <cstdio>
@@ -39,3 +39,38 @@ const char* TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP = "allow-frame-drop";
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";

namespace AMediaFormatUtils {

#define DEFINE_FORMAT_VALUE_COPY_FUNC(_type, _typeName)                                      \
    bool CopyFormatEntry##_typeName(const char* key, AMediaFormat* from, AMediaFormat* to) { \
        _type value;                                                                         \
        if (AMediaFormat_get##_typeName(from, key, &value)) {                                \
            AMediaFormat_set##_typeName(to, key, value);                                     \
            return true;                                                                     \
        }                                                                                    \
        return false;                                                                        \
    }

DEFINE_FORMAT_VALUE_COPY_FUNC(const char*, String);
DEFINE_FORMAT_VALUE_COPY_FUNC(int64_t, Int64);
DEFINE_FORMAT_VALUE_COPY_FUNC(int32_t, Int32);
DEFINE_FORMAT_VALUE_COPY_FUNC(float, Float);

void CopyFormatEntries(AMediaFormat* from, AMediaFormat* to, const EntryCopier* entries,
                       size_t entryCount) {
    if (from == nullptr || to == nullptr) {
        LOG(ERROR) << "Cannot copy null formats";
        return;
    } else if (entries == nullptr || entryCount < 1) {
        LOG(WARNING) << "No entries to copy";
        return;
    }

    for (size_t i = 0; i < entryCount; ++i) {
        if (!entries[i].copy(entries[i].key, from, to) && entries[i].copy2 != nullptr) {
            entries[i].copy2(entries[i].key, from, to);
        }
    }
}
}  // namespace AMediaFormatUtils
 No newline at end of file
+9 −0
Original line number Diff line number Diff line
@@ -270,6 +270,15 @@ media_status_t VideoTrackTranscoder::configureDestinationFormat(
    // Prevent decoder from overwriting frames that the encoder has not yet consumed.
    AMediaFormat_setInt32(decoderFormat.get(), TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP, 0);

    // Copy over configurations that apply to both encoder and decoder.
    static const AMediaFormatUtils::EntryCopier kEncoderEntriesToCopy[] = {
            ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
            ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
    };
    const size_t entryCount = sizeof(kEncoderEntriesToCopy) / sizeof(kEncoderEntriesToCopy[0]);
    AMediaFormatUtils::CopyFormatEntries(mDestinationFormat.get(), decoderFormat.get(),
                                         kEncoderEntriesToCopy, entryCount);

    status = AMediaCodec_configure(mDecoder, decoderFormat.get(), mSurface, NULL /* crypto */,
                                   0 /* flags */);
    if (status != AMEDIA_OK) {
+75 −14
Original line number Diff line number Diff line
@@ -76,11 +76,27 @@ private:
    bool mFinished = false;
};

static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
                               const std::string& dstFileName, bool includeAudio,
                               bool transcodeVideo = true) {
static AMediaFormat* CreateDefaultVideoFormat() {
    // Default bitrate
    static constexpr int32_t kVideoBitRate = 20 * 1000 * 1000;  // 20Mbs

    AMediaFormat* videoFormat = AMediaFormat_new();
    AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
    return videoFormat;
}

/**
 * Callback to configure tracks for transcoding.
 * @param mime The source track mime type.
 * @param dstFormat The destination format if the track should be transcoded or nullptr if the track
 * should be passed through.
 * @return True if the track should be included in the output file.
 */
using TrackSelectionCallback = std::function<bool(const char* mime, AMediaFormat** dstFormat)>;

static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
                               const std::string& dstFileName,
                               TrackSelectionCallback trackSelectionCallback) {
    // Write-only, create file if non-existent.
    static constexpr int kDstOpenFlags = O_WRONLY | O_CREAT;
    // User R+W permission.
@@ -133,21 +149,17 @@ static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFi
            }

            if (strncmp(mime, "video/", 6) == 0) {
                if (transcodeVideo) {
                    dstFormat = AMediaFormat_new();
                    AMediaFormat_setInt32(dstFormat, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
                }

                int32_t frameCount;
                if (AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_COUNT, &frameCount)) {
                    state.counters["VideoFrameRate"] =
                            benchmark::Counter(frameCount, benchmark::Counter::kIsRate);
                }
            } else if (!includeAudio && strncmp(mime, "audio/", 6) == 0) {
                continue;
            }

            if (trackSelectionCallback(mime, &dstFormat)) {
                status = transcoder->configureTrackFormat(i, dstFormat);
            }

            if (dstFormat != nullptr) {
                AMediaFormat_delete(dstFormat);
            }
@@ -179,6 +191,21 @@ exit:
    if (dstFd > 0) close(dstFd);
}

static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
                               const std::string& dstFileName, bool includeAudio,
                               bool transcodeVideo) {
    TranscodeMediaFile(state, srcFileName, dstFileName,
                       [=](const char* mime, AMediaFormat** dstFormatOut) -> bool {
                           *dstFormatOut = nullptr;
                           if (strncmp(mime, "video/", 6) == 0 && transcodeVideo) {
                               *dstFormatOut = CreateDefaultVideoFormat();
                           } else if (strncmp(mime, "audio/", 6) == 0 && !includeAudio) {
                               return false;
                           }
                           return true;
                       });
}

// Benchmark registration wrapper for transcoding.
#define TRANSCODER_BENCHMARK(func) \
    BENCHMARK(func)->UseRealTime()->MeasureProcessCPUTime()->Unit(benchmark::kMillisecond)
@@ -186,19 +213,51 @@ exit:
static void BM_TranscodeAvc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
    TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
                       "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
                       true /* includeAudio */);
                       true /* includeAudio */, true /* transcodeVideo */);
}

static void BM_TranscodeAvc2AvcAudioVideo2Video(benchmark::State& state) {
    TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
                       "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_V.mp4",
                       false /* includeAudio */);
                       false /* includeAudio */, true /* transcodeVideo */);
}

static void BM_TranscodeAvc2AvcVideo2Video(benchmark::State& state) {
    TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
                       "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
                       false /* includeAudio */);
                       false /* includeAudio */, true /* transcodeVideo */);
}

static void BM_TranscodeAvc2AvcAV2AVMaxOperatingRate(benchmark::State& state) {
    TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
                       "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
                       [](const char* mime, AMediaFormat** dstFormatOut) -> bool {
                           AMediaFormat* dstFormat = nullptr;
                           if (strncmp(mime, "video/", 6) == 0) {
                               dstFormat = CreateDefaultVideoFormat();
                               AMediaFormat_setFloat(dstFormat, AMEDIAFORMAT_KEY_OPERATING_RATE,
                                                     INT32_MAX);
                               AMediaFormat_setInt32(dstFormat, AMEDIAFORMAT_KEY_PRIORITY, 1);
                           }
                           *dstFormatOut = dstFormat;
                           return true;
                       });
}

static void BM_TranscodeAvc2AvcV2VMaxOperatingRate(benchmark::State& state) {
    TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
                       "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
                       [](const char* mime, AMediaFormat** dstFormatOut) -> bool {
                           if (strncmp(mime, "video/", 6) == 0) {
                               AMediaFormat* dstFormat = CreateDefaultVideoFormat();
                               AMediaFormat_setFloat(dstFormat, AMEDIAFORMAT_KEY_OPERATING_RATE,
                                                     INT32_MAX);
                               AMediaFormat_setInt32(dstFormat, AMEDIAFORMAT_KEY_PRIORITY, 1);
                               *dstFormatOut = dstFormat;
                               return true;
                           }
                           return false;
                       });
}

static void BM_TranscodeAudioVideoPassthrough(benchmark::State& state) {
@@ -215,6 +274,8 @@ static void BM_TranscodeVideoPassthrough(benchmark::State& state) {
TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAudioVideo2AudioVideo);
TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAudioVideo2Video);
TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcVideo2Video);
TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AVMaxOperatingRate);
TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcV2VMaxOperatingRate);
TRANSCODER_BENCHMARK(BM_TranscodeAudioVideoPassthrough);
TRANSCODER_BENCHMARK(BM_TranscodeVideoPassthrough);

+25 −0
Original line number Diff line number Diff line
@@ -54,4 +54,29 @@ static constexpr int TBD_AMEDIACODEC_BUFFER_FLAG_KEY_FRAME = 0x1;

static constexpr int kBitrateModeConstant = 2;

namespace AMediaFormatUtils {

typedef struct {
    const char* key;
    bool (*copy)(const char* key, AMediaFormat* from, AMediaFormat* to);
    bool (*copy2)(const char* key, AMediaFormat* from, AMediaFormat* to);
} EntryCopier;

#define ENTRY_COPIER(keyName, typeName) \
    { keyName, AMediaFormatUtils::CopyFormatEntry##typeName, nullptr }
#define ENTRY_COPIER2(keyName, typeName, typeName2)            \
    {                                                          \
        keyName, AMediaFormatUtils::CopyFormatEntry##typeName, \
                AMediaFormatUtils::CopyFormatEntry##typeName2  \
    }

bool CopyFormatEntryString(const char* key, AMediaFormat* from, AMediaFormat* to);
bool CopyFormatEntryInt64(const char* key, AMediaFormat* from, AMediaFormat* to);
bool CopyFormatEntryInt32(const char* key, AMediaFormat* from, AMediaFormat* to);
bool CopyFormatEntryFloat(const char* key, AMediaFormat* from, AMediaFormat* to);

void CopyFormatEntries(AMediaFormat* from, AMediaFormat* to, const EntryCopier* entries,
                       size_t entryCount);

}  // namespace AMediaFormatUtils
#endif  // ANDROID_MEDIA_TRANSCODING_NDK_COMMON_H