AVI file with inverted image

Shyamala Devi 1 Reputation point
2020-09-14T07:34:14.15+00:00

I've used the sample from https://learn.microsoft.com/en-us/windows/win32/medfound/tutorial--using-the-sink-writer-to-encode-video and modified it to create as avi file instead of mp4. This works as desired if the input FPS is 30. If I modify it to anything other than 30 the image gets inverted.

Below is my code. First halve of the pixels are set to green and the second to red.
for (DWORD i = 0; i < VIDEO_PELS; ++i)
{
if(i< VIDEO_PELS/2)// Set first halve to green
videoFrameBuffer[i] = 0x0000FF00;
else// Set second halve to red
videoFrameBuffer[i] = 0x00FF0000;
}
If I change INPUT_VIDEO_FPS to 25, the resultant image is red and then green. If it is 30, then resultant image is green and then red.

Could anybody please help me with this?

Regards,
Shyamala

#include <Windows.h>  
#include <mfapi.h>  
#include <mfidl.h>  
#include <Mfreadwrite.h>  
#include <mferror.h>  
  
#pragma comment(lib, "mfreadwrite")  
#pragma comment(lib, "mfplat")  
#pragma comment(lib, "mfuuid")  
  
template <class T> void SafeRelease(T **ppT)  
{  
	if (*ppT)  
	{  
		(*ppT)->Release();  
		*ppT = NULL;  
	}  
}  
  
// Format constants  
const UINT32 VIDEO_WIDTH = 1920;  
const UINT32 VIDEO_HEIGHT = 1080;  
const UINT32 VIDEO_FPS = 30;  
const UINT32 INPUT_VIDEO_FPS = 25;  
const UINT64 VIDEO_FRAME_DURATION = 10 * 1000 * 1000 / VIDEO_FPS;  
const UINT32 VIDEO_BIT_RATE = 800000;  
const GUID   VIDEO_ENCODING_FORMAT = MFVideoFormat_WMV1;  
const GUID   VIDEO_INPUT_FORMAT = MFVideoFormat_RGB32;  
const UINT32 VIDEO_PELS = VIDEO_WIDTH * VIDEO_HEIGHT;  
const UINT32 VIDEO_FRAME_COUNT = 20 * VIDEO_FPS;  
  
// Buffer to hold the video frame data.  
DWORD videoFrameBuffer[VIDEO_PELS];  
  
HRESULT InitializeSinkWriter(IMFSinkWriter **ppWriter, DWORD *pStreamIndex)  
{  
	*ppWriter = NULL;  
	*pStreamIndex = NULL;  
  
	IMFSinkWriter   *pSinkWriter = NULL;  
	IMFMediaType    *pMediaTypeOut = NULL;  
	IMFMediaType    *pMediaTypeIn = NULL;  
	DWORD           streamIndex;  
	  
	IMFAttributes *pContainerAttrs = NULL;  
  
	//Initialize IMFAttribute variable to hold 2 properties  
	const int noOfAttributes = 2;  
	HRESULT hr = MFCreateAttributes(&pContainerAttrs, noOfAttributes);  
	if (FAILED(hr))  
	{  
		return hr;  
	}  
  
	//Set the transcoding container to ASF   
	hr = pContainerAttrs->SetGUID(MF_TRANSCODE_CONTAINERTYPE, MFTranscodeContainerType_ASF);  
	if (FAILED(hr))  
	{  
		return hr;;  
	}  
  
	hr = pContainerAttrs->SetUINT32(MF_TRANSCODE_ADJUST_PROFILE, MF_TRANSCODE_ADJUST_PROFILE_DEFAULT);  
	if (FAILED(hr))  
	{  
		return hr;  
	}  
	hr = MFCreateSinkWriterFromURL(L"output.avi", NULL, pContainerAttrs, &pSinkWriter);  
  
	// Set the output media type.  
	if (SUCCEEDED(hr))  
	{  
		hr = MFCreateMediaType(&pMediaTypeOut);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, VIDEO_ENCODING_FORMAT);  
	}  
	UINT32 bitRate = VIDEO_WIDTH * 3 * VIDEO_HEIGHT * 8 * 30;  
	if (bitRate > VIDEO_BIT_RATE)  
	{  
		bitRate = VIDEO_BIT_RATE;  
	}  
	hr = pMediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, VIDEO_BIT_RATE);  
  
	if (SUCCEEDED(hr))  
	{  
		hr = pMediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = MFSetAttributeSize(pMediaTypeOut, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_FRAME_RATE, VIDEO_FPS, 1);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = pSinkWriter->AddStream(pMediaTypeOut, &streamIndex);  
	}  
  
	// Set the input media type.  
	if (SUCCEEDED(hr))  
	{  
		hr = MFCreateMediaType(&pMediaTypeIn);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = pMediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, VIDEO_INPUT_FORMAT);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = pMediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = MFSetAttributeSize(pMediaTypeIn, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);  
	}  
	UINT32 denominator = 1000;  
	UINT32 numerator = INPUT_VIDEO_FPS * denominator;  
	if (SUCCEEDED(hr))  
	{  
		hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_FRAME_RATE, INPUT_VIDEO_FPS, 1);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);  
	}  
	if (SUCCEEDED(hr))  
	{  
		hr = pSinkWriter->SetInputMediaType(streamIndex, pMediaTypeIn, NULL);  
	}  
  
	// Tell the sink writer to start accepting data.  
	if (SUCCEEDED(hr))  
	{  
		hr = pSinkWriter->BeginWriting();  
	}  
  
	// Return the pointer to the caller.  
	if (SUCCEEDED(hr))  
	{  
		*ppWriter = pSinkWriter;  
		(*ppWriter)->AddRef();  
		*pStreamIndex = streamIndex;  
	}  
  
	SafeRelease(&pSinkWriter);  
	SafeRelease(&pMediaTypeOut);  
	SafeRelease(&pMediaTypeIn);  
	return hr;  
}  
  
