TS Source Filter 详细开发步骤

时间:2024-03-09 18:33:25

 

一、TS流的分析

TS流是如何产生的?

   

 

 从上图可以看出,视频ES和音频ES通过打包器和共同或独立的系统时间基准形成一个个PES,通过TS复用器复用形成的传输流。注意这里的TS流是位流格式(分析Packet的时候会解释),也即是说TS流是可以按位读取的。

TS流的格式是怎样的?

        TS流是基于Packet的位流格式,每个包是188个字节(或204个字节,在188个字节后加上了16字节的CRC校验数据,其他格式一样)。整个TS流组成形式如下:

 

Packet Header(包头)信息说明

1

sync_byte

8bits

同步字节

2

transport_error_indicator

1bit

错误指示信息(1:该包至少有1bits传输错误)

3

payload_unit_start_indicator

1bit

负载单元开始标志(packet不满188字节时需填充)

4

transport_priority

1bit

传输优先级标志(1:优先级高)

5

PID

13bits

Packet ID号码,唯一的号码对应不同的包

6

transport_scrambling_control

2bits

加密标志(00:未加密;其他表示已加密)

7

adaptation_field_control

2bits

附加区域控制

8

continuity_counter

4bits

包递增计数器

 PID是TS流中唯一识别标志,Packet Data是什么内容就是由PID决定的。如果一个TS流中的一个Packet的Packet Header中的PID是0x0000,那么这个Packet的Packet Data就是DVB的PAT表而非其他类型数据(如Video、Audio或其他业务信息)。下表给出了一些表的PID值,这些值是固定的,不允许用于更改。

PID 值

PAT

0x0000

CAT

0x0001

TSDT

0x0002

EIT,ST

0x0012

RST,ST

0x0013

TDT,TOT,ST

0x0014

         TS流的基本内容就是这些了。

        回顾一下,TS流是一种位流(当然就是数字的),它是由ES流分割成PES后复用而成的;它经过网络传输被机顶盒接收到;数字电视机顶盒接收到TS流后将解析TS流。

        TS流是由一个个Packet(包)构成的,每个包都是由Packet Header(包头)和Packet Data(包数据)组成的。其中Packet Header指示了该Packet是什么属性的,并给出了该Packet Data的数据的唯一网络标识符PID。

PAT

PAT(Program Association Table):节目关联表,该表的PID是固定的0x0000,它的主要作用是指出该传输流ID,以及该路传输流中所对应的几路节目流的 MAP 表和网络信息表的PID。 节目关联表(PAT Program Association Table) 是数字电视系统中节目指示的根节点。 其包标识符(Packet IDentifier、简称PID)为0。终端设备(如机顶盒)搜索节目时最先都是从这张表开始搜索的。从PAT中解析出节目映射表(Program Map Table、简称PMT),再从PMT解析出基本元素(如视频、音频、数据等)的PID及节目号、再根据节目从节目业务描述表(Service Description Table、简称SDT)中搜索出节目名称。 CAT相关表是从PMT中得到。

PMT

PMT(Program Map Table):节目映射表,该表的PID是由PAT提供给出的。通过该表可以得到一路节目中包含的信息,例如,该路节目由哪些流构成和这些流的类型(视频,音频,数据),指定节目中各流对应的PID,以及该节目的PCR所对应的PID。  

 

程序需用TS流分析

它是一个单节目TS流, 格式为:  视频(codec: MPEG4, PID:0x11), 音频(codec: AC3, PID:0x14)

 

 

 

二、基本环境的配置

1、         新建项目--->Win32--->DLL、空项目

 

2VC++目录--->包含目录

C:\ProgramFiles\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses

C:\ProgramFiles\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\common

C:\Program Files\Microsoft SDKs\Windows\v7.1\Include

 

 

3VC++目录--->库目录

C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib

C:\ProgramFiles\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses\Debug

 

4、                     链接器--->输入--->附加依赖项

 

三、编码实现

Filter的注册

AMOVIESETUP_MEDIATYPE、

AMOVIESETUP_PIN、

AMOVIESETUP_FILTER  

基类函数重写

    //填充样本函数 

    //参数pMediaSample就是要传递到下一个Filter输入pin的样本 

    //把数据填充到pMediaSample中就是这个函数的功能 

    //overridden CSourceStream functions

    HRESULT FillBuffer(IMediaSample *pms);

 

 

     //协商每个CMediaSample数据块的大小 

    HRESULT DecideBufferSize(IMemAllocator *pIMemAlloc,

        ALLOCATOR_PROPERTIES *pProperties);

 

    //检测是否支持参数传入的媒体类型

    HRESULT CheckMediaType(const CMediaType *pMediaType);

 

    //获得媒体类型 

    //在枚举器中枚举支持的媒体类型时调用此函数得到PIN支持的媒体类型 

    //此函数设置pmt的各个成员,因此,由此函数的内容觉得PIN支持什么媒体类型

    HRESULT GetMediaType(int iPosition, CMediaType *pmt);

 

    // Used to create output queue objects

    HRESULT Active();

    HRESULT Inactive();

    // Overriden to pass data to the output queues

    HRESULT Deliver(IMediaSample *pMediaSample);

 

