使用 IMFSinkWriter 编码的视频的播放速度会根据宽度而变化
Playback speed of video encoded with IMFSinkWriter changes based on width
我正在使用Win32s接收器编写器制作屏幕录像机(没有音频(,将一系列位图编码为MP4文件。
由于某种原因,视频播放速度(似乎(与视频宽度成比例地增加。
从这篇文章中,我发现这很可能是因为我计算缓冲区大小不正确。这里的区别在于,一旦音频缓冲区大小的计算正确,他们的视频播放问题就得到了修复,但由于我根本不编码任何音频,所以我不确定从中获取什么。
我也尝试阅读有关缓冲区如何工作的信息,但我真的不知道缓冲区大小是如何导致不同播放速度的。
这是整个代码的粘贴,除了缓冲区大小和/或帧索引/持续时间之外,我真的无法追踪问题。
即:根据成员变量m_width
的宽度(以像素为单位(,播放速度会发生变化。那是;宽度越高,视频播放速度越快,反之亦然。
下面是两个视频示例:3840x1080 和 640x1080,请注意系统时钟。 Imugr 不保留文件的原始分辨率,但我在上传前仔细检查,该程序确实创建了声称分辨率的文件。
rtStart 和 rtDuration 就是这样定义的,它们都是 MP4File 类的私有成员。
LONGLONG rtStart = 0;
UINT64 rtDuration;
MFFrameRateToAverageTimePerFrame(m_FPS, 1, &rtDuration);
这是更新 rtStart 的地方,位图的各个位被传递给帧编写器。
将LPVOID
对象移至私有成员,希望能提高性能。现在,不需要每次追加帧时都进行堆分配。
HRESULT MP4File::AppendFrame(HBITMAP frame)
{
HRESULT hr = NULL;
if (m_isInitialFrame)
{
hr = InitializeMovieCreation();
if (FAILED(hr))
return hr;
m_isInitialFrame = false;
}
if (m_hHeap && m_lpBitsBuffer) // Makes sure buffer is initialized
{
BITMAPINFO bmpInfo;
bmpInfo.bmiHeader.biBitCount = 0;
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Get individual bits from bitmap and loads it into the buffer used by `WriteFrame`
GetDIBits(m_hDC, frame, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS);
bmpInfo.bmiHeader.biCompression = BI_RGB;
GetDIBits(m_hDC, frame, 0, bmpInfo.bmiHeader.biHeight, m_lpBitsBuffer, &bmpInfo, DIB_RGB_COLORS);
hr = WriteFrame();
if (SUCCEEDED(hr))
{
rtStart += rtDuration;
}
}
return m_writeFrameResult = hr;
}
最后,帧编写器实际将位加载到缓冲区中,然后写入接收器写入器。
HRESULT MP4File::WriteFrame()
{
IMFSample *pSample = NULL;
IMFMediaBuffer *pBuffer = NULL;
const LONG cbWidth = 4 * m_width;
const DWORD cbBufferSize = cbWidth * m_height;
BYTE *pData = NULL;
// Create a new memory buffer.
HRESULT hr = MFCreateMemoryBuffer(cbBufferSize, &pBuffer);
// Lock the buffer and copy the video frame to the buffer.
if (SUCCEEDED(hr))
{
hr = pBuffer->Lock(&pData, NULL, NULL);
}
if (SUCCEEDED(hr))
{
hr = MFCopyImage(
pData, // Destination buffer.
cbWidth, // Destination stride.
(BYTE*)m_lpBitsBuffer, // First row in source image.
cbWidth, // Source stride.
cbWidth, // Image width in bytes.
m_height // Image height in pixels.
);
}
if (pBuffer)
{
pBuffer->Unlock();
}
// Set the data length of the buffer.
if (SUCCEEDED(hr))
{
hr = pBuffer->SetCurrentLength(cbBufferSize);
}
// Create a media sample and add the buffer to the sample.
if (SUCCEEDED(hr))
{
hr = MFCreateSample(&pSample);
}
if (SUCCEEDED(hr))
{
hr = pSample->AddBuffer(pBuffer);
}
// Set the time stamp and the duration.
if (SUCCEEDED(hr))
{
hr = pSample->SetSampleTime(rtStart);
}
if (SUCCEEDED(hr))
{
hr = pSample->SetSampleDuration(rtDuration);
}
// Send the sample to the Sink Writer and update the timestamp
if (SUCCEEDED(hr))
{
hr = m_pSinkWriter->WriteSample(m_streamIndex, pSample);
}
SafeRelease(&pSample);
SafeRelease(&pBuffer);
return hr;
}
有关编码的几个详细信息:
帧- 率:30帧
- 比特率: 15 000 000
- 输出编码格式:H264 (MP4(
对我来说,这种行为是有道理的。
见 https://github.com/mofo7777/Stackoverflow/tree/master/ScreenCaptureEncode
我的程序使用 DirectX9 而不是 GetDIBits,但行为是相同的。尝试使用不同的屏幕分辨率使用此程序,以确认此行为。
我确认,通过我的程序,视频播放速度与视频宽度(以及视频高度(成比例增加。
为什么?
要复制的数据更多,时间更长。和错误的采样时间/采样持续时间。
使用 30 FPS,表示每个帧 33.3333333 毫秒:
- Do GetDIBits, MFCopyImage, WriteSample 在 33.3333333 ms 结束...
- 你是否准确地在 33.3333333 毫秒处写入每一帧......不。
所以只做 rtStart += rtDuration 是错误的,因为你没有在这个时候准确地捕获和写入屏幕。GetDIBits/DirectX9无法以30 FPS的速度处理,相信我。为什么Microsoft提供Windows桌面复制(仅适用于Windows 8/10(?
关键是延迟。
你知道GetDIBits,MFCopyImage和WriteSample需要多长时间吗?你应该知道,要理解问题所在。通常,它需要超过 33.3333333 毫秒。但它是可变的。 您必须知道它才能将正确的 FPS 调整到编码器。但是您还需要在正确的时间编写样本。
如果您使用 5-10 FPS 而不是 30 FPS 的MF_MT_FRAME_RATE,您会发现它更逼真,但不是最佳的。
例如,使用 IMFPresentationClock 来处理正确的 WriteSample 时间。
- C++取消引用指针.为什么会发生变化
- 为什么在读取文件大小时文件IO速度会发生变化
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- 文件系统:复制功能的速度秘诀是什么
- 为什么cudaMemGetInfo报告设备内存总量的变化
- 学习多线程C++:添加线程不会使执行速度更快,即使它看起来应该
- 如何避免LED在循环状态变化中闪烁?
- 当 I2C 值在C++中发生变化时收到通知
- Gtkmm 会不断检查 Gtk::日历上的所选日期是否发生变化
- 在C++中使用并行化的预期速度是多少(不是 OpenMp,而是 <thread>)
- 当我调用对象的方法时,对象的成员会发生变化
- 两个连续的 OpenMP 并行区域会相互减慢速度
- 为什么向量内部的指针在从函数返回时会发生变化?
- 查找标准::hash_map与标准::矢量的速度
- 加快在C++中读取/处理日志文件的速度
- 使用 IMFSinkWriter 编码的视频的播放速度会根据宽度而变化
- 滤除噪音和速度值的变化
- C++ 矩阵产品:速度提高,变化不大
- 抑制下载速度的变化
- 未修改的DLL速度在加载整个解决方案时变化