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

Revision 10030, 8.6 kB (checked in by robert, 6 years ago)

From Jean Sebastien Guay, added error reporting handling of wider range of video formats.

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
169    case END_OF_STREAM:
170        return readNextPacketEndOfStream();
171
172    case REWINDING:
173        return readNextPacketRewinding();
174
175    default:
176        assert(false);
177        return false;
178    }
179}
180
181
182
183void FFmpegDecoder::rewind()
184{
185    m_pending_packet.clear();
186
187    flushAudioQueue();
188    flushVideoQueue();
189    rewindButDontFlushQueues();
190}
191
192
193
194
195void FFmpegDecoder::findAudioStream()
196{
197    for (unsigned int i = 0; i < m_format_context->nb_streams; ++i)
198    {
199        if (m_format_context->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
200        {
201            m_audio_stream = m_format_context->streams[i];
202            m_audio_index = i;
203            return;
204        }
205    }
206
207    m_audio_stream = 0;
208    m_audio_index = std::numeric_limits<unsigned int>::max();
209}
210
211
212
213void FFmpegDecoder::findVideoStream()
214{
215    for (unsigned int i = 0; i < m_format_context->nb_streams; ++i)
216    {
217        if (m_format_context->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
218        {
219            m_video_stream = m_format_context->streams[i];
220            m_video_index = i;
221            return;
222        }
223    }
224
225    throw std::runtime_error("could not find a video stream");
226}
227
228
229
230inline void FFmpegDecoder::flushAudioQueue()
231{
232    FFmpegPacketClear pc;
233    m_audio_queue.flush(pc);
234}
235
236
237
238inline void FFmpegDecoder::flushVideoQueue()
239{
240    FFmpegPacketClear pc;
241    m_video_queue.flush(pc);
242}
243
244
245
246bool FFmpegDecoder::readNextPacketNormal()
247{
248    AVPacket packet;
249
250    if (! m_pending_packet)
251    {
252        bool end_of_stream = false;
253
254        // Read the next frame packet
255        if (av_read_frame(m_format_context.get(), &packet) < 0)
256        {
257            if (url_ferror(m_format_context->pb) == 0)
258                end_of_stream = true;
259            else
260                throw std::runtime_error("av_read_frame() failed");
261        }
262
263        if (end_of_stream)
264        {
265            // If we reach the end of the stream, change the decoder state
266            if (loop())
267                rewindButDontFlushQueues();
268            else
269                m_state = END_OF_STREAM;
270
271            return false;
272        }
273        else
274        {
275            // Make the packet data available beyond av_read_frame() logical scope.
276            if (av_dup_packet(&packet) < 0)
277                throw std::runtime_error("av_dup_packet() failed");
278
279            m_pending_packet = FFmpegPacket(packet);           
280        }
281    }
282
283    // Send data packet
284    if (m_pending_packet.type == FFmpegPacket::PACKET_DATA)
285    {
286        if (m_pending_packet.packet.stream_index == m_audio_index)
287        {
288            if (m_audio_queue.timedPush(m_pending_packet, 10)) {
289                m_pending_packet.release();
290                return true;
291            }
292        }
293        else if (m_pending_packet.packet.stream_index == m_video_index)
294        {
295            if (m_video_queue.timedPush(m_pending_packet, 10)) {
296                m_pending_packet.release();
297                return true;
298            }
299        }
300        else
301        {
302            m_pending_packet.clear();
303            return true;
304        }
305    }
306
307    return false;
308}
309
310
311
312bool FFmpegDecoder::readNextPacketEndOfStream()
313{
314    const FFmpegPacket packet(FFmpegPacket::PACKET_END_OF_STREAM);
315
316    m_audio_queue.timedPush(packet, 10);
317    m_video_queue.timedPush(packet, 10);
318
319    return false;
320}
321   
322
323
324bool FFmpegDecoder::readNextPacketRewinding()
325{
326    const FFmpegPacket packet(FFmpegPacket::PACKET_FLUSH);
327
328    if (m_audio_queue.timedPush(packet, 10) && m_video_queue.timedPush(packet, 10))
329        m_state = NORMAL;
330
331    return false;
332}
333
334
335
336void FFmpegDecoder::rewindButDontFlushQueues()
337{
338    const AVRational AvTimeBaseQ = { 1, AV_TIME_BASE }; // = AV_TIME_BASE_Q
339
340    const int64_t pos = m_clocks.getStartTime() * AV_TIME_BASE;
341    const int64_t seek_target = av_rescale_q(pos, AvTimeBaseQ, m_video_stream->time_base);
342
343    if (av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/) < 0)
344        throw std::runtime_error("av_seek_frame failed()");
345
346    m_state = REWINDING;
347}
348
349
350
351} // namespace osgFFmpeg
Note: See TracBrowser for help on using the browser.