I use Media Foundation to write files. The program is as follows:
#include <windows.h>
#include <windowsx.h>
#include <comdef.h>
#include <stdio.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <Mferror.h>
#include <mfplay.h>
#include <codecapi.h>
#include <atlcomcli.h>
#pragma comment(lib, "ole32")
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mfuuid")
int main()
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = MFStartup(MF_VERSION);
IMFMediaType *pMediaType;
IMFMediaType *pMediaTypeOut;
IMFSourceReader *pSourceReader;
IMFAttributes *pAttributes;
IMFSinkWriter *pSinkWriter;
IMFMediaType *pCurrentMediaType;
LONGLONG nDruration = 300000000;
hr = MFCreateSourceReaderFromURL(L"./in.mp4", NULL, &pSourceReader);
pSourceReader->SetStreamSelection(MF_SOURCE_READER_FIRST_VIDEO_STREAM, TRUE);
IMFMediaType *pPartialType;
MFCreateMediaType(&pPartialType);
hr = pPartialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
hr = pPartialType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
hr = pSourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr, pPartialType);
hr = pSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, &pPartialType);
hr = pSourceReader->SetStreamSelection(MF_SOURCE_READER_FIRST_VIDEO_STREAM, TRUE);
hr = MFCreateMediaType(&pMediaTypeOut);
hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_HEVC);
hr = pMediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, 512000);
hr = pMediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_FRAME_RATE, 30, 1);
hr = MFSetAttributeSize(pMediaTypeOut, MF_MT_FRAME_SIZE, 720, 480);
hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 16, 9);
DWORD nWriterStreamIndex = -1;
hr = MFCreateSinkWriterFromURL(L"./out.mp4", NULL, NULL, &pSinkWriter);
hr = pSinkWriter->AddStream(pMediaTypeOut, &nWriterStreamIndex);
hr = pSinkWriter->SetInputMediaType(nWriterStreamIndex, pPartialType, NULL);
LONGLONG SampleDuration = 0L;
hr = pSinkWriter->BeginWriting();
for (;;)
{
DWORD nStreamIndex, nStreamFlags;
LONGLONG nTime;
IMFSample *pSample;
hr = pSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
&nStreamIndex,
&nStreamFlags,
&nTime,
&pSample);
printf("FLAGS %d\n", nStreamFlags);
printf("TIME %lld\n", nTime);
if (nStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM)
{
break;
}
//Update media type, when current media tye changed.
if (nStreamFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) {
pSourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, MF_SOURCE_READER_CURRENT_TYPE_INDEX, &pCurrentMediaType);
printf("MediaType changed\n");
pSourceReader->SetStreamSelection(MF_SOURCE_READER_FIRST_VIDEO_STREAM, TRUE);
hr = pSinkWriter->SetInputMediaType(nWriterStreamIndex, pCurrentMediaType, NULL);
continue;
}
pSample->GetSampleDuration(&SampleDuration);
if (nTime >= nDruration)
{
break;
}
// Calculate new timestamp of sample when this sample is written on output file
if (nTime + SampleDuration >= nDruration)
{
SampleDuration = nDruration - nTime;
pSample->SetSampleDuration(SampleDuration);
}
pSample->SetSampleTime(nTime);
if (FAILED(hr)) {
printf("ReadSample Error...\n");
return hr;
}
//write sample
if (pSample)
{
OutputDebugString(L"Write sample...\n");
hr = pSinkWriter->WriteSample(
nWriterStreamIndex,
pSample
);
if (FAILED(hr)) {
pSample->Release();
printf("WriteSample Error...\n");
return hr;
}
pSample->Release();
pSample = NULL;
}
}
hr = pSinkWriter->Finalize();
return 0;
}
After running the program, I get the file "out.mp4". I play it with WMP (Window Media Player) and don't close it.
Then I run the program again, it crashes when calling MFCreateSinkWriterFromURL.
hr = HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION): The process cannot access the file because it is being used by another process.
I think the cause is out.mp4 being locked by WMP.
3. However, if I add a code to delete the out.mp4 file before calling MFCreateSinkWriterFromURL, the program will always run successfully. At this point, WMP will sometimes report an error.
if (PathFileExistsW("./out.mp4")) {
if (!DeleteFileW("./out.mp4")) {
LERROR("msg", "delete output file failed", "error", GetLastError());
}
}
I try to delete a video file using File Explorer while the same file is still played by WMP. Results similar to step 3.
I did all the above steps again and replaced WMP with Movie&TV (on Win10), the results were slightly different.
In step 2, the program does not give error while the file is still playing on Movie&TV
In step 3 and step 4, we can always delete the file and Movie&TV gives error
0x8007002 - The item is missing or we can't get to it
So I have questions here:
- With WMP, why can't we overwrite files (using Media Foundation) but we can delete files?
- With Movie&TV, why can't the program detect the file being played by Movie&TV? Also like WMP, why can't the file be deleted while it's being played with Movie&TV ?