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.

Line 
1
2#include "FFmpegImageStream.hpp"
3#include "FFmpegAudioStream.hpp"
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::TOP_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    osg::notify(osg::INFO)<<"Destructing FFmpegImageStream..."<<std::endl;
43
44    quit(true);
45   
46    osg::notify(osg::INFO)<<"Have done quit"<<std::endl;
47
48    // release athe audio streams to make sure that the decoder doesn't retain any external
49    // refences.
50    getAudioStreams().clear();
51
52    // destroy the decoder and associated threads
53    m_decoder = 0;
54
55
56    delete m_commands;
57
58    osg::notify(osg::INFO)<<"Destructed FFMpegImageStream."<<std::endl;
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
75
76    setPixelAspectRatio(m_decoder->video_decoder().pixelAspectRatio());
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
85    m_decoder->video_decoder().setUserData(this);
86    m_decoder->video_decoder().setPublishCallback(publishNewFrame);
87
88    if (m_decoder->audio_decoder().validContext())
89    {
90        osg::notify(osg::NOTICE)<<"Attaching FFmpegAudioStream"<<std::endl;
91
92        getAudioStreams().push_back(new FFmpegAudioStream(m_decoder.get()));
93    }
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
133void FFmpegImageStream::seek(double time) {
134    m_seek_time = time;
135    m_commands->push(CMD_SEEK);
136}
137
138
139
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)
152    m_decoder->close(waitForThreadToExit);
153}
154
155
156double FFmpegImageStream::getLength() const
157{
158    return m_decoder->duration();
159}
160
161
162
163double FFmpegImageStream::getFrameRate() const
164{
165    return m_decoder->video_decoder().frameRate();
166}
167
168
169
170bool FFmpegImageStream::isImageTranslucent() const 
171{
172    return m_decoder->video_decoder().alphaChannel();
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    }
213   
214    osg::notify(osg::NOTICE)<<"Finished FFmpegImageStream::run()"<<std::endl;
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
242    case CMD_SEEK:
243        cmdSeek(m_seek_time);
244        return true;
245
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();
266
267        m_decoder->resume();
268    }
269
270    _status = PLAYING;
271}
272
273
274
275void FFmpegImageStream::cmdPause()
276{
277    if (_status == PLAYING)
278    {
279        m_decoder->pause();
280    }
281
282    _status = PAUSED;
283}
284
285
286
287void FFmpegImageStream::cmdRewind()
288{
289    m_decoder->rewind();
290}
291
292void FFmpegImageStream::cmdSeek(double time)
293{
294    m_decoder->seek(time);
295}
296
297
298void FFmpegImageStream::publishNewFrame(const FFmpegDecoderVideo &, void * user_data)
299{
300    FFmpegImageStream * const this_ = reinterpret_cast<FFmpegImageStream*>(user_data);
301
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
308    /** \bug If viewer.realize() hasn't been already called, this doesn't work? */
309    this_->dirty();
310#endif
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.