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

Commit 1831f7bd authored by Ray Essick's avatar Ray Essick
Browse files

Initial libmediaformatshaper

mediaformatshaper library to amend MediaFormat parameters before
encoding. This is the initial cut of the library.
Disabled by default, enable with "setprop debug.stagefright.enableshaping 1"

Bug: 182827840
Test: build, boot, encode video
Change-Id: I8cefb6ed6ad286ae2192796bf15760e873e0d2f3
parent 8c4e9c7e
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -51,7 +51,10 @@ apex_defaults {
        },
    },
    // JNI
    native_shared_libs: ["libmediaparser-jni"],
    native_shared_libs: [
        "libmediaparser-jni",
        "libmediaformatshaper",
    ],
    compile_multilib: "both",
    prebuilts: [
        "code_coverage.policy",
+4 −1
Original line number Diff line number Diff line
@@ -432,5 +432,8 @@ cc_library_static {
        },
    },

    apex_available: ["com.android.media"],
    apex_available: [
        "//apex_available:platform",
        "com.android.media"
    ],
}
+91 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */


// these headers include the structure of needed function pointers
cc_library_headers {
    name: "libmediaformatshaper_headers",
    export_include_dirs: ["include"],
    apex_available: [
        "//apex_available:platform",
        "com.android.media",
    ],
    min_sdk_version: "29",
    host_supported: true,
    target: {
        darwin: {
            enabled: false,
        },
    },
}

cc_defaults {
    name: "libmediaformatshaper_defaults",
    srcs: [
        "CodecProperties.cpp",
        "FormatShaper.cpp",
        "ManageShapingCodecs.cpp",
        "VideoShaper.cpp",
        "VQApply.cpp",
    ],

    local_include_dirs: [
        "include",
    ],

    shared_libs: [
        "liblog",
        "libutils",
    ],

    cflags: [
        "-Werror",
        "-Wall",
        "-fvisibility=hidden",
        "-Wthread-safety",                      // enables GUARDED_BY()
    ],

    target: {
        android: {
            shared_libs: [
                "libmediandk#29",
            ],
        },
    },

    sanitize: {
        cfi: true,
        misc_undefined: [
            "unsigned-integer-overflow",
            "signed-integer-overflow",
        ],
    },
}

