当前位置: 代码迷 >> 综合 >> 【MediaCodec】配置文档查看(一)
  详细解决方案

【MediaCodec】配置文档查看(一)

热度:88   发布时间:2023-12-05 20:48:24.0

1 查看media codec配置文件

1.1 华为p8青春版

system/etc目录下,查看m开头文件
在这里插入图片描述

1.2 当前项目

在这里插入图片描述
system目录下只有media_profiles.xml
但是在vendor目录下就可以看到
在这里插入图片描述
目前看下来有两种配置文件:
media_codecs.xml和media_profiles.xml
两个分别对应MediaCodec和MediaProfiles

2 配置文件查看

2.1 mediacodec

2.1.1 audio

文件路径:system/etc/media_codecs_google_audio.xml

常见参数:
Decoders(编码器)和Encoders(解码器):包含mediacodec信息
type:编解码类型,注意后续代码中获取该参数
Limit标签:

  1. channel-count:通道数
  2. sample-rate:采样率
  3. bitrate:码率
  4. complexity:复杂度
  5. bitrate-modes:码率控制方法1 2
    VBR(Variable Bit Rate)动态比特率
    CBR(Constants Bits Rate)固定码率
    CQ 不控制码率
<Included><Decoders><MediaCodec name="OMX.google.mp3.decoder" type="audio/mpeg"><Limit name="channel-count" max="2" /><Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" /><Limit name="bitrate" range="8000-320000" /><!-- 省略部分 --><MediaCodec name="OMX.google.aac.decoder" type="audio/mp4a-latm"><Limit name="channel-count" max="8" /><Limit name="sample-rate" ranges="7350,8000,11025,12000,16000,22050,24000,32000,44100,48000" /><Limit name="bitrate" range="8000-960000" /></MediaCodec></Decoders><Encoders><MediaCodec name="OMX.google.aac.encoder" type="audio/mp4a-latm"><Limit name="channel-count" max="6" /><Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" /><!-- also may support 64000, 88200 and 96000 Hz --><Limit name="bitrate" range="8000-960000" /></MediaCodec><!-- 省略部分 --><MediaCodec name="OMX.google.flac.encoder" type="audio/flac"><Limit name="channel-count" max="2" /><Limit name="sample-rate" ranges="1-655350" /><Limit name="bitrate" range="1-21000000" /><Limit name="complexity" range="0-8" default="5" /><Feature name="bitrate-modes" value="CQ" /></MediaCodec></Encoders>
</Included>

2.1.2 video

文件路径:system/etc/media_codecs_google_video.xml

常见参数:

  1. size:分辨率
  2. alignment
  3. block-size
  4. blocks-per-second
  5. adaptive-playback(feature标签)
<Included><Decoders><MediaCodec name="OMX.google.mpeg2.decoder" type="video/mpeg2"><!-- profiles and levels: ProfileMain : LevelHL --><Limit name="size" min="16x16" max="1920x1088" /><Limit name="alignment" value="2x2" /><Limit name="block-size" value="16x16" /><Limit name="blocks-per-second" range="1-244800" /><Limit name="bitrate" range="1-20000000" /><Feature name="adaptive-playback" /></MediaCodec><MediaCodec name="OMX.google.h264.decoder" type="video/avc"><!-- profiles and levels: ProfileHigh : Level41 --><Limit name="size" min="16x16" max="1920x1088" /><Limit name="alignment" value="2x2" /><Limit name="block-size" value="16x16" /><Limit name="blocks-per-second" range="1-244800" /><Limit name="bitrate" range="1-12000000" /><Feature name="adaptive-playback" /></MediaCodec></Decoders><Encoders><MediaCodec name="OMX.google.h263.encoder" type="video/3gpp"><!-- profiles and levels: ProfileBaseline : Level45 --><Limit name="size" min="176x144" max="176x144" /><Limit name="alignment" value="16x16" /><Limit name="bitrate" range="1-128000" /></MediaCodec><MediaCodec name="OMX.google.h264.encoder" type="video/avc"><!-- profiles and levels: ProfileBaseline : Level41 --><Limit name="size" min="16x16" max="1920x1088" /><Limit name="alignment" value="2x2" /><Limit name="block-size" value="16x16" /><Limit name="blocks-per-second" range="1-244800" /><Limit name="bitrate" range="1-12000000" /></MediaCodec></Encoders>

