MPC中是如何显示字幕文件的?

来源:互联网 发布:程序员职业发展路线 编辑:程序博客网 时间:2024/04/28 11:47

现在的音视频文件大都有字幕文件,那么播放器是如何显示这些字幕文件的呢?

下面是笔者分析MPC代码的过程:

 

1.class Rasterizer;

   光栅操作类

   Rasterizer::Draw()是最终画图的方法。

 

 

2.class CSimpleTextSubtitle : public CSTSArray;

 

   实现了对各种不同字幕文件的读写

 

3.class CRenderedTextSubtitle : public CSimpleTextSubtitle,

                                   public ISubPicProviderImpl,   

                                             public ISubStream;

   实现了字幕流及各种不同字幕文件的处理。

 

4.class CSubtitle : public CList<CLine*>

   实现了文字的各种特效处理。

 

5.CDirectVobSub::put_TextSettings()

  设置字体的各种样式,如字体、字符集、是否斜体、高度、权重、是否画删除线、是否画下划线、字体颜色、是否有阴影、是否有轮廓。

 

  CVideoProcFilter::put_Placement()

  设置字幕显示位置,为百分比。

 

  CDirectVobSub::put_TextBorderStyle()

  设置边缘风格,是轮廓还是不透明的方框、边缘宽度、阴影深度

 

  CDirectVobSub::put_TextFontStyle2()

  设置倾斜角度、宽度比例、高度比例、文字间距

 

  CDirectVobSub::put_TextColor()

  设置主要、次要、轮廓、阴影的颜色和透明度。

 

6.SubPicDesc结构体中的type为颜色空间,:MSP_YUY2,MSP_RGB16等。

                              bits为帧数据。

 

7.CMemSubPic::AlphaBlt();

   进行alpha混合;

 

8.GetTextExtentPoint32W()返回的一个汉字的宽度为1115,原因是在CRenderedTextSubtitle类的构造函数中调用了SetMapMode(g_hDC, MM_TEXT)。

 

9.new CText的实例,然后调用CLineAddTail()CText的实例添加到CLine;

   调用Cline::PaintShadow()画阴影;

   调用Cline::PaintOutline()画轮廓;

   调用Cline::PaintBody()画主体;

  

   代码如下所示:

   CWord* w = new CText(style, str.Mid(i, j-i), m_ktype, m_kstart, m_kend);

   CLine* ret = new CLine();

   if(!ret) return(NULL);

   ret->AddTail(w->Copy());

 

   delete w;

 

   bbox2 |= l->PaintShadow(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);

   bbox2 |= l->PaintOutline(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);

   bbox2 |= l->PaintBody(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);

 

10.void CSimpleTextSubtitle::Add()将字幕文字添加到队列中;

    void CRenderedTextSubtitle::ParseString()将一个长的字幕分解成一行或多行;

 

11.在CVideoProcFilter::Transform(IMediaSample* pIn)中获取一个ISubPic的实例,然后调用其方法AlphaBlt()将字幕画到帧上,

    如下面的代码所示:

    if(SUCCEEDED(m_pSubPicQueue->LookupSubPic(CalcCurrentTime(), &pSubPic)) && pSubPic)

    {

CRect r;

pSubPic->GetDirtyRect(r);

 

if(fFlip ^ fFlipSub)

spd.h = -spd.h;

Debug_LOG("LookupSubPic ok");

pSubPic->AlphaBlt(r, r, &spd);

    }

 

    CVideoProcFilter::InitSubPicQueue()

    {

          CComPtr<ISubPicAllocator> pSubPicAllocator = new CMemSubPicAllocator(m_spd.type, CSize(m_w, m_h));

         

          pSubPicAllocator->SetCurSize(window);

pSubPicAllocator->SetCurVidRect(CRect(CPoint((window.cx - video.cx)/2, (window.cy - video.cy)/2), video));

 

//下面的两行代码为陈丹宇添加,测试能否获取一个ISubPic *对象

CComPtr<ISubPic> pStatic;

if(FAILED(pSubPicAllocator->GetStatic(&pStatic)))

break;

    }

 

    class CMemSubPicAllocator : public ISubPicAllocatorImpl;

   

    STDMETHODIMP ISubPicAllocatorImpl::GetStatic(ISubPic** ppSubPic)

    {

if(!ppSubPic)

return E_POINTER;

 

if(!m_pStatic)

{

if(!Alloc(true, &m_pStatic) || !m_pStatic)

return E_OUTOFMEMORY;

}

 

m_pStatic->SetSize(m_cursize, m_curvidrect);

 

(*ppSubPic = m_pStatic)->AddRef();

 

return S_OK;

    }

 

    CMemSubPicAllocator::Alloc(bool fStatic, ISubPic** ppSubPic)

    {

if(!(*ppSubPic = new CMemSubPic(spd)))

return(false);

 

(*ppSubPic)->AddRef();

    }

 

 

   12.从CMemSubPic::AlphaBlt()中的src和dst值可以看出,字幕位图的大小是与图像帧的大小一样的。

       同时,从如下代码

       if(s2[3] < 0xff)

      {

d2[0] = (((d2[0]-0x10)*s2[3])>>8) + s2[1];

       }

       可以看出字幕位图是32位位图数据,其第四位(s2[3])alpha通道,如果该值等于0xff则该像素为透明的,否则将当前像素的值覆盖目标位置像素的值;

 

   13.CVideoProcFilter::Transform()中的pSubPic->AlphaBlt(r, d, &spd);

       CMemSubPic::AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget);

 

       将当前字幕画到目标帧中。

       而当前字幕的数据又是怎么得到的呢?

       CSubPicQueue::ThreadProc() ->

       RenderTo(pStatic, rtStart, rtStop, fps); ->

       CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox) ->

       bbox2 |= l->PaintShadow(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);

       bbox2 |= l->PaintOutline(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);

       bbox2 |= l->PaintBody(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);

 

  14.显示字幕的步骤:

      (1).构造字幕的描述信息SubPicDesc,然后调用new CMemSubPic构造出一个ISubPic的实例;

      (2).构造样式STSStyle;

      (3).调用如下代码创建字幕:

           CWord* w = new CText(style, str, 0, 0, 0);

 CLine* l = new CLine();

 if(!l) return(NULL);

 l->AddTail(w->Copy());

 

 调用如下函数分别画阴影、轮廓和文字:

 bbox2 |= l->PaintShadow(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);

 bbox2 |= l->PaintOutline(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);

 bbox2 |= l->PaintBody(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);

 

     (4).构造目标帧的描述信息SubPicDesc,并调用CMemSubPic::AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget)

          将字幕画到目标帧中;