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", ¬use) && (
!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", ¬use)其實就在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,還有很多頭文件需要加,有的文件代碼也需要稍許修改,但整個脈絡是這樣的。