2.2 MediaProfiles

文档路径:/system/etc/media_profiles.xml

<MediaSettings><!-- Back Camera --><CamcorderProfiles cameraId="0"><EncoderProfile quality="high" fileFormat="mp4" duration="30"><Video codec="h264"bitRate="20000000"width="1920"height="1080"frameRate="30" /><Audio codec="aac"bitRate="156000"sampleRate="48000"channels="2" /></EncoderProfile></CamcorderProfiles><EncoderOutputFileFormat name="3gp" /><EncoderOutputFileFormat name="mp4" /><VideoEncoderCap name="hevc" enabled="true"minBitRate="64000" maxBitRate="100000000"minFrameWidth="176" maxFrameWidth="4096"minFrameHeight="144" maxFrameHeight="2160"minFrameRate="15" maxFrameRate="30"maxHFRFrameWidth="0" maxHFRFrameHeight="0"maxHFRMode="0" /><AudioEncoderCap name="aac" enabled="true"minBitRate="8000" maxBitRate="96000"minSampleRate="8000" maxSampleRate="48000"minChannels="1" maxChannels="6" /><VideoDecoderCap name="wmv" enabled="true"/><AudioDecoderCap name="wma" enabled="true"/>
</MediaSettings>

3 xml文件加载流程

3.1 MediaCodec

3.1.1 配置文件路径定义

//MediaCodecsXmlParser.h (frameworks\av\media\libstagefright\xmlparser\include\media\stagefright\xmlparser)
class MediaCodecsXmlParser {
    
public:// Treblized media codec list will be located in /odm/etc or /vendor/etc.static std::vector<std::string> getDefaultSearchDirs() {
    return {
     "/odm/etc", "/vendor/etc", "/etc" };}static std::vector<std::string> getDefaultXmlNames() {
    return {
     "media_codecs.xml", "media_codecs_performance.xml" };}static constexpr char const* defaultProfilingResultsXmlPath ="/data/misc/media/media_codecs_profiling_results.xml";
}

这里定义了默认的文件路径和默认的xml文件名

在IOmxStore 初始化的时候会调用rusahn

struct OmxStore : public IOmxStore {
    OmxStore(const sp<IOmx> &omx = nullptr,const char* owner = "default",const std::vector<std::string> &searchDirs =MediaCodecsXmlParser::getDefaultSearchDirs(),const std::vector<std::string> &xmlFiles =MediaCodecsXmlParser::getDefaultXmlNames(),const char *xmlProfilingResultsPath =MediaCodecsXmlParser::defaultProfilingResultsXmlPath);virtual ~OmxStore();
}

3.1.2 获取xml文件路径

media_codecs.xml文件加载函数

MediaCodecsXmlParser::parseXmlFilesInSearchDirs
|-- MediaCodecsXmlParser::Impl::parseXmlFilesInSearchDirs
|----MediaCodecsXmlParser::parseXmlPath
|------MediaCodecsXmlParser::Impl::parseXmlPath
|--------getVendorXmlPath

  1. getDefaultSearchDirs获取搜索文件夹路径
  2. parseXmlFilesInSearchDirs遍历这些文件夹
  3. parseXmlPath中会再搜索下vendor目录下的特定文件(比如/vendor/etc/media_codecs_performance_sdm66l_v1.xml)
