Loading media/libmediaformatshaper/CodecProperties.cpp +123 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,10 @@ #include <media/formatshaper/CodecProperties.h> // we aren't going to mess with shaping points dimensions beyond this static const int32_t DIMENSION_LIMIT = 16384; namespace android { namespace mediaformatshaper { Loading Loading @@ -113,7 +117,13 @@ void CodecProperties::setTuningValue(std::string key, std::string value) { setBpp(bpp); legal = true; } } else if (!strncmp(key.c_str(), "vq-target-bpp-", strlen("vq-target-bpp-"))) { std::string resolution = key.substr(strlen("vq-target-bpp-")); if (bppPoint(resolution, value)) { legal = true; } } else if (!strcmp(key.c_str(), "vq-target-bppx100")) { // legacy, prototyping const char *p = value.c_str(); char *q; int32_t iValue = strtol(p, &q, 0); Loading Loading @@ -143,6 +153,119 @@ bool CodecProperties::getTuningValue(std::string key, std::string &value) { return false; } bool CodecProperties::bppPoint(std::string resolution, std::string value) { int32_t width = 0; int32_t height = 0; double bpp = -1; // resolution is "WxH", "W*H" or a standard name like "720p" if (resolution == "1080p") { width = 1080; height = 1920; } else if (resolution == "720p") { width = 720; height = 1280; } else if (resolution == "540p") { width = 540; height = 960; } else if (resolution == "480p") { width = 480; height = 854; } else { size_t sep = resolution.find('x'); if (sep == std::string::npos) { sep = resolution.find('*'); } if (sep == std::string::npos) { ALOGW("unable to parse resolution: '%s'", resolution.c_str()); return false; } std::string w = resolution.substr(0, sep); std::string h = resolution.substr(sep+1); char *q; const char *p = w.c_str(); width = strtol(p, &q, 0); if (q == p) { width = -1; } p = h.c_str(); height = strtol(p, &q, 0); if (q == p) { height = -1; } if (width <= 0 || height <= 0 || width > DIMENSION_LIMIT || height > DIMENSION_LIMIT) { ALOGW("unparseable: width, height '%s'", resolution.c_str()); return false; } } const char *p = value.c_str(); char *q; bpp = strtod(p, &q); if (q == p) { ALOGW("unparseable bpp '%s'", value.c_str()); return false; } struct bpp_point *point = (struct bpp_point*) malloc(sizeof(*point)); if (point == nullptr) { ALOGW("unable to allocate memory for bpp point"); return false; } point->pixels = width * height; point->width = width; point->height = height; point->bpp = bpp; if (mBppPoints == nullptr) { point->next = nullptr; mBppPoints = point; } else if (point->pixels < mBppPoints->pixels) { // at the front point->next = mBppPoints; mBppPoints = point; } else { struct bpp_point *after = mBppPoints; while (after->next) { if (point->pixels > after->next->pixels) { after = after->next; continue; } // insert before after->next point->next = after->next; after->next = point; break; } if (after->next == nullptr) { // hasn't gone in yet point->next = nullptr; after->next = point; } } return true; } double CodecProperties::getBpp(int32_t width, int32_t height) { // look in the per-resolution list int32_t pixels = width * height; if (mBppPoints) { struct bpp_point *point = mBppPoints; while (point && point->pixels < pixels) { point = point->next; } if (point) { ALOGV("getBpp(w=%d,h=%d) returns %f from bpppoint w=%d h=%d", width, height, point->bpp, point->width, point->height); return point->bpp; } } ALOGV("defaulting to %f bpp", mBpp); return mBpp; } std::string CodecProperties::getMapping(std::string key, std::string kind) { ALOGV("getMapping(key %s, kind %s )", key.c_str(), kind.c_str()); Loading media/libmediaformatshaper/CodecSeeding.cpp +5 −2 Original line number Diff line number Diff line Loading @@ -50,13 +50,16 @@ typedef struct { static preloadTuning_t featuresAvc[] = { {true, "vq-target-bpp", "2.45"}, {true, "vq-target-qpmax", "41"}, {true, "vq-target-bpp-1080p", "2.40"}, {true, "vq-target-bpp-540p", "2.60"}, {true, "vq-target-bpp-480p", "3.00"}, {true, "vq-target-qpmax", "40"}, {true, nullptr, 0} }; static preloadTuning_t featuresHevc[] = { {true, "vq-target-bpp", "2.30"}, {true, "vq-target-qpmax", "42"}, // nop, since hevc codecs don't declare qp support {true, "vq-target-qpmax", "40"}, // nop, since hevc codecs don't declare qp support {true, nullptr, 0} }; Loading media/libmediaformatshaper/VQApply.cpp +72 −50 Original line number Diff line number Diff line Loading @@ -48,6 +48,15 @@ namespace mediaformatshaper { // static const int BITRATE_MODE_VBR = 1; // constants we use within the calculations // constexpr double BITRATE_LEAVE_UNTOUCHED = 2.0; constexpr double BITRATE_QP_UNAVAILABLE = 1.20; // 10% didn't work so hot on bonito (with no QP support) // 15% is next.. still leaves a few short // 20% ? this is on the edge of what I want do do // // Caller retains ownership of and responsibility for inFormat // Loading @@ -69,67 +78,80 @@ int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int f } // // apply any and all tools that we have. // consider any and all tools available // -- qp // -- minimum bits-per-pixel // if (!codec->supportsQp()) { ALOGD("minquality: no qp bounding in codec %s", codec->getName().c_str()); } else { // use a (configurable) QP value to force better quality // int32_t qpmax = codec->targetQpMax(); int32_t qpmaxUser = INT32_MAX; if (hasQp(inFormat)) { (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &qpmaxUser); ALOGD("minquality by QP: format already sets QP"); } // if the system didn't do one, use what the user provided if (qpmax == 0 && qpmaxUser != INT32_MAX) { qpmax = qpmaxUser; } // XXX: if both said something, how do we want to reconcile that int64_t bitrateChosen = 0; int32_t qpChosen = INT32_MAX; if (qpmax > 0) { ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpmax); AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpmax); int64_t bitrateConfigured = 0; int32_t bitrateConfiguredTmp = 0; (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfiguredTmp); bitrateConfigured = bitrateConfiguredTmp; bitrateChosen = bitrateConfigured; // force spreading the QP across frame types, since we imposing a value qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true); } } double bpp = codec->getBpp(); if (bpp > 0.0) { // if we've decided to use bits-per-pixel (per second) to drive the quality // // (properly phrased as 'bits per second per pixel' so that it's resolution // and framerate agnostic // // all of these is structured so that a missing value cleanly gets us to a // non-faulting value of '0' for the minimum bits-per-pixel. // int32_t width = 0; (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width); int32_t height = 0; (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height); int32_t bitrateConfigured = 0; (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfigured); int64_t pixels = ((int64_t)width) * height; int64_t bitrateFloor = pixels * bpp; double minimumBpp = codec->getBpp(width, height); int64_t bitrateFloor = pixels * minimumBpp; if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX; ALOGD("minquality/bitrate: target %d floor %" PRId64 "(%.3f bpp * (%d w * %d h)", bitrateConfigured, bitrateFloor, codec->getBpp(), height, width); // if we are far enough above the target bpp, leave it alone // ALOGV("bitrate: configured %" PRId64 " floor %" PRId64, bitrateConfigured, bitrateFloor); if (bitrateConfigured >= BITRATE_LEAVE_UNTOUCHED * bitrateFloor) { ALOGV("high enough bitrate: configured %" PRId64 " >= %f * floor %" PRId64, bitrateConfigured, BITRATE_LEAVE_UNTOUCHED, bitrateFloor); return 0; } // raise anything below the bitrate floor if (bitrateConfigured < bitrateFloor) { ALOGD("minquality/target bitrate raised from %d to %" PRId64 " bps", ALOGD("raise bitrate: configured %" PRId64 " to floor %" PRId64, bitrateConfigured, bitrateFloor); AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateFloor); bitrateChosen = bitrateFloor; } bool qpPresent = hasQp(inFormat); // add QP, if not already present if (!qpPresent) { int32_t qpmax = codec->targetQpMax(); if (qpmax != INT32_MAX) { ALOGV("choosing qp=%d", qpmax); qpChosen = qpmax; } } // if QP is desired but not supported, compensate with additional bits if (!codec->supportsQp()) { if (qpPresent || qpChosen != INT32_MAX) { ALOGD("minquality: desired QP, but unsupported, boost bitrate %" PRId64 " to %" PRId64, bitrateChosen, (int64_t)(bitrateChosen * BITRATE_QP_UNAVAILABLE)); bitrateChosen = bitrateChosen * BITRATE_QP_UNAVAILABLE; qpChosen = INT32_MAX; } } // apply our chosen values // if (qpChosen != INT32_MAX) { ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen); AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen); // force spreading the QP across frame types, since we are imposing a value qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true); } if (bitrateChosen != bitrateConfigured) { ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps", bitrateConfigured, bitrateChosen); AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen); } return 0; Loading media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h +16 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ #include <mutex> #include <string> #include <inttypes.h> #include <utils/RefBase.h> namespace android { Loading Loading @@ -73,7 +75,7 @@ class CodecProperties { // This is used to calculate a minimum bitrate for any particular resolution. // A 1080p (1920*1080 = 2073600 pixels) to be encoded at 5Mbps has a bpp == 2.41 void setBpp(double bpp) { mBpp = bpp;} double getBpp() {return mBpp;} double getBpp(int32_t width, int32_t height); // Does this codec support QP bounding // The getMapping() methods provide any needed mapping to non-standard keys. Loading @@ -92,10 +94,22 @@ class CodecProperties { std::string mMediaType; int mApi = 0; int mMinimumQuality = 0; int mTargetQpMax = 0; int mTargetQpMax = INT32_MAX; bool mSupportsQp = false; double mBpp = 0.0; // allow different target bits-per-pixel based on resolution // similar to codec 'performance points' // uses 'next largest' (by pixel count) point as minimum bpp struct bpp_point { struct bpp_point *next; int32_t pixels; int32_t width, height; double bpp; }; struct bpp_point *mBppPoints = nullptr; bool bppPoint(std::string resolution, std::string value); std::mutex mMappingLock; // XXX figure out why I'm having problems getting compiler to like GUARDED_BY std::map<std::string, std::string> mMappings /*GUARDED_BY(mMappingLock)*/ ; Loading Loading
media/libmediaformatshaper/CodecProperties.cpp +123 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,10 @@ #include <media/formatshaper/CodecProperties.h> // we aren't going to mess with shaping points dimensions beyond this static const int32_t DIMENSION_LIMIT = 16384; namespace android { namespace mediaformatshaper { Loading Loading @@ -113,7 +117,13 @@ void CodecProperties::setTuningValue(std::string key, std::string value) { setBpp(bpp); legal = true; } } else if (!strncmp(key.c_str(), "vq-target-bpp-", strlen("vq-target-bpp-"))) { std::string resolution = key.substr(strlen("vq-target-bpp-")); if (bppPoint(resolution, value)) { legal = true; } } else if (!strcmp(key.c_str(), "vq-target-bppx100")) { // legacy, prototyping const char *p = value.c_str(); char *q; int32_t iValue = strtol(p, &q, 0); Loading Loading @@ -143,6 +153,119 @@ bool CodecProperties::getTuningValue(std::string key, std::string &value) { return false; } bool CodecProperties::bppPoint(std::string resolution, std::string value) { int32_t width = 0; int32_t height = 0; double bpp = -1; // resolution is "WxH", "W*H" or a standard name like "720p" if (resolution == "1080p") { width = 1080; height = 1920; } else if (resolution == "720p") { width = 720; height = 1280; } else if (resolution == "540p") { width = 540; height = 960; } else if (resolution == "480p") { width = 480; height = 854; } else { size_t sep = resolution.find('x'); if (sep == std::string::npos) { sep = resolution.find('*'); } if (sep == std::string::npos) { ALOGW("unable to parse resolution: '%s'", resolution.c_str()); return false; } std::string w = resolution.substr(0, sep); std::string h = resolution.substr(sep+1); char *q; const char *p = w.c_str(); width = strtol(p, &q, 0); if (q == p) { width = -1; } p = h.c_str(); height = strtol(p, &q, 0); if (q == p) { height = -1; } if (width <= 0 || height <= 0 || width > DIMENSION_LIMIT || height > DIMENSION_LIMIT) { ALOGW("unparseable: width, height '%s'", resolution.c_str()); return false; } } const char *p = value.c_str(); char *q; bpp = strtod(p, &q); if (q == p) { ALOGW("unparseable bpp '%s'", value.c_str()); return false; } struct bpp_point *point = (struct bpp_point*) malloc(sizeof(*point)); if (point == nullptr) { ALOGW("unable to allocate memory for bpp point"); return false; } point->pixels = width * height; point->width = width; point->height = height; point->bpp = bpp; if (mBppPoints == nullptr) { point->next = nullptr; mBppPoints = point; } else if (point->pixels < mBppPoints->pixels) { // at the front point->next = mBppPoints; mBppPoints = point; } else { struct bpp_point *after = mBppPoints; while (after->next) { if (point->pixels > after->next->pixels) { after = after->next; continue; } // insert before after->next point->next = after->next; after->next = point; break; } if (after->next == nullptr) { // hasn't gone in yet point->next = nullptr; after->next = point; } } return true; } double CodecProperties::getBpp(int32_t width, int32_t height) { // look in the per-resolution list int32_t pixels = width * height; if (mBppPoints) { struct bpp_point *point = mBppPoints; while (point && point->pixels < pixels) { point = point->next; } if (point) { ALOGV("getBpp(w=%d,h=%d) returns %f from bpppoint w=%d h=%d", width, height, point->bpp, point->width, point->height); return point->bpp; } } ALOGV("defaulting to %f bpp", mBpp); return mBpp; } std::string CodecProperties::getMapping(std::string key, std::string kind) { ALOGV("getMapping(key %s, kind %s )", key.c_str(), kind.c_str()); Loading
media/libmediaformatshaper/CodecSeeding.cpp +5 −2 Original line number Diff line number Diff line Loading @@ -50,13 +50,16 @@ typedef struct { static preloadTuning_t featuresAvc[] = { {true, "vq-target-bpp", "2.45"}, {true, "vq-target-qpmax", "41"}, {true, "vq-target-bpp-1080p", "2.40"}, {true, "vq-target-bpp-540p", "2.60"}, {true, "vq-target-bpp-480p", "3.00"}, {true, "vq-target-qpmax", "40"}, {true, nullptr, 0} }; static preloadTuning_t featuresHevc[] = { {true, "vq-target-bpp", "2.30"}, {true, "vq-target-qpmax", "42"}, // nop, since hevc codecs don't declare qp support {true, "vq-target-qpmax", "40"}, // nop, since hevc codecs don't declare qp support {true, nullptr, 0} }; Loading
media/libmediaformatshaper/VQApply.cpp +72 −50 Original line number Diff line number Diff line Loading @@ -48,6 +48,15 @@ namespace mediaformatshaper { // static const int BITRATE_MODE_VBR = 1; // constants we use within the calculations // constexpr double BITRATE_LEAVE_UNTOUCHED = 2.0; constexpr double BITRATE_QP_UNAVAILABLE = 1.20; // 10% didn't work so hot on bonito (with no QP support) // 15% is next.. still leaves a few short // 20% ? this is on the edge of what I want do do // // Caller retains ownership of and responsibility for inFormat // Loading @@ -69,67 +78,80 @@ int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int f } // // apply any and all tools that we have. // consider any and all tools available // -- qp // -- minimum bits-per-pixel // if (!codec->supportsQp()) { ALOGD("minquality: no qp bounding in codec %s", codec->getName().c_str()); } else { // use a (configurable) QP value to force better quality // int32_t qpmax = codec->targetQpMax(); int32_t qpmaxUser = INT32_MAX; if (hasQp(inFormat)) { (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &qpmaxUser); ALOGD("minquality by QP: format already sets QP"); } // if the system didn't do one, use what the user provided if (qpmax == 0 && qpmaxUser != INT32_MAX) { qpmax = qpmaxUser; } // XXX: if both said something, how do we want to reconcile that int64_t bitrateChosen = 0; int32_t qpChosen = INT32_MAX; if (qpmax > 0) { ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpmax); AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpmax); int64_t bitrateConfigured = 0; int32_t bitrateConfiguredTmp = 0; (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfiguredTmp); bitrateConfigured = bitrateConfiguredTmp; bitrateChosen = bitrateConfigured; // force spreading the QP across frame types, since we imposing a value qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true); } } double bpp = codec->getBpp(); if (bpp > 0.0) { // if we've decided to use bits-per-pixel (per second) to drive the quality // // (properly phrased as 'bits per second per pixel' so that it's resolution // and framerate agnostic // // all of these is structured so that a missing value cleanly gets us to a // non-faulting value of '0' for the minimum bits-per-pixel. // int32_t width = 0; (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width); int32_t height = 0; (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height); int32_t bitrateConfigured = 0; (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfigured); int64_t pixels = ((int64_t)width) * height; int64_t bitrateFloor = pixels * bpp; double minimumBpp = codec->getBpp(width, height); int64_t bitrateFloor = pixels * minimumBpp; if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX; ALOGD("minquality/bitrate: target %d floor %" PRId64 "(%.3f bpp * (%d w * %d h)", bitrateConfigured, bitrateFloor, codec->getBpp(), height, width); // if we are far enough above the target bpp, leave it alone // ALOGV("bitrate: configured %" PRId64 " floor %" PRId64, bitrateConfigured, bitrateFloor); if (bitrateConfigured >= BITRATE_LEAVE_UNTOUCHED * bitrateFloor) { ALOGV("high enough bitrate: configured %" PRId64 " >= %f * floor %" PRId64, bitrateConfigured, BITRATE_LEAVE_UNTOUCHED, bitrateFloor); return 0; } // raise anything below the bitrate floor if (bitrateConfigured < bitrateFloor) { ALOGD("minquality/target bitrate raised from %d to %" PRId64 " bps", ALOGD("raise bitrate: configured %" PRId64 " to floor %" PRId64, bitrateConfigured, bitrateFloor); AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateFloor); bitrateChosen = bitrateFloor; } bool qpPresent = hasQp(inFormat); // add QP, if not already present if (!qpPresent) { int32_t qpmax = codec->targetQpMax(); if (qpmax != INT32_MAX) { ALOGV("choosing qp=%d", qpmax); qpChosen = qpmax; } } // if QP is desired but not supported, compensate with additional bits if (!codec->supportsQp()) { if (qpPresent || qpChosen != INT32_MAX) { ALOGD("minquality: desired QP, but unsupported, boost bitrate %" PRId64 " to %" PRId64, bitrateChosen, (int64_t)(bitrateChosen * BITRATE_QP_UNAVAILABLE)); bitrateChosen = bitrateChosen * BITRATE_QP_UNAVAILABLE; qpChosen = INT32_MAX; } } // apply our chosen values // if (qpChosen != INT32_MAX) { ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen); AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen); // force spreading the QP across frame types, since we are imposing a value qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true); } if (bitrateChosen != bitrateConfigured) { ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps", bitrateConfigured, bitrateChosen); AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen); } return 0; Loading
media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h +16 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ #include <mutex> #include <string> #include <inttypes.h> #include <utils/RefBase.h> namespace android { Loading Loading @@ -73,7 +75,7 @@ class CodecProperties { // This is used to calculate a minimum bitrate for any particular resolution. // A 1080p (1920*1080 = 2073600 pixels) to be encoded at 5Mbps has a bpp == 2.41 void setBpp(double bpp) { mBpp = bpp;} double getBpp() {return mBpp;} double getBpp(int32_t width, int32_t height); // Does this codec support QP bounding // The getMapping() methods provide any needed mapping to non-standard keys. Loading @@ -92,10 +94,22 @@ class CodecProperties { std::string mMediaType; int mApi = 0; int mMinimumQuality = 0; int mTargetQpMax = 0; int mTargetQpMax = INT32_MAX; bool mSupportsQp = false; double mBpp = 0.0; // allow different target bits-per-pixel based on resolution // similar to codec 'performance points' // uses 'next largest' (by pixel count) point as minimum bpp struct bpp_point { struct bpp_point *next; int32_t pixels; int32_t width, height; double bpp; }; struct bpp_point *mBppPoints = nullptr; bool bppPoint(std::string resolution, std::string value); std::mutex mMappingLock; // XXX figure out why I'm having problems getting compiler to like GUARDED_BY std::map<std::string, std::string> mMappings /*GUARDED_BY(mMappingLock)*/ ; Loading