CSource过滤器上的SetMediaTime使输出AVI无意义 - 任何想法为什么?

时间:2018-02-01 22:22:41

标签: c++ video directshow avi

更新:我最初发布的代码实际上没有重现该问题;对于不予以验证,我深表歉意。奇怪行为的关键是在一帧结束和下一帧开始之间的小增量(300 UNITS = 30微秒)。出于某种原因,我使用的捕获硬件报告的帧速率与它提供捕获的帧及其时间戳时实际展示的帧率不同。我已经更新了下面的来源,举例说明了如何模仿这种行为。

我写了一个简单的"假的" directshow的图像源过滤器,源自CSource。它运作良好。但是我发现了一些奇怪的东西,我无法解释。我的FillBuffer看起来像:

const REFERENCE_TIME TIME_PER_FRAME = 166000;

HRESULT MyFilterOutputPin::FillBuffer(IMediaSample *pms)
{
    //fill the bytes of the image media sample
    static REFERENCE_TIME currentTime = 0;
    REFERENCE_TIME startTime = currentTime;
    REFERENCE_TIME endTime = currentTime + TIME_PER_FRAME; //60Hz video
    // The +300 below is an update not in the original question, and is the
    // key to reproducing the behavior.
    currentTime += TIME_PER_FRAME + 300;
    pms->SetTime(&startTime, &endTime);
    pms->SetMediaTime(&startTime, &endTime);
    return S_OK;
}

我的CMediaType是通过调用

设置的
SetCMediaTypeForBitmap(1920,1080,TIME_PER_FRAME,&cmt);

该函数实现为

void SetCMediaTypeForBitmap(unsigned long width, unsigned long height, REFERENCE_TIME averageTimePerFrame, CMediaType *pmt)
{
    CMediaType mt;
    mt.SetType(&MEDIATYPE_Video);
    mt.SetSubtype(&MEDIASUBTYPE_RGB24);
    mt.SetFormatType(&FORMAT_VideoInfo);
    mt.SetSampleSize(GetBitmapBufferSize(width, height, BIT_COUNT));
    auto pvi = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER));
    pvi->rcSource.left = pvi->rcSource.top = 0;
    pvi->rcSource.right = width;
    pvi->rcSource.bottom = height;
    pvi->rcTarget = pvi->rcSource;
    pvi->dwBitErrorRate = 0;
    pvi->AvgTimePerFrame = averageTimePerFrame;
    pvi->bmiHeader.biSize = 40;
    pvi->bmiHeader.biWidth = width;
    pvi->bmiHeader.biHeight = height;
    pvi->bmiHeader.biPlanes = 1;
    pvi->bmiHeader.biBitCount = BIT_COUNT;
    pvi->bmiHeader.biCompression = 0;
    pvi->bmiHeader.biSizeImage = mt.lSampleSize;
    pvi->dwBitRate = (DWORD)(((uint64_t)mt.lSampleSize) * 8 / pvi->AvgTimePerFrame * UNITS);
    pvi->bmiHeader.biXPelsPerMeter = pvi->bmiHeader.biYPelsPerMeter = pvi->bmiHeader.biClrUsed = pvi->bmiHeader.biClrImportant = 0;
    *pmt = mt;
}

如果我尝试在MyFilterOutputPin :: FillBuffer的覆盖中设置我的样本上的媒体时间,然后将输出写入AVI文件,则根据VirtualDub,AVI文件将具有300倍的帧数。它列出了丢弃的大多数帧,并且周期性地有一个真实的帧。

如果我只是删除SetMediaTime,输出AVI就完全正常了。

我尝试了不同的方法来设置媒体时间。我可以把时间相对于滤波器的m_pStart,参考时钟上的时间等等。它似乎并不重要 - 只是MediaTime的存在使AVI上升。

我已经看到正确的directshow捕获过滤器设置MediaTime就好了,所以我猜测我没有做某事。有什么想法/想法吗?

这是我的文件属性的截图,大约需要2秒的捕获时间。 138帧真正输出,但AVI认为它有~40000帧,或真实数量的290倍。如果我在没有SetMediaTime的情况下运行相同的代码,AVI是2秒长,有138帧和。没有"掉线"帧。 Wonky AVI properties

非丢弃帧位于0,326,552,878,1104,1430,1756,1982。它们之间的增量为326,226,326,226,226,326,326,226。它&#39 ; s肯定让我挠头......

2 个答案:

答案 0 :(得分:0)

AVI帧索引将具有以流标头中定义的固定帧速率的每个帧的条目。例如,您创建300 fps轨道,然后以1 fps频率创建源时间戳样本。生成的文件将包含您的帧,并在它们之间删除299个(零长度)帧。这就是你应该得到的。

那说你的时间戳代码片段是正确的(你以直截了当的方式做到)。同样重要的是对流本身应用什么速率,这是从媒体类型派生的,您没有在问题中包括哪些内容以及应该检查哪些内容。

媒体类型速率和时间戳之间的匹配是获得准确输出AVI文件的关键。

答案 1 :(得分:0)

我今天偶然发现了this bit of documentation,我认为它实际上在某种程度上解释了事情。从它开始,

  

(可选)过滤器还可以指定样本的媒体时间。   在视频流中,媒体时间代表帧号。

因此,多路复用器期望媒体时间(如果存在),例如0-1、1-2、2-3。当媒体时间设置为连续的块(例如0-100000,100000-200000)时,我猜测多路复用器可以应对。但是,当存在差距时,根据Microsoft提供的文档,我可以理解事情之间的分歧。

但是知道这实际上很强大。由于AVI文件是恒定的帧速率格式,因此您可以在需要时使用媒体时间来传达帧丢失。为此,我已经开始成功使用它们。

仅供参考,几天前,我再次尝试根据项目的实际时间添加媒体播放时间,而直接显示图的结束时间不是E_FAIL,而是有趣的结果。

tl; dr仅使用媒体时间来通信帧号,至少与AVI多路复用器通信。