//MediaCodecsXmlParser.cpp (frameworks\av\media\libstagefright\xmlparser)
std::string getVendorXmlPath(const std::string &path) {
    std::string vendorPath;std::string result = path;if (!strncmp(path.c_str(), "/vendor/etc/media_codecs.xml",strlen("/vendor/etc/media_codecs.xml"))) {
    vendorPath = "/vendor/etc/media_codecs_vendor";} else if (!strncmp(path.c_str(), "/vendor/etc/media_codecs_performance.xml",strlen("/vendor/etc/media_codecs_performance.xml"))) {
    vendorPath = "/vendor/etc/media_codecs_performance";}if (!vendorPath.empty()) {
    if (fileExists(vendorPath + std::string(".xml"))) {
    char version[PROP_VALUE_MAX] = {
    0};result = vendorPath + std::string(".xml");
#ifdef __ANDROID_VNDK__property_get("vendor.media.target.version", version, "0");
#elseproperty_get("vendor.sys.media.target.version", version, "0");
#endifif (atoi(version) > 0) {
    std::string versionedXml = vendorPath + std::string("_v") +std::string(version) + std::string(".xml");if(fileExists(versionedXml)) {
    result = versionedXml;}}}ALOGI("getVendorXmlPath (%s)", result.c_str());}return result;
}

3.1.3 解析xml文件

parseXmlFile函数解析xml,xml文件中的标签参数解析依据StartElementHandlerWrapper和EndElementHandlerWrapper
此时将xml文件中的参数存到XML_ParseBuffer中。

//MediaCodecsXmlParser.cpp (frameworks\av\media\libstagefright\xmlparser)
void MediaCodecsXmlParser::Impl::Parser::parseXmlFile() {
    const char *path = mPath.c_str();ALOGD("parsing %s...", path);FILE *file = fopen(path, "r");if (file == nullptr) {
    ALOGD("unable to open media codecs configuration xml file: %s", path);mStatus = NAME_NOT_FOUND;return;}mParser = std::shared_ptr<XML_ParserStruct>(::XML_ParserCreate(nullptr),[](XML_ParserStruct *parser) {
     ::XML_ParserFree(parser); });LOG_FATAL_IF(!mParser, "XML_MediaCodecsXmlParserCreate() failed.");::XML_SetUserData(mParser.get(), this);::XML_SetElementHandler(mParser.get(), StartElementHandlerWrapper, EndElementHandlerWrapper);static constexpr int BUFF_SIZE = 512;// updateStatus(OK);if (mStatus == NO_INIT) {
    mStatus = OK;}while (mStatus == OK) {
    void *buff = ::XML_GetBuffer(mParser.get(), BUFF_SIZE);if (buff == nullptr) {
    ALOGD("failed in call to XML_GetBuffer()");mStatus = UNKNOWN_ERROR;break;}int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);if (bytes_read < 0) {
    ALOGD("failed in call to read");mStatus = ERROR_IO;break;}XML_Status status = ::XML_ParseBuffer(mParser.get(), bytes_read, bytes_read == 0);if (status != XML_STATUS_OK) {
    PLOGD("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(mParser.get())));mStatus = ERROR_MALFORMED;break;}if (bytes_read == 0) {
    break;}}mParser.reset();fclose(file);file = nullptr;
}

再往上调用实际上是存到MediaCodecList中,具体的调用流程

在这里插入图片描述

//
sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
    Mutex::Autolock autoLock(sInitMutex);if (sCodecList == nullptr) {
    MediaCodecList *codecList = new MediaCodecList(GetBuilders());if (codecList->initCheck() == OK) {
    sCodecList = codecList;if (isProfilingNeeded()) {
    ALOGV("Codec profiling needed, will be run in separated thread.");pthread_t profiler;if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {
    ALOGW("Failed to create thread for codec profiling.");}}} else {
    // failure to initialize may be temporary. retry on next call.delete codecList;}}return sCodecList;
}
//根据传入的类型参数找到符合的mediacodec并创建
sp<MediaCodec> MediaCodec::CreateByType(const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,uid_t uid) {
    Vector<AString> matchingCodecs;MediaCodecList::findMatchingCodecs(mime.c_str(),encoder,0,&matchingCodecs);if (err != NULL) {
    *err = NAME_NOT_FOUND;}for (size_t i = 0; i < matchingCodecs.size(); ++i) {
    sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);AString componentName = matchingCodecs[i];status_t ret = codec->init(componentName, true);if (err != NULL) {
    *err = ret;}if (ret == OK) {
    return codec;}ALOGD("Allocating component '%s' failed (%d), try next one.",componentName.c_str(), ret);}return NULL;
}

