root/OpenSceneGraph/trunk/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp @ 9860

Revision 9860, 5.3 kB (checked in by robert, 6 years ago)

Introduced double buffering of video stream to avoid tearing of image.

Removed swapBufers call and image y inversion.

RevLine 
[9816]1
2#include "FFmpegImageStream.hpp"
[9847]3#include "FFmpegAudioStream.hpp"
[9816]4
5#include <OpenThreads/ScopedLock>
6#include <osg/Notify>
7
8#include <memory>
9
10
11
12namespace osgFFmpeg {
13
14
15
16FFmpegImageStream::FFmpegImageStream() :
17    m_decoder(0),
18    m_commands(0),
19    m_frame_published_flag(false)
20{
21    setOrigin(osg::Image::BOTTOM_LEFT);
22
23    std::auto_ptr<FFmpegDecoder> decoder(new FFmpegDecoder);
24    std::auto_ptr<CommandQueue> commands(new CommandQueue);
25
26    m_decoder = decoder.release();
27    m_commands = commands.release();
28}
29
30
31
32FFmpegImageStream::FFmpegImageStream(const FFmpegImageStream & image, const osg::CopyOp & copyop) :
33    osg::ImageStream(image, copyop)
34{
35    // TODO: probably incorrect or incomplete
36}
37
38
39
40FFmpegImageStream::~FFmpegImageStream()
41{
42    quit(true);
43
44    delete m_commands;
45}
46
47
48
49bool FFmpegImageStream::open(const std::string & filename)
50{
51    setFileName(filename);
52
53    if (! m_decoder->open(filename))
54        return false;
55
56    setImage(
57        m_decoder->video_decoder().width(), m_decoder->video_decoder().height(), 1, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE,
58        const_cast<unsigned char *>(m_decoder->video_decoder().image()), NO_DELETE
59    );
[9860]60   
61    setOrigin(osg::Image::TOP_LEFT);
[9816]62
63    m_decoder->video_decoder().setUserData(this);
64    m_decoder->video_decoder().setPublishCallback(publishNewFrame);
[9847]65   
66    if (m_decoder->audio_decoder().validContext())
67    {
68        osg::notify(osg::NOTICE)<<"Attaching FFmpegAudioStream"<<std::endl;
69   
70        getAudioStreams().push_back(new FFmpegAudioStream(m_decoder.get()));
71    }
[9816]72
73    _status = PAUSED;
74    applyLoopingMode();
75
76    start(); // start thread
77
78    return true;
79}
80
81
82
83void FFmpegImageStream::play()
84{
85    m_commands->push(CMD_PLAY);
86
87#if 0
88    // Wait for at least one frame to be published before returning the call
89    OpenThreads::ScopedLock<Mutex> lock(m_mutex);
90
91    while (duration() > 0 && ! m_frame_published_flag)
92        m_frame_published_cond.wait(&m_mutex);
93
94#endif
95}
96
97
98
99void FFmpegImageStream::pause()
100{
101    m_commands->push(CMD_PAUSE);
102}
103
104
105
106void FFmpegImageStream::rewind()
107{
108    m_commands->push(CMD_REWIND);
109}
110
111
112
113void FFmpegImageStream::quit(bool waitForThreadToExit)
114{
115    // Stop the packet producer thread
116    if (isRunning())
117    {
118        m_commands->push(CMD_STOP);
119
120        if (waitForThreadToExit)
121            join();
122    }
123
124    // Close the decoder (i.e. flush the decoder packet queues)
125    m_decoder->close();
126}
127
128
129double FFmpegImageStream::duration() const
130{
131    return m_decoder->duration();
132}
133
134
135
136bool FFmpegImageStream::videoAlphaChannel() const 
137{
138    return m_decoder->video_decoder().alphaChannel();
139}
140
141
142
143double FFmpegImageStream::videoAspectRatio() const
144{
145    return m_decoder->video_decoder().aspectRatio();
146}
147
148
149
150double FFmpegImageStream::videoFrameRate() const
151{
152    return m_decoder->video_decoder().frameRate();
153}
154
155
156void FFmpegImageStream::run()
157{
158    try
159    {
160        bool done = false;
161
162        while (! done)
163        {
164            if (_status == PLAYING)
165            {
166                bool no_cmd;
167                const Command cmd = m_commands->timedPop(no_cmd, 1);
168
169                if (no_cmd)
170                {
171                    m_decoder->readNextPacket();
172                }
173                else
174                    done = ! handleCommand(cmd);
175            }
176            else
177            {
178                done = ! handleCommand(m_commands->pop());
179            }
180        }
181    }
182
183    catch (const std::exception & error)
184    {
185        osg::notify(osg::WARN) << "FFmpegImageStream::run : " << error.what() << std::endl;
186    }
187
188    catch (...)
189    {
190        osg::notify(osg::WARN) << "FFmpegImageStream::run : unhandled exception" << std::endl;
191    }
192}
193
194
195
196void FFmpegImageStream::applyLoopingMode()
197{
198    m_decoder->loop(getLoopingMode() == LOOPING);
199}
200
201
202
203bool FFmpegImageStream::handleCommand(const Command cmd)
204{
205    switch (cmd)
206    {
207    case CMD_PLAY:
208        cmdPlay();
209        return true;
210
211    case CMD_PAUSE:
212        cmdPause();
213        return true;
214
215    case CMD_REWIND:
216        cmdRewind();
217        return true;
218
219    case CMD_STOP:
220        return false;
221
222    default:
223        assert(false);
224        return false;
225    }
226}
227
228
229
230void FFmpegImageStream::cmdPlay()
231{
232    if (_status == PAUSED)
233    {
234        if (! m_decoder->audio_decoder().isRunning())
235            m_decoder->audio_decoder().start();
236
237        if (! m_decoder->video_decoder().isRunning())
238            m_decoder->video_decoder().start();
239    }
240
241    _status = PLAYING;
242}
243
244
245
246void FFmpegImageStream::cmdPause()
247{
248    if (_status == PLAYING)
249    {
250
251    }
252
253    _status = PAUSED;
254}
255
256
257
258void FFmpegImageStream::cmdRewind()
259{
260    m_decoder->rewind();
261}
262
263
264
265void FFmpegImageStream::publishNewFrame(const FFmpegDecoderVideo &, void * user_data)
266{
267    FFmpegImageStream * const this_ = reinterpret_cast<FFmpegImageStream*>(user_data);
268
[9860]269#if 1
270    this_->setImage(
271        this_->m_decoder->video_decoder().width(), this_->m_decoder->video_decoder().height(), 1, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE,
272        const_cast<unsigned char *>(this_->m_decoder->video_decoder().image()), NO_DELETE
273    );
274#else
[9816]275    /** \bug If viewer.realize() hasn't been already called, this doesn't work? */
276    this_->dirty();
[9860]277#endif
[9816]278
279    OpenThreads::ScopedLock<Mutex> lock(this_->m_mutex);
280
281    if (! this_->m_frame_published_flag)
282    {
283        this_->m_frame_published_flag = true;
284        this_->m_frame_published_cond.signal();
285    }
286}
287
288
289
290} // namespace osgFFmpeg
Note: See TracBrowser for help on using the browser.