Frame accurate synchronizing of subtitle files with MPEG video using DirectShow - c++

This is a problem I have been dealing with for a while, and haven't been able to get a good answer (even from Microsoft). I'm using the generic dump filter to write hardware compressed MPEG files out to disk. In the graph, I also have a SampleGrabber filter that gets called on every frame. From the SampleGrabber callback, I get a subtitle, along with the DirectShow timestamp and write them out to a SAMI (.smi) subtitle file. This all seems to be working, as the SAMI file contains the correct subtitles for every frame. However, I have a few problems:
The first few (usually 3 or 4) DirectShow timestamps are all 0. If I'm getting callbacks from the SampleGrabber, shouldn't these timestamps be incrementing?
When I begin playback, the first timestamp shown is about 10-20 subtitles into the SAMI file. I'd assume the first frame would show the first timestamp in the file.
This is probably related to #2, but the subtitles are not synchronized to the appropriate frames in the file. They can sometimes be up to 40 frames late.
I'm using DirectShow via C++, capturing with a Hauppauge HVR-1800 under Windows XP SP3 (with latest drivers 09/08/2008), and playing back under Media Player Classic 6.4.9.0. Any ideas are welcome.

Are you using getting the incoming IMediaSample's GetTime or GetMediaTime. GetTime is what you want as it respresents the streams presentations time.
Be sure to also check the incoming IMediaSample's isPreRoll function. Preroll samples should be ignored as they will be output again during playback. Another thing I would do is make sure that your sample grabber is as far downstream in your filtergraph as it can be. Preferably after any demuxer's and renderers.
Also see the article on TimeStamps in the DirectShow documentation. It outlines the other caveats of using timestamps.
Of course, even after all of the tips above, there is still no absolute guarantee as to how a particular DirectShow filter is going to behave.

Related

How to feed video data into a DirectShow filter to compress/encode and save to file?

First of all, here is what I'm trying to accomplish:
We'd like to add the capability to our commercial application to generate a video file to visualize data. It should be saved in a reasonably compressed format. It is important that the encoding library/codecs are licensed such that we're allowed to use and sell our software without restriction. It's also important that the media format can easily be played by a customer, i.e. can be played by Windows Media Player without requiring a codec pack to be installed.
I'm trying to use DirectShow in c++ by creating a source filter with one output pin that generates the video. I'm closely following the DirectShow samples called Bouncing Ball and Push Source. In GraphEdit I can successfully connect to a video renderer and see the video play. I have also managed to connect to AVI Mux and then file writer to write an uncompressed AVI file. The only issue with this is the huge file size. However, I have not been able to save the video in a compressed format. This problem also happens with the Ball and Push Source samples.
I can connect the output pin to a WM ASF Writer, but when I click play I get "This graph can't play. Unspecified error (Return code: 0x80004005)."
I can't even figure out how to connect to the WMVideo9 Encoder DMO ("These filters cannot agree on a connection"). I could successfully save to mjpeg, but compression was not very substantial.
Please let me know if I'm doing something wrong in GraphEdit or if my source filter code needs to be modified. Alternatively, if there is another (non-DirectShow) option that would work for me I'm open to suggestions. Thanks.
You don't give details to help you with your modification of the filters, however Ball sample generates output which can be written to a file.
Your choice of WM ASF Writer filter is okay - it is a stock filter and it is more or less easy to deal with. There is however a caveat: you need to select video only profile on the filter first, and then connect video input. WM ASF Writer won't run with an unconnected input pin, and in default state it also has an audio input. Of course this can also be done programmatically.
The graph can be as simple as this, and it can be run and it generates a playable file.

DirectShow video stream ends immediately (m_pMediaSample is NULL)