3.2 mediaprofile

3.2.1 MediaProfiles类定义

在code中直接搜MediaProfiles,查看这个结构体的定义

文件路径:frameworks\av\media\libmedia\include\media\MediaProfiles.h

class MediaProfiles
{
    
public:static constexpr char const * const xmlFiles[] = {
    "odm/etc/media_profiles_V1_0.xml","vendor/etc/media_profiles_V1_0.xml","system/etc/media_profiles.xml"};/*在MediaProfiles中定义了对应标签的结构体,如AudioCodec,VideoCodec,CamcorderProfile,VideoEncoderCap */struct AudioCodec {
    AudioCodec(audio_encoder codec, int bitRate, int sampleRate, int channels): mCodec(codec),mBitRate(bitRate),mSampleRate(sampleRate),mChannels(channels) {
    }AudioCodec(const AudioCodec& copy) {
    mCodec = copy.mCodec;mBitRate = copy.mBitRate;mSampleRate = copy.mSampleRate;mChannels = copy.mChannels;}~AudioCodec() {
    }audio_encoder mCodec;int mBitRate;int mSampleRate;int mChannels;};}

3.2.2 加载xml函数流程

MediaProfiles::getInstance
首先判断media.settings.xml属性是否设置了
· 若已经定义了就再根据”ro.board.platform“,选定对应的xml文件
· 若未定义,就遍历xmlFiles,找到合适文件


static MediaProfiles *sInstance;/*static*/ MediaProfiles*
MediaProfiles::getInstance()
{
    /*判断属性media.settings.xml是否定义了xml文件路径*/if (property_get("media.settings.xml", value, NULL) <= 0) {
    const char* xmlFile = nullptr;for (auto const& f : xmlFiles) {
    if (checkXmlFile(f)) {
    xmlFile = f;break;}}if (xmlFile == nullptr) {
    ALOGW("Could not find a validated xml file. ""Using the default instance instead.");sInstance = createDefaultInstance();} else {
    sInstance = createInstanceFromXmlFile(xmlFile);}}else{
    /*判断属性然后根据platform加载对应*/if (!strncmp(value, "/vendor/etc", strlen("/vendor/etc"))) {
    property_get("ro.board.platform", platform, NULL);if (!strcmp(platform, "msm8953")){
    if (property_get("vendor.media.target.version", value, "0") &&(atoi(value) == 1)){
    strlcpy(value, "/vendor/etc/media_profiles_8953_v1.xml",PROPERTY_VALUE_MAX);} else {
    strlcpy(value, "/vendor/etc/media_profiles_vendor.xml",PROPERTY_VALUE_MAX);}}}/*重点看这里*/sInstance = createInstanceFromXmlFile(value);}CHECK(sInstance != NULL);sInstance->checkAndAddRequiredProfilesIfNecessary();sIsInitialized = true;}return sInstance;
}

MediaProfiles::createInstanceFromXmlFile函数
加载xml文件,存到static MediaProfiles *sInstance中。

