LAV Filter 源代码分析 2: LAV Splitter
LAV Filter 中最著名的就是 LAV Splitter,支持Matroska /WebM,MPEG-TS/PS,MP4/MOV,FLV,OGM / OGG,AVI等其他格式,广泛存在于各种视频播放器(暴风影音这类的)之中。
本文分析一下它的源代码。在分析之前,先看看它是什么样的。
使用GraphEdit随便打开一个视频文件,就可以看见LAV Filter:
可以右键点击这个Filter看一下它的属性页面,如图所示:
属性设置页面:
支持输入格式:
下面我们在 VC 2010 中看一下它的源代码:
从何看起呢?就先从directshow的注册函数看起吧,位于dllmain.cpp之中。部分代码的含义已经用注释标注上了。从代码可以看出,和普通的DirectShow Filter没什么区别。
dllmain.cpp
//初始化AVFormatSTDMETHODIMP CLAVFDemuxer::InitAVFormat(LPCOLESTR pszFileName, BOOL bForce){ HRESULT hr = S_OK; const char *format = NULL; //获取InputFormat信息(,短名称,长名称) lavf_get_iformat_infos(m_avFormat->iformat, &format, NULL); if (!bForce && (!format || !m_pSettings->IsFormatEnabled(format))) { DbgLog((LOG_TRACE, 20, L"::InitAVFormat() - format of type '%S' disabled, failing", format ? format : m_avFormat->iformat->name)); return E_FAIL; } m_pszInputFormat = format ? format : m_avFormat->iformat->name; m_bVC1SeenTimestamp = FALSE; LPWSTR extension = pszFileName ? PathFindExtensionW(pszFileName) : NULL; m_bMatroska = (_strnicmp(m_pszInputFormat, "matroska", 8) == 0); m_bOgg = (_strnicmp(m_pszInputFormat, "ogg", 3) == 0); m_bAVI = (_strnicmp(m_pszInputFormat, "avi", 3) == 0); m_bMPEGTS = (_strnicmp(m_pszInputFormat, "mpegts", 6) == 0); m_bMPEGPS = (_stricmp(m_pszInputFormat, "mpeg") == 0); m_bRM = (_stricmp(m_pszInputFormat, "rm") == 0); m_bPMP = (_stricmp(m_pszInputFormat, "pmp") == 0); m_bMP4 = (_stricmp(m_pszInputFormat, "mp4") == 0); m_bTSDiscont = m_avFormat->iformat->flags & AVFMT_TS_DISCONT; WCHAR szProt[24] = L"file"; if (pszFileName) { DWORD dwNumChars = 24; hr = UrlGetPart(pszFileName, szProt, &dwNumChars, URL_PART_SCHEME, 0); if (SUCCEEDED(hr) && dwNumChars && (_wcsicmp(szProt, L"file") != 0)) { m_avFormat->flags |= AVFMT_FLAG_NETWORK; DbgLog((LOG_TRACE, 10, TEXT("::InitAVFormat(): detected network protocol: %s"), szProt)); } } // TODO: make both durations below configurable // decrease analyze duration for network streams if (m_avFormat->flags & AVFMT_FLAG_NETWORK || (m_avFormat->flags & AVFMT_FLAG_CUSTOM_IO && !m_avFormat->pb->seekable)) { // require at least 0.2 seconds m_avFormat->max_analyze_duration = max(m_pSettings->GetNetworkStreamAnalysisDuration() * 1000, 200000); } else { // And increase it for mpeg-ts/ps files if (m_bMPEGTS || m_bMPEGPS) m_avFormat->max_analyze_duration = 10000000; } av_opt_set_int(m_avFormat, "correct_ts_overflow", !m_pBluRay, 0); if (m_bMatroska) m_avFormat->flags |= AVFMT_FLAG_KEEP_SIDE_DATA; m_timeOpening = time(NULL); //获取媒体流信息 int ret = avformat_find_stream_info(m_avFormat, NULL); if (ret < 0) { DbgLog((LOG_ERROR, 0, TEXT("::InitAVFormat(): av_find_stream_info failed (%d)"), ret)); goto done; } DbgLog((LOG_TRACE, 10, TEXT("::InitAVFormat(): avformat_find_stream_info finished, took %I64d seconds"), time(NULL) - m_timeOpening)); m_timeOpening = 0; // Check if this is a m2ts in a BD structure, and if it is, read some extra stream properties out of the CLPI files if (m_pBluRay) { m_pBluRay->ProcessClipLanguages(); } else if (pszFileName && m_bMPEGTS) { CheckBDM2TSCPLI(pszFileName); } SAFE_CO_FREE(m_stOrigParser); m_stOrigParser = (enum AVStreamParseType *)CoTaskMemAlloc(m_avFormat->nb_streams * sizeof(enum AVStreamParseType)); if (!m_stOrigParser) return E_OUTOFMEMORY; for(unsigned int idx = 0; idx < m_avFormat->nb_streams; ++idx) { AVStream *st = m_avFormat->streams[idx]; // Disable full stream parsing for these formats if (st->need_parsing == AVSTREAM_PARSE_FULL) { if (st->codec->codec_id == AV_CODEC_ID_DVB_SUBTITLE) { st->need_parsing = AVSTREAM_PARSE_NONE; } } if (m_bOgg && st->codec->codec_id == AV_CODEC_ID_H264) { st->need_parsing = AVSTREAM_PARSE_FULL; } // Create the parsers with the appropriate flags init_parser(m_avFormat, st); UpdateParserFlags(st);#ifdef DEBUG AVProgram *streamProg = av_find_program_from_stream(m_avFormat, NULL, idx); DbgLog((LOG_TRACE, 30, L"Stream %d (pid %d) - program: %d, codec: %S; parsing: %S;", idx, st->id, streamProg ? streamProg->pmt_pid : -1, avcodec_get_name(st->codec->codec_id), lavf_get_parsing_string(st->need_parsing)));#endif m_stOrigParser[idx] = st->need_parsing; if ((st->codec->codec_id == AV_CODEC_ID_DTS && st->codec->codec_tag == 0xA2) || (st->codec->codec_id == AV_CODEC_ID_EAC3 && st->codec->codec_tag == 0xA1)) st->disposition |= LAVF_DISPOSITION_SECONDARY_AUDIO; UpdateSubStreams(); if (st->codec->codec_type == AVMEDIA_TYPE_ATTACHMENT && (st->codec->codec_id == AV_CODEC_ID_TTF || st->codec->codec_id == AV_CODEC_ID_OTF)) { if (!m_pFontInstaller) { m_pFontInstaller = new CFontInstaller(); } m_pFontInstaller->InstallFont(st->codec->extradata, st->codec->extradata_size); } } CHECK_HR(hr = CreateStreams()); return S_OK;done: //关闭输入 CleanupAVFormat(); return E_FAIL;}