Android多媒體開發(4)——添加FFmpeg支持

Android多媒體開發需要添加FFmpeg支持,其實其步驟在前面Android Media Framework學習的一系列文章中就有講述,《支持格式擴展》這篇文章已經講完了Android多媒體需要擴展支持其他格式的步驟,這篇文章具體講述Android怎么支持FFmpeg。

參考文章有兩篇,分別是:文章一文章二

1.Sniffer
音視頻的解碼播放是從探測文件格式開始的,即在DataSource.cpp中通過Sniff探測文件格式,擴展FFmpeg也是一樣的,先要在DataSource中加入相應的Sniff。

DataSource通過RegisterDefaultSniffers給每一種格式添加相應的sniffer。

// static
void DataSource::RegisterDefaultSniffers() {
    Mutex::Autolock autoLock(gSnifferMutex);
    if (gSniffersRegistered) {
        return;
    }

    RegisterSniffer_l(SniffMPEG4);
    RegisterSniffer_l(SniffMatroska);
    RegisterSniffer_l(SniffOgg);
    RegisterSniffer_l(SniffWAV);
    RegisterSniffer_l(SniffFLAC);
    RegisterSniffer_l(SniffAMR);
    RegisterSniffer_l(SniffMPEG2TS);
    RegisterSniffer_l(SniffMP3);
    RegisterSniffer_l(SniffAAC);
    RegisterSniffer_l(SniffMPEG2PS);
    RegisterSniffer_l(SniffWVM);
    RegisterSnifferPlugin();

    char value[PROPERTY_VALUE_MAX];
    if (property_get("drm.service.enabled", value, NULL)
            && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
        RegisterSniffer_l(SniffDRM);
    }
    //RegisterSniffer_l(SniffASF);
    //RegisterSniffer_l(SniffNVAVI);

    gSniffersRegistered = true;
}

這里,我們需要關注的是RegisterSnifferPlugin()。

// static
void DataSource::RegisterSnifferPlugin() {
    static void (*getExtractorPlugin)(MediaExtractor::Plugin *) =
            (void (*)(MediaExtractor::Plugin *))loadExtractorPlugin();

    MediaExtractor::Plugin *plugin = MediaExtractor::getPlugin();
    if (!plugin->sniff && getExtractorPlugin) {
        getExtractorPlugin(plugin);
    }
    if (plugin->sniff) {
        RegisterSniffer_l(plugin->sniff);
    }
}

我們看到,RegisterSnifferPlugin()中有個loadExtractorPlugin()方法,具體看它的實現。

static void *loadExtractorPlugin() {
    void *ret = NULL;
    char lib[PROPERTY_VALUE_MAX];
    if (property_get("media.stagefright.extractor-plugin", lib, "libFFmpegExtractor.so")) {
        if (void *extractorLib = ::dlopen(lib, RTLD_LAZY)) {
            ret = ::dlsym(extractorLib, "getExtractorPlugin");
            ALOGW_IF(!ret, "Failed to find symbol, dlerror: %s", ::dlerror());
        } else {
            ALOGV("Failed to load %s, dlerror: %s", lib, ::dlerror());
        }
    }
    return ret;
}

可以看到,也就是在loadExtractorPlugin()方法加載了libFFmpegExtractor.so庫。

2.Extractor
擴展了FFmpeg的sniffer,接下來就是創建FFmpegExtractor實例,如果不明白可以回頭看看Android Media Framework的相關文章。
MediaExtractor管理著各種具體的Extractor,FFmpegExtractor也不例外。在上面的loadExtractorPlugin()方法中調用的是FFmpegExtractor的getExtractorPlugin()方法,看看具體實現:

extern "C" void getExtractorPlugin(android::MediaExtractor::Plugin *plugin)
{
    plugin->sniff = android::SniffFFMPEG;
    plugin->create = android::CreateFFmpegExtractor;
}

很好理解,就是指明SniffFFMPEG并調用CreateFFmpegExtractor。其中SniffFFMPEG()方法就是通過設置confidence值來指定格式,我自己項目中的比較復雜,這里貼下文章二中的代碼:

bool SniffFFMPEG(  const sp<DataSource> &source, String8 *mimeType, 
        float *confidence, sp<AMessage> *) {  
  
    LOGD("SniffFFMPEG: confidence force to be 1.0");  
  
    // QGC: ffmpeg force to be high confidence  
    *mimeType = MEDIA_MIMETYPE_CONTAINER_FFMPEG;  
    *confidence = 1.0f;  
    return true;  
  
}

相當暴力,直接指定*confidence = 1.0f。

CreateFFmpegExtractor顧名思義就是創建FFmpegExtractor的實例。

MediaExtractor *CreateFFmpegExtractor(const sp<DataSource> &source, const char *mime, const sp<AMessage> &meta) {
    MediaExtractor *ret = NULL;
    AString notuse;
    if (meta.get() && meta->findString("extended-extractor", &notuse) && (
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)          ||
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)           ||
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)        ||
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)          ||
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC3)           ||
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_APE)           ||
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_DTS)           ||
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II) ||
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RA)            ||
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_WMA)           ||
            !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FFMPEG)        ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)     ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MOV)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)  ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_TS)        ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)   ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AVI)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_ASF)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WEBM)      ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WMV)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPG)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FLV)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_DIVX)      ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_RM)        ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FLAC)      ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_APE)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_DTS)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MP2)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_RA)        ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_VC1)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_HEVC)      ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WMA)       ||
            !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FFMPEG))) {
        ALOGV("media format %s", mime);
        bool audio_only = false;
        if (!strncmp(mime, "audio/", 6)) {
            ALOGV("set audio only mode");
            audio_only = true;
        }
        ret = new FFmpegExtractor(source, meta, audio_only);
    }

    ALOGD("%ssupported mime: %s", (ret ? "" : "un"), mime);
    return ret;
}

這個方法用到的 meta->findString("extended-extractor", &notuse)其實就在SniffFFMPEG中指定(當然上邊的SniffFFMPEG沒有),這里給出:

(*meta)->setString("extended-extractor", "extended-extractor");
(*meta)->setString("extended-extractor-subtype", "ffmpegextractor");
(*meta)->setString("extended-extractor-mime", container);

3.kComponents
kComponents數組指定的是系統支持的編解碼器,當然要加上FFmpeg的相關東西。