/*static*/ MediaProfiles*
MediaProfiles::createInstanceFromXmlFile(const char *xml)
{
    FILE *fp = NULL;CHECK((fp = fopen(xml, "r")));XML_Parser parser = ::XML_ParserCreate(NULL);CHECK(parser != NULL);MediaProfiles *profiles = new MediaProfiles();::XML_SetUserData(parser, profiles);::XML_SetElementHandler(parser, startElementHandler, NULL);/*FIXME:expat is not compiled with -DXML_DTD. We don't have DTD parsing support.if (!::XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS)) {ALOGE("failed to enable DTD support in the xml file");return UNKNOWN_ERROR;}*/const int BUFF_SIZE = 512;for (;;) {
    void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);if (buff == NULL) {
    ALOGE("failed to in call to XML_GetBuffer()");delete profiles;profiles = NULL;goto exit;}int bytes_read = ::fread(buff, 1, BUFF_SIZE, fp);if (bytes_read < 0) {
    ALOGE("failed in call to read");delete profiles;profiles = NULL;goto exit;}CHECK(::XML_ParseBuffer(parser, bytes_read, bytes_read == 0));if (bytes_read == 0) break;  // done parsing the xml file}exit:::XML_ParserFree(parser);::fclose(fp);return profiles;
}

以上是stagefright的处理流程。注意Nuplayer好像有改变

在这里插入图片描述

至此,将xml文件中的配置参数存到了MediaProfiles *sInstance中

3.3.4 加载流程

3.3.4.1 stagefrightRecorder

查看下函数MediaProfiles::getInstance如何被调用的

/*frameworks\av\media\libmediaplayerservice\StagefrightRecorder.cpp*//*调用getInstance加载xml文件*/
status_t StagefrightRecorder::reset() {
    mEncoderProfiles = MediaProfiles::getInstance();
}/*在StagefrightRecorder构造函数中会调用rest*/
StagefrightRecorder::StagefrightRecorder(const String16 &opPackageName): MediaRecorderBase(opPackageName),mWriter(NULL),mOutputFd(-1),mAudioSource((audio_source_t)AUDIO_SOURCE_CNT), // initialize with invalid valuemVideoSource(VIDEO_SOURCE_LIST_END),mStarted(false),mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),mDeviceCallbackEnabled(false),mSelectedMicDirection(MIC_DIRECTION_UNSPECIFIED),mSelectedMicFieldDimension(MIC_FIELD_DIMENSION_NORMAL) {
    ALOGV("Constructor");mAnalyticsDirty = false;reset();
}

创建client

/*frameworks\av\media\libmediaplayerservice\MediaRecorderClient.cpp*/
MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid,const String16& opPackageName)
{
    ALOGV("Client constructor");mPid = pid;mRecorder = AVMediaServiceFactory::get()->createStagefrightRecorder(opPackageName);mMediaPlayerService = service;
}/*MediaPlayerService.cpp (frameworks\av\media\libmediaplayerservice)*/
sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const String16 &opPackageName)
{
    pid_t pid = IPCThreadState::self()->getCallingPid();sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid, opPackageName);wp<MediaRecorderClient> w = recorder;Mutex::Autolock lock(mLock);mMediaRecorderClients.add(w);ALOGV("Create new media recorder client from pid %d", pid);return recorder;
}

在这里插入图片描述

3.3.4.2 NuPlayer(无关)

获取mediacodec实例

