Media Foundation Webcam live capture freezes in low light condition - c++

We are building a video communication software. We are using Media Foundation to obtain the live Stream. We use the IMFSourceReadder to perform the capture.
The sequence of call looks like:
hr = pAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, m_pwszSymbolicLink);
hr = MFCreateDeviceSourceActivate(pAttributes, &avdevice);
hr = avdevice->ActivateObject(__uuidof(IMFMediaSource), (void**) &m_mediaSource);
hr = m_mediaSource->CreatePresentationDescriptor(&pPD);
hr = pPD->GetStreamDescriptorByIndex(m_streamIdx, &fSelected, &pSD);
hr =
// we select the best native MediaType enumerating the source reader
pHandler->SetCurrentMediaType(m_bestNativeType);
hr = pAttributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, FALSE);
hr = pAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE);
hr = MFCreateSourceReaderFromMediaSource(m_mediaSource, pAttributes, &m_reader);
Then we start to read the frame SYNCHRONOUSLY in a separate thread using
m_reader->ReadSample()
When we need to stop the device or reconfigure it, we stop the thread (by setting an flag and exiting the thread). We call the following
hr = m_mediaSource->Stop();
m_mediaSource->Shutdown();
SafeRelease(&m_mediaSource);
SafeRelease(&m_reader);
The software can be out ouf call. There, it captures the webcam video in VGA format and display it on screen. In call, it selects the best capture format depending on the negociated call quality and restarts the capture.
The issues that we are experiencing are the following: some cameras freeze sometimes in low light conditions (low fps output). It can happen right away at the beginning of the call or during the call.
When it freezes, one of the two things can happen (not sure which one)
m_reader->ReadSample() fails repetitively with MF_E_OPERATION_CANCELLED error code
m_reader->ReadSample() returns often producing more than 80 frames per seconds producing same frozen image.
When we hang up the device is reconfigured back to VGA capture and works fine.
Does someone struggled with Media Foundation on the same issue?

You wrote that web camera "freez" - produce low frame rate while capture image with low light condition. The result of it that controller of web camera take more time on exposition of photo matrix in automatic mode. It allows improve quality of image by increasing frame duration. So, it is special feature of hardware part. it is possible to switch such behavior of camera from auto mode on manual mode of parameter
Code::Result VideoCaptureDevice::setParametrs(CamParametrs parametrs){
ResultCode::Result result = ResultCode::VIDEOCAPTUREDEVICE_SETPARAMETRS_ERROR;
if(pLocalSource)
{
unsigned int shift = sizeof(Parametr);
Parametr *pParametr = (Parametr *)(&settings);
Parametr *pPrevParametr = (Parametr *)(&prevParametrs);
CComPtrCustom<IAMVideoProcAmp> pProcAmp;
HRESULT hr = pLocalSource->QueryInterface(IID_PPV_ARGS(&pProcAmp));
if (SUCCEEDED(hr))
{
for(unsigned int i = 0; i < 10; i++)
{
if(pPrevParametr[i].CurrentValue != pParametr[i].CurrentValue || pPrevParametr[i].Flag != pParametr[i].Flag)
hr = pProcAmp->Set(VideoProcAmp_Brightness + i, pParametr[i].CurrentValue, pParametr[i].Flag);
}
}
else
{
result = ResultCode::VIDEOCAPTUREDEVICE_SETPARAMETRS_SETVIDEOPROCESSOR_ERROR;
goto finish;
}
CComPtrCustom<IAMCameraControl> pProcControl;
hr = pLocalSource->QueryInterface(IID_PPV_ARGS(&pProcControl));
if (SUCCEEDED(hr))
{
for(unsigned int i = 0; i < 7; i++)
{
if(pPrevParametr[10 + i].CurrentValue != pParametr[10 + i].CurrentValue || pPrevParametr[10 + i].Flag != pParametr[10 + i].Flag)
hr = pProcControl->Set(CameraControl_Pan+i, pParametr[10 + i].CurrentValue, pParametr[10 + i].Flag);
}
}
else
{
result = ResultCode::VIDEOCAPTUREDEVICE_SETPARAMETRS_SETVIDEOCONTROL_ERROR;
goto finish;
}
result = ResultCode::OK;
prevParametrs = parametrs.settings;
}finish:
if(result != ResultCode::OK)
DebugPrintOut::getInstance().printOut(L"VIDEO CAPTURE DEVICE: Parametrs of video device cannot be set!!!\n");
return result;
}
where:
struct Parametr
{
long CurrentValue;
long Min;
long Max;
long Step;
long Default;
long Flag;
Parametr();
};
struct CamParametrs
{
Parametr Brightness;
Parametr Contrast;
Parametr Hue;
Parametr Saturation;
Parametr Sharpness;
Parametr Gamma;
Parametr ColorEnable;
Parametr WhiteBalance;
Parametr BacklightCompensation;
Parametr Gain;
Parametr Pan;
Parametr Tilt;
Parametr Roll;
Parametr Zoom;
Parametr Exposure;
Parametr Iris;
Parametr Focus;
};
More code you can find on site:
Capturing Live-video from Web-camera on Windows 7 and Windows 8
However, using of IMFSourceReader can be not effective. Media Foundation model uses async interaction - after sending the request into the media source code must listen responding from media source with new frame or some other info. Method with direct calling m_reader->ReadSample() cannot be effective - you faced with it. Method m_reader->ReadSample() can be effective with reading frames from video file while delay can be very low, but for web camera I can advice use topology - session binding, like in my code Capturing Live-video from Web-camera on Windows 7 and Windows 8
Regards,
Evgeny Pereguda