cc_library {
    name: "libmediaformatshaper",
    defaults: ["libmediaformatshaper_defaults"],

    min_sdk_version: "29",

    apex_available: [
        "//apex_available:platform",
        "com.android.media",
    ],

    version_script: "exports.lds",

}
+143 −0
Original line number Diff line number Diff line
/*
 * Copyright 2021, 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 "CodecProperties"
#include <utils/Log.h>

#include <string>

#include <media/formatshaper/CodecProperties.h>

namespace android {
namespace mediaformatshaper {

CodecProperties::CodecProperties(std::string name, std::string mediaType) {
    mName = name;
    mMediaType = mediaType;
}

std::string CodecProperties::getName(){
    return mName;
}

std::string CodecProperties::getMediaType(){
    return mMediaType;
}

int CodecProperties::supportedMinimumQuality() {
    return mMinimumQuality;
}
void CodecProperties::setSupportedMinimumQuality(int vmaf) {
    mMinimumQuality = vmaf;
}

int CodecProperties::targetQpMax() {
    return mTargetQpMax;
}
void CodecProperties::setTargetQpMax(int qpMax) {
    mTargetQpMax = qpMax;
}

// what API is this codec set up for (e.g. API of the associated partition)
// vendor-side (OEM) codecs may be older, due to 'vendor freeze' and treble
int CodecProperties::supportedApi() {
    return mApi;
}

std::string CodecProperties::getMapping(std::string key, std::string kind) {
    ALOGV("getMapping(key %s, kind %s )", key.c_str(), kind.c_str());
    //play with mMappings
    auto mapped = mMappings.find(kind + "-" + key);
    if (mapped != mMappings.end()) {
        std::string result = mapped->second;
        ALOGV("getMapping(%s, %s) -> %s", key.c_str(), kind.c_str(), result.c_str());
        return result;
    }
    ALOGV("nope, return unchanged key");
    return key;
}


// really a bit of debugging code here.
void CodecProperties::showMappings() {
    ALOGD("Mappings:");
    int count = 0;
    for (const auto& [key, value] : mMappings) {
         count++;
         ALOGD("'%s' -> '%s'", key.c_str(), value.c_str());
    }
    ALOGD("total %d mappings", count);
}

void CodecProperties::setMapping(std::string kind, std::string key, std::string value) {
    ALOGV("setMapping(%s,%s,%s)", kind.c_str(), key.c_str(), value.c_str());
    std::string metaKey = kind + "-" + key;
    mMappings.insert({metaKey, value});
}

const char **CodecProperties::getMappings(std::string kind, bool reverse) {
    ALOGV("getMappings(kind %s, reverse %d", kind.c_str(), reverse);
    // how many do we need?
    int count = mMappings.size();
    if (count == 0) {
        ALOGV("empty mappings");
        return nullptr;
    }
    size_t size = sizeof(char *) * (2 * count + 2);
    const char **result = (const char **)malloc(size);
    if (result == nullptr) {
        ALOGW("no memory to return mappings");
        return nullptr;
    }
    memset(result, '\0', size);

    const char **pp = result;
    for (const auto& [key, value] : mMappings) {
        // split out the kind/key
        size_t pos = key.find('-');
        if (pos == std::string::npos) {
            ALOGD("ignoring malformed key: %s", key.c_str());
            continue;
        }
        std::string actualKind = key.substr(0,pos);
        if (kind.length() != 0 && kind != actualKind) {
            ALOGD("kinds don't match: want '%s' got '%s'", kind.c_str(), actualKind.c_str());
            continue;
        }
        if (reverse) {
            // codec specific -> std aka 'unmapping'
            pp[0] = strdup( value.c_str());
            pp[1] = strdup( key.substr(pos+1).c_str());
        } else {
            // std -> codec specific
            pp[0] = strdup( key.substr(pos+1).c_str());
            pp[1] = strdup( value.c_str());
        }
        ALOGV(" %s -> %s", pp[0], pp[1]);
        pp += 2;
    }

    pp[0] = nullptr;
    pp[1] = nullptr;

    return result;
}


} // namespace mediaformatshaper
} // namespace android
+184 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 "FormatShaper"
#include <utils/Log.h>

#include <string>
#include <inttypes.h>

#include <media/NdkMediaFormat.h>

#include <media/formatshaper/VQops.h>
#include <media/formatshaper/CodecProperties.h>
#include <media/formatshaper/FormatShaper.h>
#include <media/formatshaper/VideoShaper.h>

namespace android {
namespace mediaformatshaper {

//
// Caller retains ownership of and responsibility for inFormat
//

//
// the interface to the outside
//

int shapeFormat(shaperHandle_t shaper, AMediaFormat* inFormat, int flags) {
    CodecProperties *codec = (CodecProperties*) shaper;
    if (codec == nullptr) {
        return -1;
    }
    if (!codec->isRegistered()) {
        return -1;
    }

    // run through the list of possible transformations
    //

    std::string mediaType = codec->getMediaType();
    if (strncmp(mediaType.c_str(), "video/", 6) == 0) {
        // video specific shaping
        (void) videoShaper(codec, inFormat, flags);

    } else if (strncmp(mediaType.c_str(), "audio/", 6) == 0) {
        // audio specific shaping

    } else {
        ALOGV("unknown mediatype '%s', left untouched", mediaType.c_str());

    }

    return 0;
}

int setMap(shaperHandle_t shaper,  const char *kind, const char *key, const char *value) {
    ALOGV("setMap: kind %s key %s -> value %s", kind, key, value);
    CodecProperties *codec = (CodecProperties*) shaper;
    if (codec == nullptr) {
        return -1;
    }
    // must not yet be registered
    if (codec->isRegistered()) {
        return -1;
    }

    codec->setMapping(kind, key, value);
    return 0;
}

int setFeature(shaperHandle_t shaper, const char *feature, int value) {
    ALOGV("set_feature: feature %s value %d", feature, value);
    CodecProperties *codec = (CodecProperties*) shaper;
    if (codec == nullptr) {
        return -1;
    }
    // must not yet be registered
    if (codec->isRegistered()) {
        return -1;
    }

    if (!strcmp(feature, "vq-minimum-quality")) {
        codec->setSupportedMinimumQuality(value);
    } else if (!strcmp(feature, "vq-supports-qp")) {
        codec->setSupportsQp(value != 0);
    } else if (!strcmp(feature, "vq-target-qpmax")) {
        codec->setTargetQpMax(value);
    } else if (!strcmp(feature, "vq-target-bppx100")) {
        double bpp = value / 100.0;
        codec->setBpp(bpp);
    } else {
        // changed nothing, don't mark as configured
        return 0;
    }
    return 0;
}

/*
 * The routines that manage finding, creating, and registering the shapers.
 */

shaperHandle_t findShaper(const char *codecName, const char *mediaType) {
    CodecProperties *codec = findCodec(codecName, mediaType);
    return (shaperHandle_t) codec;
}

shaperHandle_t createShaper(const char *codecName, const char *mediaType) {
    CodecProperties *codec = new CodecProperties(codecName, mediaType);
    return (shaperHandle_t) codec;
}

shaperHandle_t registerShaper(shaperHandle_t shaper, const char *codecName, const char *mediaType) {
    ALOGV("registerShaper(handle, codecName %s, mediaType %s", codecName, mediaType);
    CodecProperties *codec = (CodecProperties*) shaper;
    if (codec == nullptr) {
        return nullptr;
    }
    // must not yet be registered
    if (codec->isRegistered()) {
        return nullptr;
    }

    codec = registerCodec(codec, codecName, mediaType);
    return (shaperHandle_t) codec;
}

// mapping & unmapping
// give me the mappings for 'kind'.
// kind==null (or empty string), means *all* mappings

const char **getMappings(shaperHandle_t shaper, const char *kind) {
    CodecProperties *codec = (CodecProperties*) shaper;
    if (codec == nullptr)
        return nullptr;
    if (kind == nullptr)
        kind = "";

    return codec->getMappings(kind, /* reverse */ false);
}

const char **getReverseMappings(shaperHandle_t shaper, const char *kind) {
    CodecProperties *codec = (CodecProperties*) shaper;
    if (codec == nullptr)
        return nullptr;
    if (kind == nullptr)
        kind = "";

    return codec->getMappings(kind, /* reverse */ true);
}


// the system grabs this structure
__attribute__ ((visibility ("default")))
extern "C" FormatShaperOps_t shaper_ops = {
    .version = SHAPER_VERSION_V1,

    .findShaper = findShaper,
    .createShaper = createShaper,
    .setMap = setMap,
    .setFeature = setFeature,
    .registerShaper = registerShaper,

    .shapeFormat = shapeFormat,
    .getMappings = getMappings,
    .getReverseMappings = getReverseMappings,
};

}  // namespace mediaformatshaper
}  // namespace android
Loading