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

Revision 10809, 6.4 kB (checked in by robert, 4 years ago)

From Rafa Gaitan, "Current ffmpeg plugin didn't support pause and seek, I have added this
functionality and I also modified osgmovie example to support "seek"."

Note from Robert Osfield, changes osgmovie to use '>' for the seek as '+' was already used in a separate submission that had been merged.

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{
[9910]21    setOrigin(osg::Image::TOP_LEFT);
[9816]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{
[9912]42    osg::notify(osg::INFO)<<"Destructing FFmpegImageStream..."<<std::endl;
[9861]43
[9816]44    quit(true);
[9869]45   
[9912]46    osg::notify(osg::INFO)<<"Have done quit"<<std::endl;
[9816]47
[9869]48    // release athe audio streams to make sure that the decoder doesn't retain any external
49    // refences.
50    getAudioStreams().clear();
51
[9861]52    // destroy the decoder and associated threads
53    m_decoder = 0;
54
55
[9816]56    delete m_commands;
[9861]57
[9912]58    osg::notify(osg::INFO)<<"Destructed FFMpegImageStream."<<std::endl;
[9816]59}
60
61
62
63bool FFmpegImageStream::open(const std::string & filename)
64{
65    setFileName(filename);
66
67    if (! m_decoder->open(filename))
68        return false;
69
70    setImage(
71        m_decoder->video_decoder().width(), m_decoder->video_decoder().height(), 1, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE,
72        const_cast<unsigned char *>(m_decoder->video_decoder().image()), NO_DELETE
73    );
74
[10538]75
[9910]76    setPixelAspectRatio(m_decoder->video_decoder().pixelAspectRatio());
[10538]77
78    osg::notify(osg::NOTICE)<<"ffmpeg::open("<<filename<<") size("<<s()<<", "<<t()<<") aspect ratio "<<m_decoder->video_decoder().pixelAspectRatio()<<std::endl;
79
80#if 1
81    // swscale is reported errors and then crashing when rescaling video of size less than 10 by 10.
82    if (s()<=10 || t()<=10) return false;
83#endif
84
[9816]85    m_decoder->video_decoder().setUserData(this);
86    m_decoder->video_decoder().setPublishCallback(publishNewFrame);
[10538]87
[9847]88    if (m_decoder->audio_decoder().validContext())
89    {
90        osg::notify(osg::NOTICE)<<"Attaching FFmpegAudioStream"<<std::endl;
[10538]91
[9847]92        getAudioStreams().push_back(new FFmpegAudioStream(m_decoder.get()));
93    }
[9816]94
95    _status = PAUSED;
96    applyLoopingMode();
97
98    start(); // start thread
99
100    return true;
101}
102
103
104
105void FFmpegImageStream::play()
106{
107    m_commands->push(CMD_PLAY);
108
109#if 0
110    // Wait for at least one frame to be published before returning the call
111    OpenThreads::ScopedLock<Mutex> lock(m_mutex);
112
113    while (duration() > 0 && ! m_frame_published_flag)
114        m_frame_published_cond.wait(&m_mutex);
115
116#endif
117}
118
119
120
121void FFmpegImageStream::pause()
122{
123    m_commands->push(CMD_PAUSE);
124}
125
126
127
128void FFmpegImageStream::rewind()
129{
130    m_commands->push(CMD_REWIND);
131}
132
[10809]133void FFmpegImageStream::seek(double time) {
134    m_seek_time = time;
135    m_commands->push(CMD_SEEK);
136}
[9816]137
138
[10809]139
[9816]140void FFmpegImageStream::quit(bool waitForThreadToExit)
141{
142    // Stop the packet producer thread
143    if (isRunning())
144    {
145        m_commands->push(CMD_STOP);
146
147        if (waitForThreadToExit)
148            join();
149    }
150
151    // Close the decoder (i.e. flush the decoder packet queues)
[9869]152    m_decoder->close(waitForThreadToExit);
[9816]153}
154
155
[9910]156double FFmpegImageStream::getLength() const
[9816]157{
158    return m_decoder->duration();
159}
160
161
162
[9910]163double FFmpegImageStream::getFrameRate() const
[9816]164{
[9910]165    return m_decoder->video_decoder().frameRate();
[9816]166}
167
168
169
[9910]170bool FFmpegImageStream::isImageTranslucent() const 
[9816]171{
[9910]172    return m_decoder->video_decoder().alphaChannel();
[9816]173}
174
175
176
177void FFmpegImageStream::run()
178{
179    try
180    {
181        bool done = false;
182
183        while (! done)
184        {
185            if (_status == PLAYING)
186            {
187                bool no_cmd;
188                const Command cmd = m_commands->timedPop(no_cmd, 1);
189
190                if (no_cmd)
191                {
192                    m_decoder->readNextPacket();
193                }
194                else
195                    done = ! handleCommand(cmd);
196            }
197            else
198            {
199                done = ! handleCommand(m_commands->pop());
200            }
201        }
202    }
203
204    catch (const std::exception & error)
205    {
206        osg::notify(osg::WARN) << "FFmpegImageStream::run : " << error.what() << std::endl;
207    }
208
209    catch (...)
210    {
211        osg::notify(osg::WARN) << "FFmpegImageStream::run : unhandled exception" << std::endl;
212    }
[9869]213   
214    osg::notify(osg::NOTICE)<<"Finished FFmpegImageStream::run()"<<std::endl;
[9816]215}
216
217
218
219void FFmpegImageStream::applyLoopingMode()
220{
221    m_decoder->loop(getLoopingMode() == LOOPING);
222}
223
224
225
226bool FFmpegImageStream::handleCommand(const Command cmd)
227{
228    switch (cmd)
229    {
230    case CMD_PLAY:
231        cmdPlay();
232        return true;
233
234    case CMD_PAUSE:
235        cmdPause();
236        return true;
237
238    case CMD_REWIND:
239        cmdRewind();
240        return true;
241
[10809]242    case CMD_SEEK:
243        cmdSeek(m_seek_time);
244        return true;
245
[9816]246    case CMD_STOP:
247        return false;
248
249    default:
250        assert(false);
251        return false;
252    }
253}
254
255
256
257void FFmpegImageStream::cmdPlay()
258{
259    if (_status == PAUSED)
260    {
261        if (! m_decoder->audio_decoder().isRunning())
262            m_decoder->audio_decoder().start();
263
264        if (! m_decoder->video_decoder().isRunning())
265            m_decoder->video_decoder().start();
[10809]266
267        m_decoder->resume();
[9816]268    }
269
270    _status = PLAYING;
271}
272
273
274
275void FFmpegImageStream::cmdPause()
276{
277    if (_status == PLAYING)
278    {
[10809]279        m_decoder->pause();
[9816]280    }
281
282    _status = PAUSED;
283}
284
285
286
287void FFmpegImageStream::cmdRewind()
288{
289    m_decoder->rewind();
290}
291
[10809]292void FFmpegImageStream::cmdSeek(double time)
293{
294    m_decoder->seek(time);
295}
[9816]296
297
298void FFmpegImageStream::publishNewFrame(const FFmpegDecoderVideo &, void * user_data)
299{
300    FFmpegImageStream * const this_ = reinterpret_cast<FFmpegImageStream*>(user_data);
301
[9860]302#if 1
303    this_->setImage(
304        this_->m_decoder->video_decoder().width(), this_->m_decoder->video_decoder().height(), 1, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE,
305        const_cast<unsigned char *>(this_->m_decoder->video_decoder().image()), NO_DELETE
306    );
307#else
[9816]308    /** \bug If viewer.realize() hasn't been already called, this doesn't work? */
309    this_->dirty();
[9860]310#endif
[9816]311
312    OpenThreads::ScopedLock<Mutex> lock(this_->m_mutex);
313
314    if (! this_->m_frame_published_flag)
315    {
316        this_->m_frame_published_flag = true;
317        this_->m_frame_published_cond.signal();
318    }
319}
320
321
322
323} // namespace osgFFmpeg
Note: See TracBrowser for help on using the browser.