HRESULT WriteFrame(  
	IMFSinkWriter *pWriter,  
	DWORD streamIndex,  
	const LONGLONG& rtStart        // Time stamp.  
)  
{  
	IMFSample *pSample = NULL;  
	IMFMediaBuffer *pBuffer = NULL;  
  
	const LONG cbWidth = 4 * VIDEO_WIDTH;  
	const DWORD cbBuffer = cbWidth * VIDEO_HEIGHT;  
  
	BYTE *pData = NULL;  
  
	// Create a new memory buffer.  
	HRESULT hr = MFCreateMemoryBuffer(cbBuffer, &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*)videoFrameBuffer,    // First row in source image.  
			cbWidth,                    // Source stride.  
			cbWidth,                    // Image width in bytes.  
			VIDEO_HEIGHT                // Image height in pixels.  
		);  
	}  
	if (pBuffer)  
	{  
		pBuffer->Unlock();  
	}  
  
	// Set the data length of the buffer.  
	if (SUCCEEDED(hr))  
	{  
		hr = pBuffer->SetCurrentLength(cbBuffer);  
	}  
  
	// 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(VIDEO_FRAME_DURATION);  
	}  
  
	// Send the sample to the Sink Writer.  
	if (SUCCEEDED(hr))  
	{  
		hr = pWriter->WriteSample(streamIndex, pSample);  
	}  
  
	SafeRelease(&pSample);  
	SafeRelease(&pBuffer);  
	return hr;  
}  
  
void main()  
{  
	  
	for (DWORD i = 0; i < VIDEO_PELS; ++i)  
	{ 		  
		if(i< VIDEO_PELS/2)// Set first halve to green  
		videoFrameBuffer[i] = 0x0000FF00;  
		else// Set second halve to red  
			videoFrameBuffer[i] = 0x00FF0000;  
	}  
  
	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);  
	if (SUCCEEDED(hr))  
	{  
		hr = MFStartup(MF_VERSION);  
		if (SUCCEEDED(hr))  
		{  
			IMFSinkWriter *pSinkWriter = NULL;  
			DWORD stream;  
  
			hr = InitializeSinkWriter(&pSinkWriter, &stream);  
			if (SUCCEEDED(hr))  
			{  
				// Send frames to the sink writer.  
				LONGLONG rtStart = 0;  
  
  
				for (DWORD i = 0; i < VIDEO_FRAME_COUNT; ++i)  
				{  
					hr = WriteFrame(pSinkWriter, stream, rtStart);  
					if (FAILED(hr))  
					{  
						break;  
					}  
					rtStart += VIDEO_FRAME_DURATION;  
				}  
			}  
			if (SUCCEEDED(hr))  
			{  
				hr = pSinkWriter->Finalize();  
			}  
			SafeRelease(&pSinkWriter);  
			MFShutdown();  
		}  
		CoUninitialize();  
	}  
}  
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,647 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,753 questions
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Viorel 117.9K Reputation points
    2020-09-15T11:10:42.937+00:00

    Documentation says that “The sink writer does not support video resizing, frame-rate conversion, or audio resampling, unless these functions are provided by the encoder”. Maybe the desired feature is unavailable in your scenario.

    Perhaps it is possible to use the recommended “Frame Rate Converter DSP”. Or simply duplicate or drop input frames in your code.

    1 person found this answer helpful.
    0 comments No comments

  2. Rita Han - MSFT 2,166 Reputation points
    2020-09-15T02:30:18.037+00:00

    Hello @Shyamala Devi ,

    You define a new constant INPUT_VIDEO_FPS and set its value to 25. But you forget to replace all VIDEO_FPS with INPUT_VIDEO_FPS. For example, in these two lines still use old value VIDEO_FPS (30).

     const UINT64 VIDEO_FRAME_DURATION = 10 * 1000 * 1000 / VIDEO_FPS;      
     const UINT32 VIDEO_FRAME_COUNT = 20 * VIDEO_FPS;  
    

    Please directly set VIDEO_FPS to 25 instead of using INPUT_VIDEO_FPS. It will work.

    Thank you!


    If the answer is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.