sp<IMediaCodecList> MediaCodecList::getInstance() {
    Mutex::Autolock _l(sRemoteInitMutex);if (sRemoteList == nullptr) {
    sp<IBinder> binder =defaultServiceManager()->getService(String16("media.player"));sp<IMediaPlayerService> service =interface_cast<IMediaPlayerService>(binder);if (service.get() != nullptr) {
    sRemoteList = service->getCodecList();if (sRemoteList != nullptr) {
    sBinderDeathObserver = new BinderDeathObserver();binder->linkToDeath(sBinderDeathObserver.get());}}if (sRemoteList == nullptr) {
    // if failed to get remote list, create local listsRemoteList = getLocalInstance();}}return sRemoteList;
}
/*NuPlayer.cpp (frameworks\av\media\libmediaplayerservice\nuplayer)*/status_t NuPlayer::onInstantiateSecureDecoders() {
    status_t err;if (!(mSourceFlags & Source::FLAG_SECURE)) {
    return BAD_TYPE;}if (mRenderer != NULL) {
    ALOGE("renderer should not be set when instantiating secure decoders");return UNKNOWN_ERROR;}// TRICKY: We rely on mRenderer being null, so that decoder does not start requesting// data on instantiation.if (mSurface != NULL) {
    err = instantiateDecoder(false, &mVideoDecoder);if (err != OK) {
    return err;}}if (mAudioSink != NULL) {
    err = instantiateDecoder(true, &mAudioDecoder);if (err != OK) {
    return err;}}return OK;
}/*启动*/
void NuPlayer::onStart(int64_t startPositionUs, MediaPlayerSeekMode mode) {
    ALOGV("onStart: mCrypto: %p (%d)", mCrypto.get(),(mCrypto != NULL ? mCrypto->getStrongCount() : 0));if (!mSourceStarted) {
    mSourceStarted = true;mSource->start();}if (startPositionUs > 0) {
    performSeek(startPositionUs, mode);if (mSource->getFormat(false /* audio */) == NULL) {
    return;}}mOffloadAudio = false;mAudioEOS = false;mVideoEOS = false;mStarted = true;mPaused = false;uint32_t flags = 0;if (mSource->isRealTime()) {
    flags |= Renderer::FLAG_REAL_TIME;}bool hasAudio = (mSource->getFormat(true /* audio */) != NULL);bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);if (!hasAudio && !hasVideo) {
    ALOGE("no metadata for either audio or video source");mSource->stop();mSourceStarted = false;notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_MALFORMED);return;}ALOGV_IF(!hasAudio, "no metadata for audio source");  // video only streamsp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */);audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;if (mAudioSink != NULL) {
    streamType = mAudioSink->getAudioStreamType();}mOffloadAudio =canOffloadStream(audioMeta, hasVideo, mSource->isStreaming(), streamType)&& (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f);// Modular DRM: Disabling audio offload if the source is protectedif (mOffloadAudio && mIsDrmProtected) {
    mOffloadAudio = false;ALOGV("onStart: Disabling mOffloadAudio now that the source is protected.");}if (mOffloadAudio) {
    flags |= Renderer::FLAG_OFFLOAD_AUDIO;}sp<AMessage> notify = new AMessage(kWhatRendererNotify, this);++mRendererGeneration;notify->setInt32("generation", mRendererGeneration);mRenderer = AVNuFactory::get()->createRenderer(mAudioSink, mMediaClock, notify, flags);mRendererLooper = new ALooper;mRendererLooper->setName("NuPlayerRenderer");mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);mRendererLooper->registerHandler(mRenderer);status_t err = mRenderer->setPlaybackSettings(mPlaybackSettings);if (err != OK) {
    mSource->stop();mSourceStarted = false;notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);return;}if (mSurface != NULL) {
    int64_t refreshDuration = 0;native_window_get_refresh_cycle_duration(mSurface.get(), &refreshDuration);if (refreshDuration > 0)mMaxOutputFrameRate = round(1000000000.0f / refreshDuration);}float rate = getFrameRate();if (rate > 0) {
    rate = (rate > mMaxOutputFrameRate) ? mMaxOutputFrameRate : rate;mRenderer->setVideoFrameRate(rate);}if (mVideoDecoder != NULL) {
    mVideoDecoder->setRenderer(mRenderer);}if (mAudioDecoder != NULL) {
    mAudioDecoder->setRenderer(mRenderer);}startPlaybackTimer("onstart");postScanSources();
}

在这里插入图片描述

4 小结

有两种配置文件,mediacodec.xml和media_profile.xml
这两种解析出的数据分别存放到medicodeclist和mediaprofile中。
至于这两种配置文件的关系,后续有空再追