// 所有原创文章转载请注明作者及链接
// blackboycpp(AT)gmail.com
// QQ群: 135202158
这个Filter的主要作用是把TS文件分割成TS包并发送至下游的MPEG-2 Demultiplexer(Push模式)。
通过在GraphEdit里(当然也可以编码实现)对MPEG-2 Demultiplexer进行必要的设置,
再连接一些Decoder和Render filters, 可以播放此TS文件。
首先, MPEG-2 Demultiplexer是可以用于push模式的:(http://msdn.microsoft.com/en-us/library/dd390717(VS.85).aspx)
The MPEG-2 Demultiplexer ("demux") can operate in push mode or pull mode. In push mode, the data comes from a live source, such as a network broadcast. In pull mode, the data comes from a local file.
1. 首先安装DirectX SDK 9.0c并配置好环境(包括BaseClasses的编译), 如果是在VC++2005下进行工作, 可以看我以前的转帖日志
2. 找一个TS文件, 如果机器上没有, 可以下载这个:http://www.dododge.net/roku/hd-test-streams/wjz-200309111230-clip.ts
它是一个单节目TS流, 格式为: 视频(codec: MPEG4, PID:0x11), 音频(codec: AC3, PID:0x14)
2. 把位于“DirectX安装目录/Samples/C++/DirectShow/Filters”下的Ball例子复制一个, 我们就要改它了
3. 打开ball工程, 做以下改动:
(1)修改输出pin媒体格式
const AMOVIESETUP_MEDIATYPE sudOpPinTypes = { &MEDIATYPE_Stream, &MEDIASUBTYPE_MPEG2_TRANSPORT };
(2)注释掉不必要的函数, 主要是(如果编译时出错, 视情况再注释掉其他无用函数):
//HRESULT SetMediaType(const CMediaType *pMediaType); //HRESULT CheckMediaType(const CMediaType *pMediaType); //HRESULT GetMediaType(int iPosition, CMediaType *pmt); //STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
添加一个成员函数:
HRESULT GetMediaType(CMediaType *pMediaType); HRESULT CBallStream::GetMediaType(CMediaType *pmt) { CheckPointer(pmt,E_POINTER); CAutoLock cAutoLock(m_pFilter->pStateLock()); pmt->SetType(&MEDIATYPE_Stream); pmt->SetSubtype(&MEDIASUBTYPE_MPEG2_TRANSPORT); pmt->SetTemporalCompression(FALSE); return NOERROR; } // GetMediaType
(3)添加一些成员, 用于操作我们的TS文件:
private: FILE* m_file; // TS文件指针 int m_flen; // TS文件长度 int m_offset; // TS文件读取偏移
(4)在OnThreadCreate()成员函数中添加以下代码:
// 打开你硬盘上的TS文件 m_file = fopen("F://test.ts", "rb"); // 获取文件长度 fseek(m_file, 0L, SEEK_END); m_flen = ftell(m_file); // 我下载的这个文件,TS同步头0x47从第79字节开始 fseek(m_file, 79L, SEEK_SET); m_offset = 79;
(5)在DecideBufferSize()成员函数中,将每个buffer大小改成188字节:
pProperties->cbBuffer = 188;
(6)修改FillBuffer()成员函数:
HRESULT CBallStream::FillBuffer(IMediaSample *pms) { CheckPointer(pms,E_POINTER); ASSERT(m_Ball); BYTE *pData; long lDataLen; pms->GetPointer(&pData); lDataLen = pms->GetSize(); ZeroMemory(pData, lDataLen); { CAutoLock cAutoLockShared(&m_cSharedState); // 重复读文件 if(m_offset >= m_flen) { m_offset = 79; fseek(m_file, 79L, SEEK_SET); } // 用188字节的TS包数据填充Buffer fread(pData, 1, 188, m_file); m_offset += 188; } // 实际数据长度为188字节 pms->SetActualDataLength(188); pms->SetSyncPoint(FALSE); return NOERROR; } // FillBuffer
(7)在CBallStream的构造和析构函数里添加一些初始化/反初始化代码, 主要是一些文件操作。
再检查一下还有哪些无关的代码没注释掉。
4. 编译工程, 如果出错, 依提示修改。 应该主要是一些以前例子的东西, 注释掉就可以了。
5. 使用regsvr32命令注释你生成的Filter,如:
// 注册: D:/Ball/Debug>regsvr32 ball.ax // 反注册: D:/Ball/Debug>regsvr32 /u ball.ax
6. 打开Grapedit(在DirectX SDK的DirectX Utilities中), 插入Bouncing Ball Filter和Mpeg-2 Demultiplexer Filter,
再把它们对应的pin相连。 并配置Mpeg-2 Demultiplexer, 设置好各个pin对应的媒体类型,映射好PID,
并将Pin输出的数据结构设为Elementary Stream (A/V only), 如下所示:
然后再添加Audio Render, Video Render,这时MPEG-2 Demultiplexer上应该会出现相应的输出pins,
点右键选render pin即可。 Graph Edit会自动挑选解码器并完成连接。 连接好的Filter Graph如下图:
然后运行graph就可以了