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

Revision 13041, 10.5 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

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