I have a directshow Video renderer redived from CBaseVideoRenderer. The renderer is used in a graph that receives data from a live source (BDA). It looks like the connections are established properly, but the video rendering immediately ends because there is no sample. However, audio Rendering works, ie I can hear the sound while DoRenderSample of my renderer is never called.
Stepping through the code in the debugger, I found out that in CBaseRenderer::StartStreaming, the stream ends immedately, because the member m_pMediaSample is NULL. If I replace my renderer with the EVR renderer, it shows frames, ie the stream is not ending before the first frame for the EVR renderer, but only for my renderer.
Why is that and how can I fix it? I implemented (following the sample from http://www.codeproject.com/Articles/152317/DirectShow-Filters-Development-Part-Video-Render) what I understand as the basic interface (CheckMediaType, SetMediaType and DoRenderSample), so I do not see any possibility to influence what is happening here...
Edit: This is the graph as seen from the ROT:
What I basically try to do is capturing a DVB stream that uses VIDEOINFOHEADER2, which is not supported by the standard sample grabber. Although the channel is a public German TV channel without encryption, could it be that this is a DRM issue?
Edit 2: I have attached my renderer to another source (a Blackmagic Intensity Shuttle). It seams that the source causes the issue, because I get samples in the other graph.
Edit 3: Following Roman's Suggestion, I have created a transform filter. The graph looks like
an has unfortunately the same problem, ie I do not get any sample (Transform is not called).
You supposedly chose wrong path of fetching video frames out of media pipeline. So you are implementing a "network renderer", something that terminates the pipeline to further send data to network.
A renderer which accepts the feed sounds appropriate. Implementing a custom renderer, however, is an untypical task and then there is not so much information around on this. Additionally, a fully featured renderer is typically equipped with sample scheduling part, which end of stream delivery - things relatively easy to break when you customize it through inheriting from base classes. That is, while the approach sounds good, you might want to compare it to another option you have, which is...
A combination of Sample Grabber + Null Renderer, two standard filters, which you can attach your callback to and get frames having the pipeline properly terminated. The problem here is that standard Sample Grabber does not support VIDEOINFOHEADER2. With another video decoder you could possibly have the feed decoded info VIDEOINFOHEADER, which is one option. And then improvement of Sample Grabber itself is another solution: DirectX SDK Extras February 2005 (dxsdk_feb2005_extras.exe) was the SDK which included a filter similar to standard Sample Grabber called Grabber \DirectShow\Samples\C++\DirectShow\Filters\Grabber. It is/was available in source code and provided with a good description text file. It is relatively easy to extend to allow it accept VIDEOINFOHEADER2 and make payload data available to your application this way.
The easiest way to get data out of a DirectShow graph, if youњre not going to use
MultiMedia Streaming, is probably to write your own TransInPlace filter, a sub-variety
of a Transform filter. Then connect this filter to the desired stream of data you wish to
monitor, and then run, pause, seek, or otherwise control the graph. The data, as it passes
through the transform filter, can be manipulated however you want. We call this kind of
filter, a Њsample grabberћ. Microsoft released a limited-functionality sample grabber
with DX8.0. This filter is limited because it doesnњt deal with DV Data or mediatypes
with a format of VideoInfo2. It doesnњt allow the user to receive prerolled samples.
(Whatњs a preroll sample? See the DX8.1 docs) Its ЊOneShotћ mode also has some problems.
To add to this, the Grabber sample is pretty simple itself - perhaps 1000 lines of code all together, including comments.
Looks like your decoder or splitter isn't demuxing the video frames. Look further up the chain to see what filters are supplying your renderer pin with data, chances are its only recognising audio.
Try dropping the file into Graphedit (there's a better one on the web BTW) and see what filters it creates.
Then look at the samples in the DirectShow SDK.

Correcting live IMFMediaSource time stamps

I have two cameras, listed below, that I am trying to use in a Media Foundation topology. Here is a summary of my topology:
Webcam --> MJPG Decoder --> Custom MFT --> H264 Encoder --> MP4 File Sink
The problem with this is that the generated MP4 file has incorrect duration and time scale tags, both for the MP4 container and the H264 stream. I can easily correct this with a tool like MP4Box or YAMB, but my eventual goal is to stream the video.
One potential cause I have identified is that the samples generated by the camera sources do not start at time 0. According to bullet #2 in http://msdn.microsoft.com/en-us/library/windows/desktop/ms700134(v=vs.85).aspx#live_sources, timestamps of a live source should start at 0.
Along this line, I've tried the following to "correct" the sample timestamps:
Re-based the sample time in my custom MFT, using IMFSample::SetSampleTime.
Created a wrapper for the IMFMediaSource and IMFMediaStream objects, which catches and corrects the time stamps associated with the MEMediaSample and MEStreamTick events.
In both of these cases, the media session throws an error 0xC00D4A44 (MF_E_SINK_NO_SAMPLES_PROCESSED), and the resulting MP4 file ends abruptly after the "mdat" atom declaration.
Cameras
Logitech BCC950 ConferenceCam
Thinkpad W520 Integrated Camera
Systems used (both have same issue):
Windows 7 Professional x64
Windows 8 x86
Questions:
Is there some other cause I have overlooked for incorrect video duration/time scale?
If not, is there a correct approach for how to re-base sample timestamps?
Try to reset for every sample flag MFSampleExtension_Discontinuity
pSample->SetUINT32( MFSampleExtension_Discontinuity, FALSE );

Recording application output to video using FFmpeg (or similar)

We have a requirement to lets users record a video of our 3D application. I can already grab the individual rendered frames so this question is specifically about how to write frames into a video file.
I don't think writing each frame as a separate file and post-processing is a workable option.
I can look at options to record to a simple video file for later optimising/encoding, or writing directly to a sensibly encoded format.
FFmpeg was suggested in another post but it looks a bit daunting to me. Is it the best option, if not what can be suggested? We can work with LGPL but not full GPL.
We're working on Windows (Win32 not MFC) in C++. Sample/pseudo code with your recommended library is very much appreciated... basically after how to do 3 functions:
startRecording() does whatever initialization is needed
recordFrame() takes pointer to frame data and encodes it, ideally with timing data
endRecording() finalizes the video file, shuts down video system, etc
Check out the sources to Taksi on sourceforge. http://taksi.sourceforge.net/
You need 2 things.
1. A code to compress the frames.
2. A container file format. Like AVI or MPG.
Taksi useses the old VideoForWindows API and AVI not the newer COM API's but it still might work for you.

How can I optimize my screencasting utility?

I'm developing a screencasting utility in C++.
It basically captures desktop frames and creates an AVI file. The algorithm is as follows:
Create a thread: this->m_hThread=CreateThread(NULL,0,thScreenCapture,this,0,NULL);
Capture desktop in thScreenCapture n times per second (like 5 fps).
obj->Capture();
In Capture(), append the bitmap data to the avi file.
this->appendBitmapToAvi(this->avifile, bmp);
This utility also records sound. So, in method thScreenCapture, sound data is also being appended to the avi file.
The problem is that a lag occurs between frames and sound when more than 6 frames (this can change depending on hardware configuration) are captured per second.
I'm seeking for a solution to optimize the algorithm. A solution may be buffering frames in memory, not appending all of them to the avi file on-the-fly. But this makes the code more complex because I have to deal with the sound data that's being captured in a different thread.
What do you suggest to increase the fps value that this utility supports without losing synchronization?
Are you writing the AVI file yourself? A noble effort, but there are APIs to help with this task.
If you're working on the windows platform, I'd suggest considering using the DirectShow or Media Foundation APIs to mux the audio and video to disk. DirectShow is the API for A/V capture, streaming and muxing on the windows platform.
This article on CodeProject talks about audio & video sync issues and the mechanism that DirectShow uses to overcome this difficulty.
Essentially, a reference clock is employed and the frames are timestamped.
There's a very active DirectShow community that is an extremely useful resource for new folks. TMH's website is well worth checking out - he's an MS MVP and is an active member of the community.
I hope this helps!
You could take a look at the source for other screencasting software, such as CamStudio, to see how they are doing it.
If your program is disk bound (and I suspect it is), then things might improve by using compression (this is how the big name programs, such as Camtasia Studio, operate)
Use a circular double or triple buffer to store the bitmap and sound each frame and use a separate thread to add the bitmap and sound to the avi. So data-collection is in one thread, data is in a circular (thread-safe) buffer and the data-storage is in another thread.
What OS are you targeting? If tyou are working on Windows XP I would take a look at some of the DirectShow code at http://tmhare.mvps.org/downloads.htm, specifically Filter Graph Library.