//填充Sample 

HRESULT TSOutputPin::FillBuffer(IMediaSample *pms)

{

    BYTE *pData;

    long lDataLen=0;

    int i=0;

 

    if(m_hFile==NULL)

    {

        m_hFile = CreateFile(TEXT("E:\\zwj.ts"),

            GENERIC_READ,

            FILE_SHARE_READ,

            NULL,

            OPEN_EXISTING,

            FILE_ATTRIBUTE_NORMAL,

            NULL);

 

        if(m_hFile == INVALID_HANDLE_VALUE )

        {              

            _cprintf("Creating file is failed\n");

            m_hFile=NULL;

            return E_FAIL;

        }  

        _cprintf("Inside FillBuffer file is created\n");

    }

    ///////////////////////////////////////////////

     //获得Sample中存放数据的地址

    pms->GetPointer(&pData);

    //取得Sample分配的内存大小

    lDataLen = pms->GetSize();

    DWORD lDataRead;

   

     if(!ReadFile(m_hFile,pData,lDataLen,&lDataRead,NULL))

         {

              printf("Read Audio data from file failed\n");

              m_hFile=NULL;

              return 0;

           

         }

 

        if(lDataRead <= 0)

            SetFilePointer(m_hFile,NULL,0,FILE_BEGIN);

   

    pms->SetActualDataLength((long)lDataRead);

   //TODO   now check with the TS file

//  pms->SetActualDataLength(lDataLen);

 

    return NOERROR;

}

 

 

 

 

//协商Sample的大小 

HRESULT TSOutputPin::DecideBufferSize(IMemAllocator *pIMemAlloc,

                         ALLOCATOR_PROPERTIES *pProperties)

{

    DbgLog((LOG_TRACE, 0, TEXT("DecideBufferSize")));

    CAutoLock cAutoLock(m_pFilter->pStateLock());

    ASSERT(pIMemAlloc);

    ASSERT(pProperties);

    HRESULT hr = NOERROR;

 

 

    //确定buffer的个数和每个buffer大小

    pProperties->cBuffers = DIB_OUTPUTPIN_QUEUE_SIZE;

    pProperties->cbBuffer = DIB_TS_BUFFER_SIZE;

 

    ASSERT(pProperties->cbBuffer);

 

    // Ask the allocator to reserve us some sample memory, NOTE the function

    // can succeed (that is return NOERROR) but still not have allocated the

    // memory that we requested, so we must check we got whatever we wanted

 

     //设置属性页

    ALLOCATOR_PROPERTIES Actual;

    hr = pIMemAlloc->SetProperties(pProperties,&Actual);

    if (FAILED(hr)) {

        return hr;

    }

 

    // Is this allocator unsuitable

 

    if (Actual.cbBuffer < pProperties->cbBuffer) {

        return E_FAIL;

    }

 

    // Make sure that we have only DIB_OUTPUTPIN_QUEUE_SIZE buffer

 

    ASSERT( Actual.cBuffers == DIB_OUTPUTPIN_QUEUE_SIZE );

 

    return NOERROR;

}

 

 

//检查媒体类型 

//主要是对GetMediaType中设置的各个参数进行比较 

HRESULT TSOutputPin::CheckMediaType(const CMediaType *pMediaType)

{

    DbgLog((LOG_TRACE, 0, TEXT("CheckMediaType")));

 

    CAutoLock cAutoLock(m_pFilter->pStateLock());

 

    if (pMediaType->majortype != MEDIATYPE_Stream || pMediaType->subtype != MEDIASUBTYPE_MPEG2_TRANSPORT)

        return S_FALSE;

 

 

    return S_OK;

}

 

 

 

 

//获取媒体类型 

//填充pmt 

//最简单的源Filter,因此只支持一种类型,所以iPosition为0 

HRESULT TSOutputPin::GetMediaType(int iPosition, CMediaType *pmt)

{

    _cprintf("TSOutputPin::GetMediaType\n");

    DbgLog((LOG_TRACE, 0, TEXT("GetMediaType")));

    CAutoLock cAutoLock(m_pFilter->pStateLock());

    if (iPosition < 0) {

        return E_INVALIDARG;

    }

 

    if (iPosition >= 1) {

        return VFW_S_NO_MORE_ITEMS;

    }

    pmt->majortype  = MEDIATYPE_Stream;

    pmt->subtype    = MEDIASUBTYPE_MPEG2_TRANSPORT;

    pmt->formattype = FORMAT_None ;

    return S_OK;

}

 

GraphEdit调试

打开Grapedit(在DirectX SDK的DirectX Utilities中), 插入TS Source和Mpeg-2 Demultiplexer Filter,再把它们对应的pin相连。 并配置Mpeg-2 Demultiplexer, 设置好各个pin对应的媒体类型,映射好PID,并将Pin输出的数据结构设为Elementary Stream (A/V only), 如下所示:

 

 

 

 

这时MPEG-2 Demultiplexer上应该会出现相应的输出pins,点右键选render pin即可。 Graph Edit会自动挑选解码器并完成连接。 连接好的Filter Graph如下图: