VIdeoMixingFilter : end of video file error.

Greg
2010-04-22
2013-01-10
  • Greg

    Greg - 2010-04-22

    Hi,

    I've recently been using the video mixing filter and I have noticed that each time the video file reaches the end an error occurs.

    Do you have any hint where do I have to look inside the code to fix this ?

    Thank you.

     
  • Ralf Globisch

    Ralf Globisch - 2010-05-01

    Hi Greg,
    I just did some basic testing with some AVI files of various lengths and also with some webcams.
    It doesn't crash on my side but I still need to work on the code to detect the end of stream correctly.

    Can you provide some more information about what you files, media types (RGB24 or 32) you're using, etc. so that I can try to replicate it. Anymore info on in which method it crashes, i.e. if you hook up the debugger?

    As a hint, the CMultiIOBaseFilter::EndOfStream is called once for each video that ends. However it currently doesn't do anything, so I can't see how that method would cause a crash…You could insert a breakpoint in that method and see if it crashes before/after.

    Regards,
    Ralf

     
  • Greg

    Greg - 2010-05-01

    Hi Ralf,

    Thanks for your reply. The application doesn't exactly crash. I'm still able to run it but only after I press cancel  on a pop up window that appears (Monday I will post the exact message I get as I do not remember now). Also I noticed that if I close the window (showing the concatenated pictures) or if I press stop before the end of the movie, I can still press play and it will start to play again. But each time the movie reaches the end I get this window error message.

    For now I'm working with RBG24. The machine is 64 bits with XP. When I compile in 32 bits I get the same kind of error.

    I'll try to do what you said.  Thanks a lot.

    Greg

    ps : also I noticed that the two concatenated pictures (two inputs used) are not synchronized sometimes.

     
  • Greg

    Greg - 2010-05-03

    Hi Ralp,

    to have the error window appears I have to run the graph and reach the end of the video files (exact same number of frames). Then I have to press stop (I cannot rerun the graph yet).  The following is the message I get :

    "The graph was unable to complete pause within 10 seconds. Press Retry to wait another 10 seconds for completion or Cancel to attempt to stop the graph"

    Greg

     
  • Ralf Globisch

    Ralf Globisch - 2010-05-03

    Hi Greg,
    Ok, I have just managed to replicate what you describe, will have to look for the reason on the weekend. Seems like it could be some kind of lock. I use Monogram's Graphstudio for testing and the behaviour you describe only seems to happen with GraphEdit. Will have to get back to you on that one.

    On the synchronisation issue: The mixer was written to be generic, if you have more information such as you know that the frame rates are equal, etc. you could look at the VideoMixingBase::Receive method and override it with the following logic:
    - Receive frame from input one: store frame/perhaps lock sample
    - Receive frame from input two: mix samples, generate output sample and release both samples.

    Hope that helps,
    Ralf

     
  • Greg

    Greg - 2010-05-03

    Great. At least I'm not alone now :)

    For the synchronization, I've overrided the "Receive" method. This method calls (depending of the input index) the methods "ReceiveFirstSample" and  "ReceiveSecondSample". And both of them call "GenerateOutputSample" (maybe this is the problem).

    In this last method there is a condition to test if the two inputs exist. I think its here that I have to put a special condition on the frame numbers (should be equal to proceed)
    I use two avi files as inputs. Apparently it's not possible to retrieve the frame number. Do you have an idea ?
    I am not at all familiar with locks.

    Thanks again for your help. It s really appreciated.

    Greg

     
  • Ralf Globisch

    Ralf Globisch - 2010-05-03

    Hi Greg,
    No problem, glad to help :)
    I' ve sorted out your end of stream problem, do an update from subversion and that code should be fine now.

    If the two framerates are the same I would rather only call GenerateOutputSample once a sample has been received from both inputs.
    On reception of a sample you could simply store a pointer to the sample and add a ref by calling pSample->AddRef: this should effectively prevent the upstream filter from sending a new sample downstream.

    After you've done that check if both sample* are non NULL and if so, generate an output sample i.e. a sample is only generated once a sample has been received from both inputs. Then once you're finished with the output sample and have sent it downstream, don't forget to unlock the MediaSamples calling pSample->Release before the method exits and set your media sample pointers to NULL so that the next round of samples can be received. Best thing is to play around with this, till you've tweaked it for your purposes.

    Cheers,
    Ralf

     
  • Greg

    Greg - 2010-05-04

    Hi Ralf,

    the filter can now stop normally. It's great. Thanks.

    Concerning the synchronization now, I've try what you said but without success. I put a pSample->AddRef()  each time after the buffer is copied to m_pSampleBuffers and m_pSampleBuffers in the methods ReceiveFirstSample( IMediaSample *pSample ) and ReceiveSecondSample( IMediaSample *pSample ).
    I also call only one time the method GenerateOutputSample(IMediaSample *pSample, int nIndex). Only in ReceiveSecondSample( IMediaSample *pSample ).

    The problem is how do I call pSample->Release() for each of the two inputs (I guess in the same moment) after the output sample and has been sent downstream. There is only one pSample to GenerateOutputSample(IMediaSample *pSample, int nIndex).
    Everything seems to be in the if statement :

    if (m_pSampleBuffers && m_pSampleBuffers)
    {
         m_pPicConcat->Concat(m_pSampleBuffers, m_pSampleBuffers, pBufferOut);
    }

    Also how to I set the media sample pointers to NULL ?

    It s not straightforward to me :)

    Have a good day.

    Greg

     
  • Ralf Globisch

    Ralf Globisch - 2010-05-04

    Greg, you would have to add some members to the filter, either IMediaSample* m_pSample1 and IMediaSample* m_pSample2 or an array of two samples. As the respective receive method is called, store the sample as well as calling AddRef.
    Then after delivery of the output sample, call

    m_pSample1->Release();
    m_pSample1 = NULL;
    m_pSample2->Release().
    m_pSample2 = NULL;

    I'm hoping this approach will work for you, but you'll have to try it out and tweak it. Hope that helps.

     
  • Greg

    Greg - 2010-05-04

    Ralf,

    I've try since this morning but without success.

    This is my code edited with your recommendation for the method Receive. Can you tell me if you see something I forgot. The graph block.

    HRESULT VideoMixingFilter::Receive(IMediaSample *pSample, int nIndex)
    {
    CAutoLock lck(&m_csReceive);
    ASSERT(nIndex >= 0);
    ASSERT(nIndex < m_vInputPins.size());
    ASSERT(pSample);
    ASSERT(m_vOutputPins != NULL);

    if (nIndex == 0)
    {
    BYTE *pBuffer = NULL;
    HRESULT hr = pSample->GetPointer(&pBuffer);
    if (FAILED(hr))
    {
    return hr;
    }

    // Copy the buffer

    memcpy(m_pSampleBuffers, pBuffer, m_nSampleSizes);
    m_pSample0 = pSample ;
    m_pSample0->AddRef() ;

    }
    else
    {
    BYTE *pBuffer = NULL;
    HRESULT hr = pSample->GetPointer(&pBuffer);
    if (FAILED(hr))
    {
    return hr;
    }

    // Copy the buffer

    memcpy(m_pSampleBuffers, pBuffer, m_nSampleSizes);
    m_pSample1 = pSample ;
    m_pSample1->AddRef() ;
    }

    if (m_pSample0 && m_pSample1)
    {
    GenerateOutputSample(pSample, nIndex) ;

    m_pSample0->Release();
    m_pSample0 = NULL;
    m_pSample1->Release();
    m_pSample1 = NULL;
    }

    return S_OK ;

    }

    Maybe I missed something.

    Thank you again for your time.

    Greg

     
  • Ralf Globisch

    Ralf Globisch - 2010-05-05

    Hi Greg,
    I'll have a look on the weekend, it looks ok so if it doesn't work maybe we'll have to think of something else.
    What does the video do when you run it?

     
  • Greg

    Greg - 2010-05-05

    Hi Ralf,

    thanks. For my test I used the same video. Apparently with the initial code (the code I posted earlier doens't work) of the filter, when I run it frame by frame it seems ok until I reach the last frame. You can see clearly when the graph ends that the first image is in advance of one frame. You can also press the button stop and pause to see a wrong image initialization.
    Maybe the problem is when the first movie reaches the end but I am not sure.
    I am not at my office until the end of next week unfortunately. So I won't be able to run some more tests for a couple of days.

    Once again Ralf thank you for your time.

    Greg

     
  • Greg

    Greg - 2010-05-13

    Hi Ralf,

    I dunno if you had time to look through the code but this morning I found a solution to my problem. I finally used two global variables (cpt0 and cpt1) as a counter. Here is the code I suggest  for the Receive method ( there is probably a better solution) :

    HRESULT  VideoMixingFilter::Receive(IMediaSample *pSample, int nIndex)
    {

    ASSERT(nIndex >= 0);
    ASSERT(nIndex < m_vInputPins.size());
    ASSERT(pSample);
    ASSERT(m_vOutputPins != NULL);

    BYTE *pBuffer = NULL;
    HRESULT hr = pSample->GetPointer(&pBuffer);
    if (FAILED(hr))
    {
    return hr;
    }

    if (nIndex == 0)
    {
    // Copy the buffer
    memcpy(m_pSampleBuffers, pBuffer, m_nSampleSizes);
    cpt0++ ;
    }
    else
    {
    // Copy the buffer
    memcpy(m_pSampleBuffers, pBuffer, m_nSampleSizes);
    cpt1++;
    }

    if (cpt0 == cpt1)
    {
    GenerateOutputSample(pSample, nIndex) ;

    cpt0 = 0 ;
    cpt1 = 0 ;
    }

    return S_OK ;
    }

    The two variables cpt0 and cpt1 are initialized in the constructor. It seems to work correctly but I noticed some strange behaviors when I use the "pause'" and "stop" button. I think I need to reset the variable cpt0 and cpt1 each time I press these button. What do you think ?

    Greg

     
  • Greg

    Greg - 2010-05-13

    Concerning my problem, it appends only when I press the_ pause_ button and immediately after the stop button. If I press play after that manipulation the graph doesn't start.

    Greg

     
  • Ralf Globisch

    Ralf Globisch - 2010-05-14

    Great Greg, I'm glad you found a solution. Had a brief look on the weekend, but ran into some locking issues which I'll have to have a closer look at when trying to lock the samples. I'll have to spend some more time on it if you don't come right.
    The one concern I had when looking at the code you posted is what happens when two samples get delivered from one pin before any samples can get delivered from the other (if that's possible). Then cpt0 would never equal cpt1 resulting in skipped output samples: does that happen at all?

    As for the strange behaviour, best thing is to check the filter state to make sure that everything is correct. And then when you press play again, the debugger should hit the pause method again before resuming play. I'll have a closer look soon, just got some deliverables at work I need to take care of first…

     
  • Ralf Globisch

    Ralf Globisch - 2010-05-14

    Ok, check if a break point in the Pause method gets hit, when resuming play. If it does, you can step through the methods until you find where it's blocking or what's causing the graph not to play. If it doesn't get that far, also put a breakpoint in the stop  method and then step through the code to see if you can figure out where it's blocking. Or if that breakpoint doesn't get hit, maybe it blocks in pause? Hope that helps getting you started.

     
  • Greg

    Greg - 2010-05-14

    Hi Ralf,

    you're right. I ran some more tests and I noticed the behavior you described : some frames may be skipped. But it does not append each time I run the graph. I do not understand why the graph would load two images from the first source before loading one image from the second source, but it seems to append. At that point I dunno how to define reading priorities.

    Also the pause button seems working fine. The only matter is with the stop button. I suppose that (sometimes) when I press it  the  variable cpt1 is not yet incremented so when I press play cpt0 and cpt1 have a different value. That would explains why nothing is displayed.

    I will try with the breakpoints. Thank you.

    Greg

    ps : I won't be here next week.

     
  • Greg

    Greg - 2010-08-24

    Hi Ralf,

    I'm back :) I was working on something else. I still haven't found a proper solution for the synchronization problem but I have something more urgent for now. In fact I would like to add more property pages to the filter ( $ in total). What I thought is that I just needed to modify the resource files (.rc and resource.h) to create more interfaces and then add different property classes to my project.

    Afterward I modified the the CFactoryTemplate g_Templates function (DLLSetup.cpp) to consider more CLSID related to property pages and t method STDMETHODIMP GetPages(CAUUID *pPages) of the rendering filter. Like this :

    STDMETHODIMP GetPages(CAUUID *pPages)
    {
    if (pPages == NULL) return E_POINTER;
    pPages->cElems = 4;
    pPages->pElems = (GUID*)CoTaskMemAlloc(pPages->cElems * sizeof(GUID));
    if (pPages->pElems == NULL)
    {
    return E_OUTOFMEMORY;
    }
    pPages->pElems = CLSID_PropertyPageBlur;
    pPages->pElems = CLSID_PropertyPageColor;
    pPages->pElems = CLSID_PropertyPageMixing;
    pPages->pElems = CLSID_VideoMixingFilterProperties;

    return S_OK;
    }

    My problem now is that after doing that there is still one of the four property pages displayed. Do you know if there is a restriction concerning the number of property pages that could be displayed with the video mixing filter ?

    Thank you.

    Greg

     
  • Greg

    Greg - 2010-08-25

    Just to let you know. I've finally found where the problem was. I am now able to display all the property pages. I did the right thing but it was just this function that caused the problem : _swnprintf_s. I used it to display a parameter directly on the property pages and it was used incorrectly. Hope that may help someone one day.

     
  • Ralf Globisch

    Ralf Globisch - 2010-08-25

    Excellent Greg, glad you sorted it out, I've also just been very busy with project work…

     
  • Greg

    Greg - 2010-08-25

    Thanks :)

    I've an other problem now and hope this will be the last.  Again with the Video Mixing Filter . For my application, I just want to display the input image twice side by side when I have only the first input connected. For that I've modified two methods :

    HRESULT VideoMixingFilter::GenerateOutputSample(IMediaSample *pSample, int nIndex)
    {
    ...
        if (m_pSampleBuffers[0] && m_pSampleBuffers[1])
        {       
          ...
        }
        else
        {
            if (m_pSampleBuffers[0])
            {
                 m_pPicConcat->Concat(m_pSampleBuffers[0], m_pSampleBuffers[0], pBufferOut);   
            }
            else
            {
             ...
            }
        }
    ...
    }
    HRESULT VideoMixingFilter::SetOutputDimensions( BITMAPINFOHEADER* pBmih1, BITMAPINFOHEADER* pBmih2 )
    {
        if (pBmih1 && pBmih2)
        {
           ...
        }
        else
        {
            if (pBmih1)
            {
                switch (m_nOrientation) // In my case m_nOrientation always equals to 0
                {
                case 0:
                    m_nOutputWidth = (pBmih1->biWidth)<<1;
                    m_nOutputHeight = pBmih1->biHeight;
                    m_nOutputSize =  (m_nSampleSizes[0])<<1 ;
                    m_pPicConcat->Set1stDimensions(pBmih1->biWidth, pBmih1->biHeight);
                    m_pPicConcat->Set2ndDimensions(pBmih1->biWidth, pBmih1->biHeight);
                    m_pPicConcat->SetOutDimensions(m_nOutputWidth, m_nOutputHeight);
                break ;
                case 1:
                    m_pPicConcat->Set1stDimensions(pBmih1->biWidth, pBmih1->biHeight);
                break ;
                }
            }
            else if (pBmih2)
               ...
        }
    

    It doens't work of course :)  Do you know where I should modify the code to get what I want.

    Thanks a lot.

    Greg

     

     
  • Ralf Globisch

    Ralf Globisch - 2010-08-29

    Hi Greg,
    The best advice I get give is for you to step through the code: I would put break points in the modified methods. What you've modified looks ok to me. You need to determine at which point it doesn't work i.e. if you add the mixer and connect to the first input, and call the GetMediaType method from GraphEdit: does the property page show the correct size properties even if only one input is connected. Then insert another breakpoint in the ReceiveFirstSample and GenerateOutputSample and check if these methods even get called. What is happening at the moment: is the graph running? Or is there a deadlock, etc?

     
  • Greg

    Greg - 2010-08-31

    Hi Ralf,

    thanks to your advice, I succeeded. You had a good intuition, the size properties wasn't modified. Finally I removed all the modification I've done and  I've modified the GetMediaType method.

    Basically I just replaced this fragment of code :

    ...
    if (pOtherPin)
            {
                // We need to recalculate the new width and height
                hr = SetOutputDimensions(&(m_VideoInHeader[0].bmiHeader), &(m_VideoInHeader[0].bmiHeader)); 
    ...
    }
    ...
    

    with this one :

    ...
    //if (pOtherPin)
            {
                // We need to recalculate the new width and height
                if(m_vInputPins[1]->IsConnected())
                    hr = SetOutputDimensions(&(m_VideoInHeader[0].bmiHeader), &(m_VideoInHeader[1].bmiHeader));             
                else
                    hr = SetOutputDimensions(&(m_VideoInHeader[0].bmiHeader), &(m_VideoInHeader[0].bmiHeader)); 
    ...
    }
    ...
    

    I also modified the SetOutputDimensions method with :

    if (pBmih1 && pBmih2)
        {
            ...
            if(m_vInputPins[1]->IsConnected())
                m_nOutputSize =  m_nSampleSizes[0] + m_nSampleSizes[1];
            else
                m_nOutputSize =  m_nSampleSizes[0] + m_nSampleSizes[0];
    ...
    }
    else
    {
    ...
    }
    

    I am not sure if it's the best way to do it but at least it works for now. Thank you :)

    Have a good day

    Greg

     

Log in to post a comment.

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:

JavaScript is required for this form.





No, thanks