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

Revision 10961, 6.8 kB (checked in by robert, 4 years ago)

From Serge Lages, "Here is a patch to allow setting an audio volume with the AudioSink? interface, I've also modified the ffmpeg plugin code to implement the ImageStream?'s setVolume method with its AudioSink?."

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
[10961]155void FFmpegImageStream::setVolume(float volume)
156{
157    m_decoder->audio_decoder().setVolume(volume);
158}
[9816]159
[10961]160float FFmpegImageStream::getVolume() const
161{
162    return m_decoder->audio_decoder().getVolume();
163}
164
[9910]165double FFmpegImageStream::getLength() const
[9816]166{
167    return m_decoder->duration();
168}
169
170
[10851]171double FFmpegImageStream::getReferenceTime () const
172{
173    return m_decoder->reference();
174}
[9816]175
[10851]176
177
[9910]178double FFmpegImageStream::getFrameRate() const
[9816]179{
[9910]180    return m_decoder->video_decoder().frameRate();
[9816]181}
182
183
184
[9910]185bool FFmpegImageStream::isImageTranslucent() const 
[9816]186{
[9910]187    return m_decoder->video_decoder().alphaChannel();
[9816]188}
189
190
191
192void FFmpegImageStream::run()
193{
194    try
195    {
196        bool done = false;
197
198        while (! done)
199        {
200            if (_status == PLAYING)
201            {
202                bool no_cmd;
203                const Command cmd = m_commands->timedPop(no_cmd, 1);
204
205                if (no_cmd)
206                {
207                    m_decoder->readNextPacket();
208                }
209                else
210                    done = ! handleCommand(cmd);
211            }
212            else
213            {
214                done = ! handleCommand(m_commands->pop());
215            }
216        }
217    }
218
219    catch (const std::exception & error)
220    {
221        osg::notify(osg::WARN) << "FFmpegImageStream::run : " << error.what() << std::endl;
222    }
223
224    catch (...)
225    {
226        osg::notify(osg::WARN) << "FFmpegImageStream::run : unhandled exception" << std::endl;
227    }
[9869]228   
229    osg::notify(osg::NOTICE)<<"Finished FFmpegImageStream::run()"<<std::endl;
[9816]230}
231
232
233
234void FFmpegImageStream::applyLoopingMode()
235{
236    m_decoder->loop(getLoopingMode() == LOOPING);
237}
238
239
240
241bool FFmpegImageStream::handleCommand(const Command cmd)
242{
243    switch (cmd)
244    {
245    case CMD_PLAY:
246        cmdPlay();
247        return true;
248
249    case CMD_PAUSE:
250        cmdPause();
251        return true;
252
253    case CMD_REWIND:
254        cmdRewind();
255        return true;
256
[10809]257    case CMD_SEEK:
258        cmdSeek(m_seek_time);
259        return true;
260
[9816]261    case CMD_STOP:
262        return false;
263
264    default:
265        assert(false);
266        return false;
267    }
268}
269
270
271
272void FFmpegImageStream::cmdPlay()
273{
274    if (_status == PAUSED)
275    {
276        if (! m_decoder->audio_decoder().isRunning())
277            m_decoder->audio_decoder().start();
278
279        if (! m_decoder->video_decoder().isRunning())
280            m_decoder->video_decoder().start();
[10809]281
[10851]282        m_decoder->video_decoder().pause(false);
283        m_decoder->audio_decoder().pause(false);
[9816]284    }
285
286    _status = PLAYING;
287}
288
289
290
291void FFmpegImageStream::cmdPause()
292{
293    if (_status == PLAYING)
294    {
[10851]295        m_decoder->video_decoder().pause(true);
296        m_decoder->audio_decoder().pause(true);
[9816]297    }
298
299    _status = PAUSED;
300}
301
302
303
304void FFmpegImageStream::cmdRewind()
305{
306    m_decoder->rewind();
307}
308
[10809]309void FFmpegImageStream::cmdSeek(double time)
310{
311    m_decoder->seek(time);
312}
[9816]313
314
315void FFmpegImageStream::publishNewFrame(const FFmpegDecoderVideo &, void * user_data)
316{
317    FFmpegImageStream * const this_ = reinterpret_cast<FFmpegImageStream*>(user_data);
318
[9860]319#if 1
320    this_->setImage(
321        this_->m_decoder->video_decoder().width(), this_->m_decoder->video_decoder().height(), 1, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE,
322        const_cast<unsigned char *>(this_->m_decoder->video_decoder().image()), NO_DELETE
323    );
324#else
[9816]325    /** \bug If viewer.realize() hasn't been already called, this doesn't work? */
326    this_->dirty();
[9860]327#endif
[9816]328
329    OpenThreads::ScopedLock<Mutex> lock(this_->m_mutex);
330
331    if (! this_->m_frame_published_flag)
332    {
333        this_->m_frame_published_flag = true;
334        this_->m_frame_published_cond.signal();
335    }
336}
337
338
339
340} // namespace osgFFmpeg
Note: See TracBrowser for help on using the browser.