59

I need to play back a custom AVI file that contains a classic video stream, an audio stream but also a custom data stream.

The custom stream contains data that is visualized by some custom widgets; those widgets only need that each custom frame is written in a buffer at the proper time.

Our application is based on Qt, and already uses QMediaPlayer/QVideoWidget to play traditional videos, but the additional custom stream makes things more complicated, because AFAIK QMediaPlayer only plays the video/audio and ignores everything else.

I would like to avoid to reinvent the entire qt-multimedia, but I am not sure how to make the best out of the available Qt classes.


My ideas so far are:

  1. Write a custom media player class that demuxes and decodes the video using ffmpeg, implements the timing, uses QAudioOutput for playing the audio, produces a stream of QVideoFrames to be played on the video and write the custom data to some buffer for visualization.

    The problem: In order to avoid writing the code to rescale/convert the video frames, I would like to reuse QVideoWidget, but it seems to work only with the "real" QMediaPlayer.

  2. Demux the input file and feed QMediaPlayer with the AV streams. Demux the input with ffmpeg (possibly leaving the decoding to the Qt backend), have one QIODevice to retrieve only the video/audio streams from the input file and another one to retrieve the data stream. Play the video/audio with QMediaPlayer.

                  +-------+                          
                  | QFile |                          
                  +---^---+                          
                      |                              
                   inherits                          
                      |                              
            +--------------------+
            |    MyAviDemuxer    |
            |                    |
            |  holds a queue of  |
            |  demuxed packets   |
            +--------------------+
            |                    |
      readDataPacket      readVideoPacket
            |                    |
    +-------v--------+  +--------v-----------+            +-----------+
    | MyCustomReader |  | MyVideoAudioStream +--inherits--> QIODevice |
    +----------------+  +--------+-----------+            +-----------+
                                 |       
                              setMedia                  
                                 |                  
                         +-------v-------+           
                         | QMediaPlayer  |           
                         +---------------+           
    

    The problem: synchronize the timing of the data stream with QMediaPlayer, handle headers and metadata correctly.


I am slightly inclined to option 1, just because it gives me more control, but I am wondering if I missed an easier solution (even Windows-only).

sbabbi
  • 10,402
  • 2
  • 24
  • 51
  • 3
    Why in gods name are you still using AVI in 2015? – Steven Penny May 03 '15 at 00:25
  • 2
    @StevenPenny Dunno, legacy reasons and what not. We are going to change it. The question applies to any multimedia container format anyway. – sbabbi May 03 '15 at 13:22
  • Any reason you prefer FFmpeg over GStreamer? – karlphillip May 29 '15 at 02:58
  • @karlphilip The gstreamer demuxer does not like data streams - it simply fails to play at all. I even try to add the ffmpeg demuxer to the gstreamer plugins, but I could not manage to have it work. In the end I decided to rewrite a VideoPlayer on top of ffmpeg, handling all the synchronization issues by myself. – sbabbi May 29 '15 at 08:47
  • I'm not sure what the question here is, but I'm guessing it is "Which of these options is the best (or is there a better one)?" – Brian Gradin Jul 28 '15 at 18:57
  • OK I am not sure this will even work, but I got an idea while reading you Q: What if you format the data stream in a way that makes GStreamer/Qt think it is a supported format such as video/audio? That way you might be able to get access to it. Raw PCM audio comes to mind. That will simply be a blob passed through. – Lennart Rolland Oct 06 '15 at 01:56
  • Exact same problem I had 2months ago, never got an answer on qt forums – Lectem Oct 10 '15 at 16:41

2 Answers2

1

I understand you have quite the customized class structure but maybe you could use some advice from a coding newbie. I think you should be using some more basic existing data types together with your custom classes.

Solution for: synchronizing the timing of the data stream with QMediaPlayer:
Try using some timer threads (combination of Thread and timer). Make one that uses whatever the stream index is of MyVideoAudioStream (using time as the variable in the index) and "Mycustomreader" (using an array of packets with time as the variable in the index) as it's body. Add into the body some logic that cycles through the position( @param:time) in QMediaPlayer. From this,you could parse through the execution code of both at the same time. As time increases,the position in QMediaPlayer and the index of your stream would increase.

If you don't have an index or position in your custom stream,I highly suggest you create one.

  • The custom stream is embedded in the file passed to QMediaPlayer. This solution would require to open the same file twice and basically load the index twice (once for the custom stream, and once inside qt code). It is an easy way around but not very clean. It also depend on the assumption that qmediaplayer (or more properly the backend that it is using) does not bail out immediately when it sees a stream in the AVI file that can not understand. – sbabbi Nov 21 '15 at 23:58
  • Like I said, I'm a coding newbie. I Know this answer is not clean, but it's still the only answer here ... If you have a better solution,post it! – Michael Ireland Nov 28 '15 at 18:10
1

It looks like Qt actually already supports the concept of data streams to some degree - http://doc.qt.io/qt-5/qmediastreamscontrol.html#details shows that it's among the selectable types of streams for a qmediastreamscontrol.

Other docs including http://doc.qt.io/qt-5/qmediaserviceproviderplugin.html suggest that you could create a QMediaServiceProviderPlugin that implements video and audio QMediaControl interfaces (possibly by subclassing an existing media service provider), an also create your own QMediaControl interface subclass to create a Control to handle your raw data.

Hopefully implementing in this way would allow you to use existing facilities for splitting apart the streams, handling headers, and similar functionality.

Unfortunately the specifics of building a QMediaService seems to be "outside of the scope of this documentation and support on the relevant mailing lists or IRC channels should be sought." (http://doc.qt.io/qt-5/qmediaservice.html#details). The source (http://code.qt.io/cgit/qt/qtmultimedia.git/tree/src/multimedia) could be of some use doing this, however, in addition to, perhaps, the source at http://code.qt.io/cgit/qt/qtmultimedia.git/tree/src/plugins, which includes the directshow / gstreamer / coreaudio plugins.

In any case I would try to subclass and re-implement as little as possible

stonecrusher
  • 1,180
  • 8
  • 11