C++ CSource过滤器上的SetMediaTime使输出AVI变得毫无意义-知道为什么吗?

C++ CSource过滤器上的SetMediaTime使输出AVI变得毫无意义-知道为什么吗?,c++,video,directshow,avi,C++,Video,Directshow,Avi,更新:我最初发布的代码实际上没有重现问题;我真诚的道歉没有证实它。奇数行为的关键是一帧结束和下一帧开始之间的小增量(300个单位=30微秒)。由于某些原因,我使用的捕获硬件报告的帧速率与它提供捕获帧及其时间戳时实际显示的帧速率不同。我已经更新了下面的源代码,给出了如何模仿这种行为的示例。 我为directshow编写了一个简单的“假”图像源过滤器,源于CSource。它工作得很好。但我注意到一些奇怪的事情,我无法解释。我的缓冲区看起来像: const REFERENCE_TIME TIME_PE

更新:我最初发布的代码实际上没有重现问题;我真诚的道歉没有证实它。奇数行为的关键是一帧结束和下一帧开始之间的小增量(300个单位=30微秒)。由于某些原因,我使用的捕获硬件报告的帧速率与它提供捕获帧及其时间戳时实际显示的帧速率不同。我已经更新了下面的源代码,给出了如何模仿这种行为的示例。

我为directshow编写了一个简单的“假”图像源过滤器,源于CSource。它工作得很好。但我注意到一些奇怪的事情,我无法解释。我的缓冲区看起来像:

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帧和。没有“掉落”的帧。


未丢弃的帧位于0、326、552、878、1104、1430、17561982。它们之间的三角洲是326226326226226326226326226。这肯定让我摸不着头脑……

AVI帧索引将以流标头中定义的固定帧速率为每一帧创建条目。例如,创建300 fps轨迹,然后使用1 fps频率创建源时间戳采样。生成的文件将在它们之间放置您的帧和299个(零长度)帧。这就是你应该得到的

也就是说,您的时间戳代码片段是正确的(您可以用一种简单的方式来完成)。然而,同样重要的是,流本身应用的速率是多少,这是从媒体类型派生出来的,您在问题中没有包括这一类型,您应该检查它

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

我今天偶然发现了这一点,我认为这在一定程度上解释了问题。从中,

或者,过滤器还可以为样本指定介质时间。 在视频流中,媒体时间表示帧数

因此,多路复用器需要媒体时间,如果存在的话,比如0-1,1-2,2-3。当媒体时间设置为连续块时,如0-100001000000-200000,我猜是mux处理。但当有差距时,根据微软提供的文档,我可以理解事情是如何分崩离析的

但知道这一点实际上是非常强大的。由于AVI文件是一种恒定的帧速率格式,您可以在需要时使用mediatimes来传输帧丢弃。我已经开始为此成功地使用它们

仅供参考,几天前我又在一个项目中尝试了基于实际时间的mediatimes,结果并不是很有趣,directshow图形会因为E_失败而停止


tl;dr仅使用媒体时间传送帧号,至少传送到AVI mux。

感谢您的输入。所以我实际上得到了大约275个非零长度的下降帧,在非下降帧之间。掉帧的数量奇怪地在225和325之间切换(而不是别的)。所有的一切都设置为60帧/秒的赛道,这正是我得到的。在没有设置MediaTime的情况下,输出60帧可以得到1秒的剪辑。使用SetMediaTime,它给了我大约276秒的剪辑,每一个真实的帧都出现了,中间只有一堆无用的丢弃帧。我添加了一个古怪的AVI文件属性的屏幕截图,以及关于未丢弃帧位置的详细信息。这很奇怪。你们为什么不把你们当前的时间计数器按和媒体类型相同的固定增量提前呢?与代码片段中的166000/166666相反。您仍然需要将媒体类型打印输出包含在问题中。很抱歉延迟回复@Roman R.-外出度假。我清理了上面的代码,以便更准确地表示我正在做的事情-帧持续时间总是相同的,并且用于适当地创建CMediaType-我还包括了用于设置CMediaType的代码。我想我跳过了重要部分:“如果我简单地删除
SetMediaTime
,输出AVI是完全正常的。”
SetMediaTime
是可选的,而且常常根本不存在。我想AVI mux可能会以某种特殊的方式处理它:。我想远程设置媒体时间只是为了适应AVI mux,因为它以这种不可预测的方式响应媒体时间。一个有趣的观察是现有的捕获过滤器设置媒体时间,没有任何问题。它设置的介质时间中从不存在任何间隔,即使在样本上设置的常规时间中可能存在间隔/重叠。所以我认为最大的收获是连续样本上的媒体时间不能有间隔。