static const struct {
    const char *mName;
    const char *mLibNameSuffix;
    const char *mRole;

} kComponents[] = {
    { "OMX.ffmpeg.mpeg2v.decoder", "ffmpegvdec", "video_decoder.mpeg2v" },
    { "OMX.ffmpeg.h263.decoder", "ffmpegvdec", "video_decoder.h263" },
    { "OMX.ffmpeg.mpeg4.decoder", "ffmpegvdec", "video_decoder.mpeg4" },
    { "OMX.ffmpeg.wmv.decoder", "ffmpegvdec", "video_decoder.wmv" },
    { "OMX.ffmpeg.rv.decoder", "ffmpegvdec", "video_decoder.rv" },
    { "OMX.ffmpeg.h264.decoder", "ffmpegvdec", "video_decoder.avc" },
    { "OMX.ffmpeg.vp8.decoder", "ffmpegvdec", "video_decoder.vp8" },
    { "OMX.ffmpeg.vc1.decoder", "ffmpegvdec", "video_decoder.vc1" },
    { "OMX.ffmpeg.flv1.decoder", "ffmpegvdec", "video_decoder.flv1" },
    { "OMX.ffmpeg.divx.decoder", "ffmpegvdec", "video_decoder.divx" },
    { "OMX.ffmpeg.hevc.decoder", "ffmpegvdec", "video_decoder.hevc" },
    { "OMX.ffmpeg.vtrial.decoder", "ffmpegvdec", "video_decoder.trial" },
    { "OMX.ffmpeg.aac.decoder", "ffmpegadec", "audio_decoder.aac" },
    { "OMX.ffmpeg.mp3.decoder", "ffmpegadec", "audio_decoder.mp3" },
    { "OMX.ffmpeg.vorbis.decoder", "ffmpegadec", "audio_decoder.vorbis" },
    { "OMX.ffmpeg.wma.decoder", "ffmpegadec", "audio_decoder.wma" },
    { "OMX.ffmpeg.ra.decoder", "ffmpegadec", "audio_decoder.ra" },
    { "OMX.ffmpeg.flac.decoder", "ffmpegadec", "audio_decoder.flac" },
    { "OMX.ffmpeg.mp2.decoder", "ffmpegadec", "audio_decoder.mp2" },
    { "OMX.ffmpeg.ac3.decoder", "ffmpegadec", "audio_decoder.ac3" },
    { "OMX.ffmpeg.ape.decoder", "ffmpegadec", "audio_decoder.ape" },
    { "OMX.ffmpeg.dts.decoder", "ffmpegadec", "audio_decoder.dts" },
    { "OMX.ffmpeg.atrial.decoder", "ffmpegadec", "audio_decoder.trial" },
    { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
    { "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },
    { "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
    { "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },
    { "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
    { "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },
    { "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" },
    { "OMX.google.h264.encoder", "h264enc", "video_encoder.avc" },
    { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
    { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
    { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
    { "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },
    { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
    { "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" },
    { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },
    { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },
    { "OMX.google.vp8.decoder", "vpxdec", "video_decoder.vp8" },
    { "OMX.google.vp9.decoder", "vpxdec", "video_decoder.vp9" },
    { "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" },
    { "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" },
    { "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" },
    { "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" },
};

"OMX.google."開頭的是Android本身支持的,"OMX.ffmpeg."開頭的是FFmpeg的。

4.media_codecs.xml
最后,在/etc/media_codecs.xml中配置自己需要的編解碼器。

<!-- ffmpeg video codec -->
<MediaCodec name="OMX.ffmpeg.mpeg2v.decoder" type="video/mpeg2" />
<MediaCodec name="OMX.ffmpeg.h263.decoder"   type="video/3gpp" />
<MediaCodec name="OMX.ffmpeg.mpeg4.decoder"  type="video/mp4v-es" />
<MediaCodec name="OMX.ffmpeg.wmv.decoder"    type="video/x-ms-wmv" />
<MediaCodec name="OMX.ffmpeg.rv.decoder"     type="video/vnd.rn-realvideo" />
<MediaCodec name="OMX.ffmpeg.h264.decoder"   type="video/avc" />
<MediaCodec name="OMX.ffmpeg.vp8.decoder"    type="video/x-vnd.on2.vp8" />
<MediaCodec name="OMX.ffmpeg.vc1.decoder"    type="video/vc1" />
<MediaCodec name="OMX.ffmpeg.flv1.decoder"    type="video/x-flv" />
<MediaCodec name="OMX.ffmpeg.divx.decoder"    type="video/divx" />
<MediaCodec name="OMX.ffmpeg.hevc.decoder"    type="video/hevc" />
<MediaCodec name="OMX.ffmpeg.vtrial.decoder" type="video/ffmpeg" />

<!-- ffmpeg audio codec -->
<MediaCodec name="OMX.ffmpeg.aac.decoder"    type="audio/mp4a-latm" />
<MediaCodec name="OMX.ffmpeg.mp3.decoder"    type="audio/mpeg" />
<MediaCodec name="OMX.ffmpeg.vorbis.decoder" type="audio/vorbis" />
<MediaCodec name="OMX.ffmpeg.wma.decoder"    type="audio/x-ms-wma" />
<MediaCodec name="OMX.ffmpeg.ra.decoder"     type="audio/vnd.rn-realaudio" />
<MediaCodec name="OMX.ffmpeg.flac.decoder"   type="audio/flac" />
<MediaCodec name="OMX.ffmpeg.mp2.decoder"    type="audio/mpeg-L2" />
<MediaCodec name="OMX.ffmpeg.ape.decoder"    type="audio/x-ape" />
<MediaCodec name="OMX.ffmpeg.atrial.decoder" type="audio/ffmpeg" />

當然,要想完整的添加FFmpeg,還有很多頭文件需要加,有的文件代碼也需要稍許修改,但整個脈絡是這樣的。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容