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

Revision 10809, 9.9 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 "FFmpegDecoder.hpp"
3
4#include <osg/Notify>
[10030]5#include <osgDB/FileNameUtils>
[9816]6
7#include <cassert>
8#include <limits>
9#include <stdexcept>
10#include <string.h>
[10030]11#include <iostream>
[9816]12
13
14namespace osgFFmpeg {
15
16
17
18FFmpegDecoder::FFmpegDecoder() :
19    m_audio_stream(0),
20    m_video_stream(0),
21    m_audio_queue(100),
22    m_video_queue(100),
23    m_audio_decoder(m_audio_queue, m_clocks),
24    m_video_decoder(m_video_queue, m_clocks),
25    m_state(NORMAL),
26    m_loop(false)
27{
28
29}
30
31
32
33FFmpegDecoder::~FFmpegDecoder()
34{
[9869]35    close(true);
[9816]36}
37
38
39bool FFmpegDecoder::open(const std::string & filename)
40{
41    try
42    {
43        // Open video file
44        AVFormatContext * p_format_context = 0;
45
[9865]46        if (filename.compare(0, 5, "/dev/")==0)
47        {
48            avdevice_register_all();
49       
50            osg::notify(osg::NOTICE)<<"Attempting to stream "<<filename<<std::endl;
[9816]51
[9865]52            AVFormatParameters formatParams;
53            memset(&formatParams, 0, sizeof(AVFormatParameters));
54            AVInputFormat *iformat;
55
56            formatParams.channel = 0;
57            formatParams.standard = 0;
[9969]58#if 1
[9990]59            formatParams.width = 320;
60            formatParams.height = 240;
[9969]61#else
62            formatParams.width = 640;
63            formatParams.height = 480;
64#endif           
[9865]65            formatParams.time_base.num = 1;
[9990]66            formatParams.time_base.den = 30;
[9865]67
[10030]68            std::string format = "video4linux2";
69            iformat = av_find_input_format(format.c_str());
[9865]70           
71            if (iformat)
72            {
[10030]73                osg::notify(osg::NOTICE)<<"Found input format: "<<format<<std::endl;
[9865]74            }
75            else
76            {
[10030]77                osg::notify(osg::NOTICE)<<"Failed to find input format: "<<format<<std::endl;
[9865]78            }
79
[10030]80            int error = av_open_input_file(&p_format_context, filename.c_str(), iformat, 0, &formatParams);
81            if (error != 0)
82            {
83                std::string error_str;
84                switch (error)
85                {
86                    //case AVERROR_UNKNOWN: error_str = "AVERROR_UNKNOWN"; break;   // same value as AVERROR_INVALIDDATA
87                    case AVERROR_IO: error_str = "AVERROR_IO"; break;
88                    case AVERROR_NUMEXPECTED: error_str = "AVERROR_NUMEXPECTED"; break;
89                    case AVERROR_INVALIDDATA: error_str = "AVERROR_INVALIDDATA"; break;
90                    case AVERROR_NOMEM: error_str = "AVERROR_NOMEM"; break;
91                    case AVERROR_NOFMT: error_str = "AVERROR_NOFMT"; break;
92                    case AVERROR_NOTSUPP: error_str = "AVERROR_NOTSUPP"; break;
93                    case AVERROR_NOENT: error_str = "AVERROR_NOENT"; break;
94                    case AVERROR_PATCHWELCOME: error_str = "AVERROR_PATCHWELCOME"; break;
95                    default: error_str = "Unknown error"; break;
96                }
97
98                throw std::runtime_error("av_open_input_file() failed : " + error_str);
99            }
[9865]100        }
101        else
102        {
103            if (av_open_input_file(&p_format_context, filename.c_str(), 0, 0, 0) !=0 )
104                throw std::runtime_error("av_open_input_file() failed");
105        }
106       
[9826]107        m_format_context.reset(p_format_context);
[9816]108
109        // Retrieve stream info
110        if (av_find_stream_info(p_format_context) < 0)
111            throw std::runtime_error("av_find_stream_info() failed");
112
113        m_duration = double(m_format_context->duration) / AV_TIME_BASE;
114        m_start = double(m_format_context->start_time) / AV_TIME_BASE;
115
116        // TODO move this elsewhere
117        m_clocks.reset(m_start);
118
119        // Dump info to stderr
120        dump_format(p_format_context, 0, filename.c_str(), false);
121
122        // Find and open the first video and audio streams (note that audio stream is optional and only opened if possible)
123
124        findVideoStream();
125        findAudioStream();
126
127        m_video_decoder.open(m_video_stream);
128
129        try
130        {
131            m_audio_decoder.open(m_audio_stream);
132        }
133
134        catch (const std::runtime_error & error)
135        {
136            osg::notify(osg::WARN) << "FFmpegImageStream::open audio failed, audio stream will be disabled: " << error.what() << std::endl;
137        }
138    }
139
140    catch (const std::runtime_error & error)
141    {
142        osg::notify(osg::WARN) << "FFmpegImageStream::open : " << error.what() << std::endl;
143        return false;
144    }
145   
146    return true;
147}
148
149
150
[9869]151void FFmpegDecoder::close(bool waitForThreadToExit)
[9816]152{
153    flushAudioQueue();
154    flushVideoQueue();
[9869]155   
156    m_audio_decoder.close(waitForThreadToExit);
157    m_video_decoder.close(waitForThreadToExit);
[9816]158}
159
160
161
162bool FFmpegDecoder::readNextPacket()
163{
164    switch (m_state)
165    {
166    case NORMAL:
167        return readNextPacketNormal();
168
[10809]169    case PAUSE:
170        return false;
171
[9816]172    case END_OF_STREAM:
173        return readNextPacketEndOfStream();
174
175    case REWINDING:
176        return readNextPacketRewinding();
177
[10809]178    case SEEKING:
179        return readNextPacketSeeking();
180
[9816]181    default:
182        assert(false);
183        return false;
184    }
185}
186
187
188
189void FFmpegDecoder::rewind()
190{
191    m_pending_packet.clear();
192
193    flushAudioQueue();
194    flushVideoQueue();
195    rewindButDontFlushQueues();
196}
197
[10809]198void FFmpegDecoder::seek(double time)
199{
200    m_pending_packet.clear();
[9816]201
[10809]202    flushAudioQueue();
203    flushVideoQueue();
204    seekButDontFlushQueues(time);
205}
[9816]206
[10809]207void FFmpegDecoder::pause()
208{
209    m_pending_packet.clear();
[9816]210
[10809]211    flushAudioQueue();
212    flushVideoQueue();
213    m_state = PAUSE;
214}
215
216void FFmpegDecoder::resume()
217{
218    m_pending_packet.clear();
219
220    flushAudioQueue();
221    flushVideoQueue();
222    m_state = NORMAL;
223}
224
[9816]225void FFmpegDecoder::findAudioStream()
226{
227    for (unsigned int i = 0; i < m_format_context->nb_streams; ++i)
228    {
229        if (m_format_context->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
230        {
231            m_audio_stream = m_format_context->streams[i];
232            m_audio_index = i;
233            return;
234        }
235    }
236
237    m_audio_stream = 0;
238    m_audio_index = std::numeric_limits<unsigned int>::max();
239}
240
241
242
243void FFmpegDecoder::findVideoStream()
244{
245    for (unsigned int i = 0; i < m_format_context->nb_streams; ++i)
246    {
247        if (m_format_context->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
248        {
249            m_video_stream = m_format_context->streams[i];
250            m_video_index = i;
251            return;
252        }
253    }
254
255    throw std::runtime_error("could not find a video stream");
256}
257
258
259
260inline void FFmpegDecoder::flushAudioQueue()
261{
262    FFmpegPacketClear pc;
263    m_audio_queue.flush(pc);
264}
265
266
267
268inline void FFmpegDecoder::flushVideoQueue()
269{
270    FFmpegPacketClear pc;
271    m_video_queue.flush(pc);
272}
273
274
275
276bool FFmpegDecoder::readNextPacketNormal()
277{
278    AVPacket packet;
279
280    if (! m_pending_packet)
281    {
282        bool end_of_stream = false;
283
284        // Read the next frame packet
285        if (av_read_frame(m_format_context.get(), &packet) < 0)
286        {
287            if (url_ferror(m_format_context->pb) == 0)
288                end_of_stream = true;
289            else
290                throw std::runtime_error("av_read_frame() failed");
291        }
292
293        if (end_of_stream)
294        {
295            // If we reach the end of the stream, change the decoder state
296            if (loop())
297                rewindButDontFlushQueues();
298            else
299                m_state = END_OF_STREAM;
300
301            return false;
302        }
303        else
304        {
305            // Make the packet data available beyond av_read_frame() logical scope.
306            if (av_dup_packet(&packet) < 0)
307                throw std::runtime_error("av_dup_packet() failed");
308
309            m_pending_packet = FFmpegPacket(packet);           
310        }
311    }
312
313    // Send data packet
314    if (m_pending_packet.type == FFmpegPacket::PACKET_DATA)
315    {
316        if (m_pending_packet.packet.stream_index == m_audio_index)
317        {
318            if (m_audio_queue.timedPush(m_pending_packet, 10)) {
319                m_pending_packet.release();
320                return true;
321            }
322        }
323        else if (m_pending_packet.packet.stream_index == m_video_index)
324        {
325            if (m_video_queue.timedPush(m_pending_packet, 10)) {
326                m_pending_packet.release();
327                return true;
328            }
329        }
330        else
331        {
332            m_pending_packet.clear();
333            return true;
334        }
335    }
336
337    return false;
338}
339
340
341
342bool FFmpegDecoder::readNextPacketEndOfStream()
343{
344    const FFmpegPacket packet(FFmpegPacket::PACKET_END_OF_STREAM);
345
346    m_audio_queue.timedPush(packet, 10);
347    m_video_queue.timedPush(packet, 10);
348
349    return false;
350}
351   
352
353
354bool FFmpegDecoder::readNextPacketRewinding()
355{
356    const FFmpegPacket packet(FFmpegPacket::PACKET_FLUSH);
357
358    if (m_audio_queue.timedPush(packet, 10) && m_video_queue.timedPush(packet, 10))
359        m_state = NORMAL;
360
361    return false;
362}
363
364
365
366void FFmpegDecoder::rewindButDontFlushQueues()
367{
368    const AVRational AvTimeBaseQ = { 1, AV_TIME_BASE }; // = AV_TIME_BASE_Q
369
[10422]370    const int64_t pos = int64_t(m_clocks.getStartTime() * double(AV_TIME_BASE));
[9816]371    const int64_t seek_target = av_rescale_q(pos, AvTimeBaseQ, m_video_stream->time_base);
372
373    if (av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/) < 0)
374        throw std::runtime_error("av_seek_frame failed()");
375
376    m_state = REWINDING;
377}
378
[10809]379bool FFmpegDecoder::readNextPacketSeeking()
380{
381    const FFmpegPacket packet(FFmpegPacket::PACKET_FLUSH);
[9816]382
[10809]383    if (m_audio_queue.timedPush(packet, 10) && m_video_queue.timedPush(packet, 10))
384        m_state = NORMAL;
[9816]385
[10809]386    return false;   
387}
388
389void FFmpegDecoder::seekButDontFlushQueues(double time)
390{
391    const AVRational AvTimeBaseQ = { 1, AV_TIME_BASE }; // = AV_TIME_BASE_Q
392
393    const int64_t pos = int64_t(m_clocks.getStartTime()+time * double(AV_TIME_BASE));
394    const int64_t seek_target = av_rescale_q(pos, AvTimeBaseQ, m_video_stream->time_base);
395
396    if (av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/) < 0)
397        throw std::runtime_error("av_seek_frame failed()");
398
399    m_state = SEEKING;   
400}
401
402
403
[9816]404} // namespace osgFFmpeg
Note: See TracBrowser for help on using the browser.