The question description leaves an impression that you do things in a somewhat chaotic way and the resulting freeze is not necessarily caused by Media Foundation or camera.
Use of media source and source reader are certainly the right way to access a camera and it provides efficient way to capture video, both synchronously and asynchronously.
However, your incomplete code snippets show that you create a media source, then source reader, and then you keep dealing with media source directly. Well, you are not supposed to do this. Once you created a source reader, it will manage media source for you: you don't need Stop, Shutdown calls. Your calling that and other methods might bring confusion that results in incorrect source reader behavior.
That is, either you deal with a media source, or you plug it into Media Session or Source Reader and use this higher level API.
Also note that if/when you experience a freeze, you are interested to break in with debugged and locate threads that indicate freeze position.

Related

Media Foundation get IMFMediaType of a topology node

I've written an app that's based on the SampleGrabberSink example. My application is actually working as I want but one function I needed to perform was to get the video resolution when the .mp4 source changed part way into the file. I eventually worked out how to do it but it seems very long winded and I suspect there must be a simpler way.
In the sample below is there a way to shorten the code block handling the MESessionStreamSinkFormatChanged case? It seems like what's taking nearly 40 lines of code (counting initialisation and cleanup) should take 1 or 2.
HRESULT RunSession(IMFMediaSession *pSession, IMFTopology *pTopology, OnVideoResolutionChangedFunc onVideoResolutionChanged)
{
IMFMediaEvent *pEvent = NULL;
IMFTopologyNode *pNode = nullptr;
IMFStreamSink *pStreamSink = nullptr;
IUnknown *pNodeObject = NULL;
IMFMediaTypeHandler *pMediaTypeHandler = nullptr;
IMFMediaType *pMediaType = nullptr;
PROPVARIANT var;
PropVariantInit(&var);
HRESULT hr = S_OK;
CHECK_HR(hr = pSession->SetTopology(0, pTopology));
CHECK_HR(hr = pSession->Start(&GUID_NULL, &var));
while(1)
{
HRESULT hrStatus = S_OK;
MediaEventType met;
CHECK_HR(hr = pSession->GetEvent(0, &pEvent));
CHECK_HR(hr = pEvent->GetStatus(&hrStatus));
CHECK_HR(hr = pEvent->GetType(&met));
if(FAILED(hrStatus))
{
printf("Session error: 0x%x (event id: %d)\n", hrStatus, met);
hr = hrStatus;
goto done;
}
else
{
//printf("Session event: event id: %d\n", met);
switch(met)
{
case MESessionStreamSinkFormatChanged:
//std::cout << "MESessionStreamSinkFormatChanged." << std::endl;
{
MF_TOPOLOGY_TYPE nodeType;
UINT64 outputNode{0};
GUID majorMediaType;
UINT64 videoResolution{0};
UINT32 stride{0};
// This seems a ridiculously convoluted way to extract the change to the video resolution. There may
// be a simpler way but then again this is the Media Foundation and COM!
CHECK_HR_ERROR(pEvent->GetUINT64(MF_EVENT_OUTPUT_NODE, &outputNode), "Failed to get ouput node from media changed event.");
CHECK_HR_ERROR(pTopology->GetNodeByID(outputNode, &pNode), "Failed to get topology node for output ID.");
CHECK_HR_ERROR(pNode->GetObject(&pNodeObject), "Failed to get the node's object pointer.");
CHECK_HR_ERROR(pNodeObject->QueryInterface(IID_PPV_ARGS(&pStreamSink)), "Failed to get media stream sink from activation object.");
CHECK_HR_ERROR(pStreamSink->GetMediaTypeHandler(&pMediaTypeHandler), "Failed to get media type handler from stream sink.");
CHECK_HR_ERROR(pMediaTypeHandler->GetCurrentMediaType(&pMediaType), "Failed to get current media type.");
CHECK_HR_ERROR(pMediaType->GetMajorType(&majorMediaType), "Failed to get major media type.");
if(majorMediaType == MFMediaType_Video)
{
CHECK_HR_ERROR(pMediaType->GetUINT64(MF_MT_FRAME_SIZE, &videoResolution), "Failed to get new video resolution.");
CHECK_HR_ERROR(pMediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, &stride), "Failed to get the new stride.");
std::cout << "Media session video resolution changed to width " << std::to_string(HI32(videoResolution))
<< " and height " << std::to_string(LO32(videoResolution))
<< " and stride " << stride << "." << std::endl;
if(onVideoResolutionChanged != nullptr) {
onVideoResolutionChanged(HI32(videoResolution), LO32(videoResolution), stride);
}
}
break;
}
default:
break;
}
}
if(met == MESessionEnded)
{
break;
}
SafeRelease(&pEvent);
SafeRelease(&pNode);
SafeRelease(&pStreamSink);
SafeRelease(&pNodeObject);
SafeRelease(&pMediaTypeHandler);
SafeRelease(&pMediaType);
}
done:
SafeRelease(&pEvent);
SafeRelease(&pNode);
SafeRelease(&pStreamSink);
SafeRelease(&pNodeObject);
SafeRelease(&pMediaTypeHandler);
SafeRelease(&pMediaType);
return hr;
}
// This seems a ridiculously convoluted way to extract the change to the video resolution. There may
// be a simpler way but then again this is the Media Foundation and COM!
The code looks good. You don't need to do everything on resolution change - you can retrieve media type handler just once and keep the pointer when it's needed.
On the entertaining comment above I would say the following. Just like in the case of DirectShow, Sample Grabber is the way to cut corners hard and do something against the design of the pipeline. Almost everyone out there loved DirectShow Sample Grabber and so the future of Media Foundation Sample Grabber could be if there was enough of people who developed for Media Foundation in first place.
Resolution change is generally the business of primitives, i.e. source-transform, transform-transform, and transform-sink connections. Even in this scenario you are getting the notification on resolution change out of band (it's asynchronous notification for you) and you are lucky Media Foundation and its Sample Grabber are so flexible that you can handle this in first place.
To implement this reliably you would normally need a custom media sink, but Sample Grabber lets you cut a corner even at this time.
With custom sink implementation you are guaranteed that you don't receive media sample with new resolution before you agree on new resolution in first place (and you can reject it, of course). With MESessionStreamSinkFormatChanged however the event is posted for async retrieval and Sample Grabber continues processing, so technially you can have grabber callbacks with frames of new resolution before you get the session event.
If in your real application you create output node using stream sink and not media sink activate as in your example above, you would not need to retrieve media type handle using topology nodes - you would be able to pull it directly.

Windows Desktop Duplication API taking a long time

I am using the windows desktop duplication API to record my screen in windows 10. I am however having some issues with performance. When playing a video using google chrome and attempting to record the time it takes to record the screen fluctuates from 15ms to 45ms. I wanted to be able to record at at least 30fps, and I know the desktop duplication api is capable of doing it. Anyways here is the code I used to actually capture the screen:
processor->hr = processor->lDeskDupl->AcquireNextFrame(0, &processor->lFrameInfo, &processor->lDesktopResource);
if (processor->hr == DXGI_ERROR_WAIT_TIMEOUT) {
processor->lDeskDupl->ReleaseFrame();
return false;
}
if (FAILED(processor->hr)) {
processor->lDeskDupl->ReleaseFrame();
return false;
}
// QI for ID3D11Texture2D
processor->hr = processor->lDesktopResource->QueryInterface(IID_PPV_ARGS(&processor->lAcquiredDesktopImage));
if (FAILED(processor->hr)) {
processor->lDeskDupl->ReleaseFrame();
return false;
}
processor->lDesktopResource.Release();
if (processor->lAcquiredDesktopImage == nullptr) {
processor->lDeskDupl->ReleaseFrame();
return false;
}
processor->lImmediateContext->CopyResource(processor->lGDIImage, processor->lAcquiredDesktopImage);
processor->lAcquiredDesktopImage.Release();
processor->lDeskDupl->ReleaseFrame();
// Copy image into CPU access texture
processor->lImmediateContext->CopyResource(processor->lDestImage, processor->lGDIImage);
// Copy from CPU access texture to bitmap buffer
D3D11_MAPPED_SUBRESOURCE resource;
processor->subresource = D3D11CalcSubresource(0, 0, 0);
processor->lImmediateContext->Map(processor->lDestImage, processor->subresource, D3D11_MAP_READ_WRITE, 0, &resource);
BYTE* sptr = reinterpret_cast<BYTE*>(resource.pData);
BYTE* dptr = processor->pBuf;
UINT lRowPitch = min(processor->lBmpRowPitch, resource.RowPitch);
for (int i = 0; i < processor->lOutputDuplDesc.ModeDesc.Height; i++) {
memcpy_s(dptr, processor->lBmpRowPitch, sptr, lRowPitch);
sptr += resource.RowPitch;
dptr += processor->lBmpRowPitch;
}
It is important to note that this is the specific section that is taking 15ms-45ms to complete every cycle. The memcpy loop at the bottom accounts for about 2ms of that time usually so I know that that is not responsible for the time it is taking here. Also AcquireNextFrame's timeout is set to zero so it returns nearly immediately. Any help would be greatly appreciated! The code pasted here was adapted from this: https://gist.github.com/Xirexel/a69ade44df0f70afd4a01c1c9d9e02cd
You're not using the API in optimal way. Read remarks in ReleaseFrame API documentation:
For performance reasons, we recommend that you release the frame just before you call the IDXGIOutputDuplication::AcquireNextFrame method to acquire the next frame. When the client does not own the frame, the operating system copies all desktop updates to the surface. This can result in wasted GPU cycles if the operating system updates the same region for each frame that occurs.
You are not doing what's written there, you release frames as soon as you copy.

DirectShow CSourceStream::FillBuffer unpredictable number of calls after Pause and Seek to the first frame

I have a Directshow File Source Filter which has audio and frame output pins. It is written in C++ based on this tutorial on MSDN. My filter opens the video by using Medialooks MFormats SDK and provides raw data to output pins. Two pins are directly connecting to renderer filters when they are rendered.
The problem occurs when I run the graph, pause the video and seek to the frame number 0. After a call to ChangeStart method in output frame pin, sometimes FillBuffer is called three times and frame 1 is shown on the screen instead of 0. When it is called two times, it shows the correct frame which is the frame 0.
Output pins are inherited from CSourceStream and CSourceSeeking classes. Here is my FillBuffer and ChangeStart methods of the output frame pin;
FillBuffer Method
HRESULT frame_pin::FillBuffer(IMediaSample *sample)
{
CheckPointer(sample, E_POINTER);
BYTE *frame_buffer;
sample->GetPointer(&frame_buffer);
// Check if the downstream filter is changing the format.
CMediaType *mt;
HRESULT hr = sample->GetMediaType(reinterpret_cast<AM_MEDIA_TYPE**>(&mt));
if (hr == S_OK)
{
auto new_width = reinterpret_cast<VIDEOINFOHEADER2*>(mt->pbFormat)->bmiHeader.biWidth;
auto old_witdh = reinterpret_cast<VIDEOINFOHEADER2*>(m_mt.pbFormat)->bmiHeader.biWidth;
if(new_width != old_witdh)
format_changed_ = true;
SetMediaType(mt);
DeleteMediaType(mt);
}
ASSERT(m_mt.formattype == FORMAT_VideoInfo2);
VIDEOINFOHEADER2 *vih = reinterpret_cast<VIDEOINFOHEADER2*>(m_mt.pbFormat);
CComPtr<IMFFrame> mf_frame;
{
CAutoLock lock(&shared_state_);
if (source_time_ >= m_rtStop)
return S_FALSE;
// mf_reader_ is a member external SDK instance which gets the frame data with this function call
hr = mf_reader_->SourceFrameConvertedGetByNumber(&av_props_, frame_number_, -1, &mf_frame, CComBSTR(L""));
if (FAILED(hr))
return hr;
REFERENCE_TIME start, stop = 0;
start = stream_time_;
stop = static_cast<REFERENCE_TIME>(tc_.get_stop_time() / m_dRateSeeking);
sample->SetTime(&start, &stop);
stream_time_ = stop;
source_time_ += (stop - start);
frame_number_++;
}
if (format_changed_)
{
CComPtr<IMFFrame> mf_frame_resized;
mf_frame->MFResize(eMFCC_YUY2, std::abs(vih->bmiHeader.biWidth), std::abs(vih->bmiHeader.biHeight), 0, &mf_frame_resized, CComBSTR(L""), CComBSTR(L""));
mf_frame = mf_frame_resized;
}
MF_FRAME_INFO mf_frame_info;
mf_frame->MFAllGet(&mf_frame_info);
memcpy(frame_buffer, reinterpret_cast<BYTE*>(mf_frame_info.lpVideo), mf_frame_info.cbVideo);
sample->SetActualDataLength(static_cast<long>(mf_frame_info.cbVideo));
sample->SetSyncPoint(TRUE);
sample->SetPreroll(FALSE);
if (discontinuity_)
{
sample->SetDiscontinuity(TRUE);
discontinuity_ = FALSE;
}
return S_OK;
}
ChangeStart Method
HRESULT frame_pin::ChangeStart()
{
{
CAutoLock lock(CSourceSeeking::m_pLock);
tc_.reset();
stream_time_ = 0;
source_time_ = m_rtStart;
frame_number_ = static_cast<int>(m_rtStart / frame_lenght_);
}
update_from_seek();
return S_OK;
}
From the Microsoft DirectShow documentation:
The CSourceSeeking class is an abstract class for implementing
seeking in source filters with one output pin.
CSourceSeeking is not recommended for a filter with more than one
output pin. The main issue is that only one pin should respond to
seeking requests. Typically this requires communication among the pins
and the filter.
And you have two output pins in your source filter.
The CSourceSeeking class can be extended to manage more than one output pin with custom coding. When seek commands come in they'll come through both input pins so you'll need to decide which pin is controlling seeking and ignore seek commands arriving at the other input pin.

Media Foundation onReadSample wrong size of returned sample

I am working on translating a capture library from DirectShow to MediaFoundation. The capture library seemed to work quite well but I face a problem with an integrated webcam on a tablet running Windows 8 32 bit.
When enumerating the capture format (as explained in Media Foundation documentation), I got the following supported format for the camera:
0 : MFVideoFormat_NV12, resolution : 448x252, framerate : 30000x1001
1 : MFVideoFormat_YUY2, resolution : 448x252, framerate : 30000x1001
2 : MFVideoFormat_NV12, resolution : 640x360, framerate : 30000x1001
3 : MFVideoFormat_YUY2, resolution : 640x360, framerate : 30000x1001
4 : MFVideoFormat_NV12, resolution : 640x480, framerate : 30000x1001
5 : MFVideoFormat_YUY2, resolution : 640x480, framerate : 30000x1001
I then set the capture format, in this case the one at index 5, using the following function, as described in the example:
hr = pHandler->SetCurrentMediaType(pType);
This function executed without error. The camera should thus be configured to capture in YUY2 with a resolution of 640*480.
In the onReadSample callback, I should receive a sample with a buffer of size :
640 * 480 * sizeof(unsigned char) * 2 = 614400 //YUY2 is encoded on 2 bytes
However, I got a sample with a buffer of size 169344. Here below is a part of the callback function.
HRESULT SourceReader::OnReadSample(
HRESULT hrStatus,
DWORD dwStreamIndex,
DWORD dwStreamFlags,
LONGLONG llTimeStamp,
IMFSample *pSample // Can be NULL
)
{
EnterCriticalSection(&m_critsec);
if (pSample)
{
DWORD expectedBufferSize = 640*480*1*2; // = 614400 (hard code for the example)
IMFMediaBuffer* buffer = NULL;
hr = pSample->ConvertToContiguousBuffer(&buffer);
if (FAILED(hr))
{
//...
goto done;
}
DWORD byteLength = 0;
BYTE* pixels = NULL;
hr = buffer->Lock(&pixels, NULL, &byteLength);
//byteLength is 169344 instead of 614400
if (byteLength > 0 && byteLength == expectedBufferSize)
{
//do someting with the image, but never comes here because byteLength is wrong
}
//...
Any advice why I get a sample of size 169344 ?
Thanks in advance
Thanks Mgetz for your answer.
I checked the value of MF_MT_INTERLACE_MODE of the media type and it appears that the video stream contains progressive frames. The value of MF_MT_INTERLACE_MODE returns MFVideoInterlace_Progressive.
hr = pHandler->SetCurrentMediaType(m_pType);
if(FAILED(hr)){
//
}
else
{
//get info about interlacing
UINT32 interlaceFormat = MFVideoInterlace_Unknown;
m_pType->GetUINT32(MF_MT_INTERLACE_MODE, &interlaceFormat);
//...
So the video stream is not interlaced. I checked again in the onReadSample the value of MFSampleExtension_Interlaced to see if the sample is interlaced or not and it appears that the sample is interlaced.
if (pSample && m_bCapture)
{
//check if interlaced
UINT32 isSampleInterlaced = 0;
pSample->GetUINT32(MFSampleExtension_Interlaced, &isSampleInterlaced);
if(isSampleInterlaced)
{
//enters here
}
How it is possible that the stream is progressive and that the sample is interlaced? I double checked the value of MF_MT_INTERLACE_MODE in the onReadSample callback as well and it still gives me the value MFT_INPUT_STREAM_WHOLE_SAMPLES.
Concerning your first suggestion, I didn't way to force the flag MFT_INPUT_STREAM_WHOLE_SAMPLES on the input stream.
Thanks in advance
I still face the issue and I am now investigating on the different streams available.
According to the documentation, each media source provides a presentation descriptor from which we can get the streams available. To get the presentation descriptor, we have to call:
HRESULT hr = pSource->CreatePresentationDescriptor(&pPD);
I then request the streams available using the IMFPresentationDescriptor::GetStreamDescriptorCount function:
DWORD nbrStream;
pPD->GetStreamDescriptorCount(&nbrStream);
When requesting this information on the frontal webcam on an ACER tablet running windows 8, I got that three streams are available. I looped over these streams, requested their MediaTypeHandler and checked the MajorType. The three streams have for major type : MFMediaType_Video, so all the streams are video streams. When listing the media type available on the different streams, I got that all the streams support capture at 640x480. (some of the streams have more available media types).
I tested to select each of the different streams and the appropriate format type (the framework did not return any error), but I still do not receive the correct sample in the callback function...
Any advice to progress on the issue?
Finally found the issue: I had to set the media type on the source reader directly, using SourceReader->SetCurrentMediaType(..). That did the trick!
Thanks for your help!
Without knowing what the input media type descriptor is we can largely only speculate, but the most likely answer is you are saying you can handle the stream even though MFT_INPUT_STREAM_WHOLE_SAMPLES is not set on the input stream.
The next most likely cause is interlacing in which case each frame would be complete but not full resolution which you are assuming. Regardless you should verify the ENTIRE media type descriptor before accepting it.
Finally found the issue: I had to set the media type on the source reader directly, using SourceReader->SetCurrentMediaType(..). That did the trick!
Thanks for your help!

Detect end of Video with IMediaSeeking

I am playing a video to get some screens using DirectShow.
I am doing this in a loop by calling IMediaControl->Run, IVMRWindowlessControl->GetCurrentImage and then a IMediaSeeking->SetPositions.
The problem is that I cannot detect when the video is over. IMediaSeeking->SetPositions returns always same value (S_FALSE). IMediaControl->Runalso returns always S_FALSE. I have also tried IMediaEvent->GetEvent after the call to IMediaControl->Run to check for EC_COMPLETE but instead returns (always) EC_CLOCK_CHANGED.
How can I detect the end of video ? Thanks
UPDATE: Doing something like
long eventCode = 0;
LONG_PTR ptrParam1 = 0;
LONG_PTR ptrParam2 = 0;
long timeoutMs = INFINITE;
while (SUCCEEDED(pEvent->GetEvent(&eventCode, &ptrParam1, &ptrParam1, timeoutMs)))
{
if (eventCode == EC_COMPLETE)
{
break;
}
// Free the event data.
hr = pEvent->FreeEventParams(eventCode, ptrParam1, ptrParam1);
if (FAILED(hr))
{
break;
}
}
blocks after few events: 0x53 (EC_VMR_RENDERDEVICE_SET), 0x0D (EC_CLOCK_CHANGED), 0x0E (EC_PAUSED), next call to GetEvent is blocking and the video is rendered (played frame by frame) in my IVideoWindow
You should be doing IMediaEvent->GetEvent, however note you will be receiving various events, not only EC_CLOCK_CHANGED. Keep receiving and you are to get EC_COMPLETE. Step 6: Handle Graph Events on MSDN explains this in detail.
Check the state of the filter graph with IMediaControl::GetState and see if it is stopped. You can also get the duration of the video from IMediaSeeking::GetDuration that you may find helpful.
Another option is to use event signaling. This event processing can be off-threaded.