更新:我最初发布的代码实际上并未重现该问题;谨对此致以诚挚的歉意。一帧结束与下一帧开始之间的小变化(300单位= 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帧和。没有“掉落”的帧。
c++ - CSource筛选器上的SetMediaTime使输出AVI无意义-知道为什么吗?-LMLPHP

非丢失的帧分别为0、326、552、878、1104、1430、1756、1982。它们之间的增量为326、226、326、226、226、326、326、226。头...

最佳答案

AVI帧索引将以流头中定义的固定帧速率为每个帧提供条目。例如,您创建300 fps的轨道,然后以1 fps的频率为源时间戳记样本。生成的文件将包含您的帧和之间的299掉帧(零长度)帧。这就是您应该得到的。

那就是说您的时间戳记代码片段是正确的(您可以通过一种直接的方式来完成)。然而,重要的是将什么速率应用于流本身,这是从媒体类型派生的,您没有将其包括在问题中,应该检查哪个类型。

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

关于c++ - CSource筛选器上的SetMediaTime使输出AVI无意义-知道为什么吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48572737/

10-11 02:39