Guest User

Code for SO issue: Playback speed of video encoded with IMFS

a guest
Jan 20th, 2020
140
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.65 KB | None | 0 0
  1. ///////////////// MP4File.h /////////////////
  2. #pragma once
  3.  
  4. #include <Windows.h>
  5. #include <mfapi.h>
  6. #include <mfidl.h>
  7. #include <Mfreadwrite.h>
  8. #include <mferror.h>
  9. #include <string>
  10.  
  11. #pragma comment(lib, "mfreadwrite")
  12. #pragma comment(lib, "mfplat")
  13. #pragma comment(lib, "mfuuid")
  14.  
  15. class MP4File
  16. {
  17. private:
  18.     HDC             m_hDC;
  19.     IMFSinkWriter*  m_pSinkWriter;
  20.     HRESULT         m_writeFrameResult;
  21.     LONGLONG        rtStart; // m_currentFrameTimestamp
  22.     LPVOID          m_lpBitsBuffer;
  23.     HANDLE          m_hHeap;
  24.     DWORD           m_streamIndex;
  25.  
  26.     // File details
  27.     std::wstring    m_name;
  28.     UINT32          m_width;
  29.     UINT32          m_height;
  30.     UINT32          m_FPS;
  31.     UINT64          rtDuration; // m_frameDuration
  32.     UINT32          m_bitrate;
  33.     GUID            m_encoding;
  34.     GUID            m_inputEncoding;
  35.  
  36.     bool            m_isInitialFrame;
  37.  
  38.     void ReleaseMemory();
  39.  
  40.     HRESULT InitializeMovieCreation();
  41.  
  42.     HRESULT InitializeSinkWriter();
  43.  
  44.     HRESULT WriteFrame();
  45.  
  46.     template <class T> void SafeRelease(T** ppT)
  47.     {
  48.         if (*ppT)
  49.         {
  50.             (*ppT)->Release();
  51.             *ppT = NULL;
  52.         }
  53.     }
  54. public:
  55.     MP4File(std::wstring, UINT32, UINT32);
  56.     ~MP4File();
  57.  
  58.     HRESULT AppendFrame(HBITMAP);
  59.  
  60.     HRESULT Finalize();
  61. };
  62. ///////////////// MP4File.cpp /////////////////
  63. #include "stdafx.h"
  64. #include "MP4File.h"
  65.  
  66. MP4File::MP4File(std::wstring fileName, UINT32 videoWidth, UINT32 videoHeight) :
  67.     m_hDC(NULL),
  68.     m_pSinkWriter(NULL),
  69.     m_writeFrameResult(NULL),
  70.     rtStart(0),
  71.     m_lpBitsBuffer(NULL),
  72.     m_hHeap(NULL),
  73.  
  74.     m_name(fileName),
  75.     m_width(videoWidth),
  76.     m_height(videoHeight),
  77.     m_FPS(30),
  78.     rtDuration(0),//10 * 1000 * 1000 / 30),
  79.     m_bitrate(15000000),
  80.     m_encoding(MFVideoFormat_H264),
  81.     m_inputEncoding(MFVideoFormat_RGB32),
  82.  
  83.     m_isInitialFrame(true)
  84. {
  85.     HRESULT hr = MFFrameRateToAverageTimePerFrame(30, 1, &rtDuration);
  86. }
  87.  
  88. MP4File::~MP4File()
  89. {
  90.     ReleaseMemory();
  91. }
  92.  
  93. void MP4File::ReleaseMemory()
  94. {
  95.     if (m_hDC)
  96.     {
  97.         DeleteDC(m_hDC);
  98.         m_hDC = NULL;
  99.     }
  100.  
  101.     if (m_lpBitsBuffer)
  102.     {
  103.         HeapFree(m_hHeap, HEAP_NO_SERIALIZE, m_lpBitsBuffer);
  104.         m_lpBitsBuffer = NULL;
  105.     }
  106.  
  107.     if (m_hHeap)
  108.     {
  109.         HeapDestroy(m_hHeap);
  110.         m_hHeap = NULL;
  111.     }
  112.  
  113.     SafeRelease(&m_pSinkWriter);
  114.     MFShutdown();
  115.     CoUninitialize();
  116. }
  117.  
  118. HRESULT MP4File::InitializeMovieCreation()
  119. {
  120.     HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  121.  
  122.     if (SUCCEEDED(hr))
  123.     {
  124.         hr = MFStartup(MF_VERSION);
  125.         if (SUCCEEDED(hr))
  126.         {
  127.             hr = InitializeSinkWriter();
  128.             if (SUCCEEDED(hr))
  129.             {
  130.                 m_hDC = CreateCompatibleDC(NULL);
  131.                 if (m_hDC == NULL)
  132.                 {
  133.                     return E_FAIL;
  134.                 }
  135.  
  136.                 m_hHeap = HeapCreate(HEAP_NO_SERIALIZE, m_width * m_height * 4, 0);
  137.                 if (m_hHeap == NULL)
  138.                 {
  139.                     return E_FAIL;
  140.                 }
  141.  
  142.                 m_lpBitsBuffer = HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE, m_width * m_height * 4);
  143.                 if (m_lpBitsBuffer == NULL)
  144.                 {
  145.                     return E_FAIL;
  146.                 }
  147.  
  148.                 return S_OK;
  149.             }
  150.         }
  151.     }
  152.  
  153.     return E_FAIL;
  154. }
  155.  
  156. HRESULT MP4File::InitializeSinkWriter()
  157. {
  158.     IMFSinkWriter   *pSinkWriter = NULL;
  159.     IMFMediaType    *pMediaTypeOut = NULL;
  160.     IMFMediaType    *pMediaTypeIn = NULL;
  161.     DWORD           streamIndex;
  162.  
  163.     HRESULT hr = MFCreateSinkWriterFromURL((m_name + L".mp4").c_str(), NULL, NULL, &pSinkWriter);
  164.  
  165.     // Set the output media type.
  166.     if (SUCCEEDED(hr))
  167.     {
  168.         hr = MFCreateMediaType(&pMediaTypeOut);
  169.     }
  170.     if (SUCCEEDED(hr))
  171.     {
  172.         hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
  173.     }
  174.     if (SUCCEEDED(hr))
  175.     {
  176.         hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, m_encoding);
  177.     }
  178.     if (SUCCEEDED(hr))
  179.     {
  180.         hr = pMediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, m_bitrate);
  181.     }
  182.     if (SUCCEEDED(hr))
  183.     {
  184.         hr = pMediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
  185.     }
  186.     if (SUCCEEDED(hr))
  187.     {
  188.         hr = MFSetAttributeSize(pMediaTypeOut, MF_MT_FRAME_SIZE, m_width, m_height);
  189.     }
  190.     if (SUCCEEDED(hr))
  191.     {
  192.         hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_FRAME_RATE, m_FPS, 1);
  193.     }
  194.     if (SUCCEEDED(hr))
  195.     {
  196.         hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
  197.     }
  198.     if (SUCCEEDED(hr))
  199.     {
  200.         hr = pSinkWriter->AddStream(pMediaTypeOut, &streamIndex);
  201.     }
  202.  
  203.     // Set the input media type.
  204.     if (SUCCEEDED(hr))
  205.     {
  206.         hr = MFCreateMediaType(&pMediaTypeIn);
  207.     }
  208.     if (SUCCEEDED(hr))
  209.     {
  210.         hr = pMediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
  211.     }
  212.     if (SUCCEEDED(hr))
  213.     {
  214.         hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, m_inputEncoding);
  215.     }
  216.     if (SUCCEEDED(hr))
  217.     {
  218.         hr = pMediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
  219.     }
  220.     if (SUCCEEDED(hr))
  221.     {
  222.         hr = MFSetAttributeSize(pMediaTypeIn, MF_MT_FRAME_SIZE, m_width, m_height);
  223.     }
  224.     if (SUCCEEDED(hr))
  225.     {
  226.         hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_FRAME_RATE, m_FPS, 1);
  227.     }
  228.     if (SUCCEEDED(hr))
  229.     {
  230.         hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
  231.     }
  232.     if (SUCCEEDED(hr))
  233.     {
  234.         hr = pSinkWriter->SetInputMediaType(streamIndex, pMediaTypeIn, NULL);
  235.     }
  236.  
  237.     // Tell the sink writer to start accepting data.
  238.     if (SUCCEEDED(hr))
  239.     {
  240.         hr = pSinkWriter->BeginWriting();
  241.     }
  242.  
  243.     // Assign pointers to members upon complete success
  244.     if (SUCCEEDED(hr))
  245.     {
  246.         m_pSinkWriter = pSinkWriter;
  247.         m_pSinkWriter->AddRef();
  248.         m_streamIndex = streamIndex;
  249.  
  250.     }
  251.  
  252.     SafeRelease(&pSinkWriter);
  253.     SafeRelease(&pMediaTypeOut);
  254.     SafeRelease(&pMediaTypeIn);
  255.     return hr;
  256. }
  257.  
  258. HRESULT MP4File::WriteFrame()
  259. {
  260.     IMFSample *pSample = NULL;
  261.     IMFMediaBuffer *pBuffer = NULL;
  262.  
  263.     const LONG cbWidth = 4 * m_width;
  264.     const DWORD cbBufferSize = cbWidth * m_height;
  265.  
  266.     BYTE *pData = NULL;
  267.  
  268.     // Create a new memory buffer.
  269.     HRESULT hr = MFCreateMemoryBuffer(cbBufferSize, &pBuffer);
  270.  
  271.     // Lock the buffer and copy the video frame to the buffer.
  272.     if (SUCCEEDED(hr))
  273.     {
  274.         hr = pBuffer->Lock(&pData, NULL, NULL);
  275.     }
  276.     if (SUCCEEDED(hr))
  277.     {
  278.         hr = MFCopyImage(
  279.             pData,                      // Destination buffer.
  280.             cbWidth,                    // Destination stride.
  281.             (BYTE*)m_lpBitsBuffer,      // First row in source image.
  282.             cbWidth,                    // Source stride.
  283.             cbWidth,                    // Image width in bytes.
  284.             m_height                    // Image height in pixels.
  285.         );
  286.     }
  287.     if (pBuffer)
  288.     {
  289.         pBuffer->Unlock();
  290.     }
  291.  
  292.     // Set the data length of the buffer.
  293.     if (SUCCEEDED(hr))
  294.     {
  295.         hr = pBuffer->SetCurrentLength(cbBufferSize);
  296.     }
  297.  
  298.     // Create a media sample and add the buffer to the sample.
  299.     if (SUCCEEDED(hr))
  300.     {
  301.         hr = MFCreateSample(&pSample);
  302.     }
  303.     if (SUCCEEDED(hr))
  304.     {
  305.         hr = pSample->AddBuffer(pBuffer);
  306.     }
  307.  
  308.     // Set the time stamp and the duration.
  309.     if (SUCCEEDED(hr))
  310.     {
  311.         hr = pSample->SetSampleTime(rtStart);
  312.     }
  313.     if (SUCCEEDED(hr))
  314.     {
  315.         hr = pSample->SetSampleDuration(rtDuration);
  316.     }
  317.  
  318.     // Send the sample to the Sink Writer and update the timestamp
  319.     if (SUCCEEDED(hr))
  320.     {
  321.         hr = m_pSinkWriter->WriteSample(m_streamIndex, pSample);
  322.     }
  323.  
  324.     SafeRelease(&pSample);
  325.     SafeRelease(&pBuffer);
  326.  
  327.     return hr;
  328. }
  329.  
  330. HRESULT MP4File::AppendFrame(HBITMAP frame)
  331. {
  332.     HRESULT hr = NULL;
  333.  
  334.     if (m_isInitialFrame)
  335.     {
  336.         hr = InitializeMovieCreation();
  337.  
  338.         if (FAILED(hr))
  339.             return hr;
  340.  
  341.         m_isInitialFrame = false;
  342.     }
  343.  
  344.     if (m_hHeap && m_lpBitsBuffer) // Makes sure buffer is initialized
  345.     {
  346.         BITMAPINFO bmpInfo;
  347.         bmpInfo.bmiHeader.biBitCount = 0;
  348.         bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  349.  
  350.         // Get individual bits from bitmap and loads it into the buffer used by `WriteFrame`
  351.         GetDIBits(m_hDC, frame, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS);
  352.         bmpInfo.bmiHeader.biCompression = BI_RGB;
  353.         GetDIBits(m_hDC, frame, 0, bmpInfo.bmiHeader.biHeight, m_lpBitsBuffer, &bmpInfo, DIB_RGB_COLORS);
  354.  
  355.         hr = WriteFrame();
  356.    
  357.         if (SUCCEEDED(hr))
  358.         {
  359.             rtStart += rtDuration;
  360.         }
  361.     }
  362.  
  363.     return m_writeFrameResult = hr;
  364. }
  365.  
  366. HRESULT MP4File::Finalize()
  367. {
  368.     if (SUCCEEDED(m_writeFrameResult))
  369.     {
  370.         m_pSinkWriter->Finalize();
  371.  
  372.         m_isInitialFrame = true;
  373.  
  374.         return S_OK;
  375.     }
  376.  
  377.     return E_FAIL;
  378. }
  379. ///////////////// main.cpp /////////////////
  380. #include "stdafx.h"
  381. #include "MP4File.h"
  382.  
  383. #include <iostream>
  384.  
  385. const UINT32 VIDEO_WIDTH = 3840;
  386. const UINT32 VIDEO_HEIGHT = 1080;
  387.  
  388. HBITMAP GetBitmap()
  389. {
  390.     HDC hScreen = GetDC(NULL);
  391.     HDC hDC = CreateCompatibleDC(hScreen);
  392.  
  393.     HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, VIDEO_WIDTH, VIDEO_HEIGHT);
  394.     HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
  395.     BOOL bRet = BitBlt(hDC, 0, 0, VIDEO_WIDTH, VIDEO_HEIGHT, hScreen, -1920, 0, SRCCOPY);
  396.  
  397.     SelectObject(hDC, old_obj);
  398.     DeleteDC(hDC);
  399.     ReleaseDC(NULL, hScreen);
  400.  
  401.     return hBitmap;
  402. }
  403.  
  404. int main()
  405. {
  406.     HRESULT hr;
  407.     MP4File file(L"test", VIDEO_WIDTH, VIDEO_HEIGHT);
  408.  
  409.     while (true)
  410.     {
  411.         HBITMAP hBitmap = GetBitmap();
  412.         hr = file.AppendFrame(hBitmap);
  413.  
  414.         if (FAILED(hr))
  415.             break;
  416.  
  417.         if (GetAsyncKeyState(0x51))
  418.         {
  419.             std::cout << "Stopped recording";
  420.             break;
  421.         }
  422.     }
  423.  
  424.     file.Finalize();
  425.    
  426.     return 0;
  427. }
Add Comment
Please, Sign In to add comment