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

Revision 9816, 6.2 kB (checked in by robert, 5 years ago)

From Tanguy Fautre (Aris Technologies), ffmpeg plugin

RevLine 
[9816]1
2#include "FFmpegDecoder.hpp"
3
4#include <osg/Notify>
5
6#include <cassert>
7#include <limits>
8#include <stdexcept>
9#include <string.h>
10
11
12
13namespace osgFFmpeg {
14
15
16
17FFmpegDecoder::FFmpegDecoder() :
18    m_audio_stream(0),
19    m_video_stream(0),
20    m_audio_queue(100),
21    m_video_queue(100),
22    m_audio_decoder(m_audio_queue, m_clocks),
23    m_video_decoder(m_video_queue, m_clocks),
24    m_state(NORMAL),
25    m_loop(false)
26{
27
28}
29
30
31
32FFmpegDecoder::~FFmpegDecoder()
33{
34    close();
35}
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
46        if (av_open_input_file(&p_format_context, filename.c_str(), 0, 0, 0) != 0)
47            throw std::runtime_error("av_open_input_file() failed");
48
49        m_format_context.reset(p_format_context, av_close_input_file);
50
51        // Retrieve stream info
52        if (av_find_stream_info(p_format_context) < 0)
53            throw std::runtime_error("av_find_stream_info() failed");
54
55        m_duration = double(m_format_context->duration) / AV_TIME_BASE;
56        m_start = double(m_format_context->start_time) / AV_TIME_BASE;
57
58        // TODO move this elsewhere
59        m_clocks.reset(m_start);
60
61        // Dump info to stderr
62        dump_format(p_format_context, 0, filename.c_str(), false);
63
64        // Find and open the first video and audio streams (note that audio stream is optional and only opened if possible)
65
66        findVideoStream();
67        findAudioStream();
68
69        m_video_decoder.open(m_video_stream);
70
71        try
72        {
73            m_audio_decoder.open(m_audio_stream);
74        }
75
76        catch (const std::runtime_error & error)
77        {
78            osg::notify(osg::WARN) << "FFmpegImageStream::open audio failed, audio stream will be disabled: " << error.what() << std::endl;
79        }
80    }
81
82    catch (const std::runtime_error & error)
83    {
84        osg::notify(osg::WARN) << "FFmpegImageStream::open : " << error.what() << std::endl;
85        return false;
86    }
87   
88    return true;
89}
90
91
92
93void FFmpegDecoder::close()
94{
95    flushAudioQueue();
96    flushVideoQueue();
97}
98
99
100
101bool FFmpegDecoder::readNextPacket()
102{
103    switch (m_state)
104    {
105    case NORMAL:
106        return readNextPacketNormal();
107
108    case END_OF_STREAM:
109        return readNextPacketEndOfStream();
110
111    case REWINDING:
112        return readNextPacketRewinding();
113
114    default:
115        assert(false);
116        return false;
117    }
118}
119
120
121
122void FFmpegDecoder::rewind()
123{
124    m_pending_packet.clear();
125
126    flushAudioQueue();
127    flushVideoQueue();
128    rewindButDontFlushQueues();
129}
130
131
132
133
134void FFmpegDecoder::findAudioStream()
135{
136    for (unsigned int i = 0; i < m_format_context->nb_streams; ++i)
137    {
138        if (m_format_context->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
139        {
140            m_audio_stream = m_format_context->streams[i];
141            m_audio_index = i;
142            return;
143        }
144    }
145
146    m_audio_stream = 0;
147    m_audio_index = std::numeric_limits<unsigned int>::max();
148}
149
150
151
152void FFmpegDecoder::findVideoStream()
153{
154    for (unsigned int i = 0; i < m_format_context->nb_streams; ++i)
155    {
156        if (m_format_context->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
157        {
158            m_video_stream = m_format_context->streams[i];
159            m_video_index = i;
160            return;
161        }
162    }
163
164    throw std::runtime_error("could not find a video stream");
165}
166
167
168
169inline void FFmpegDecoder::flushAudioQueue()
170{
171    FFmpegPacketClear pc;
172    m_audio_queue.flush(pc);
173}
174
175
176
177inline void FFmpegDecoder::flushVideoQueue()
178{
179    FFmpegPacketClear pc;
180    m_video_queue.flush(pc);
181}
182
183
184
185bool FFmpegDecoder::readNextPacketNormal()
186{
187    AVPacket packet;
188
189    if (! m_pending_packet)
190    {
191        bool end_of_stream = false;
192
193        // Read the next frame packet
194        if (av_read_frame(m_format_context.get(), &packet) < 0)
195        {
196            if (url_ferror(m_format_context->pb) == 0)
197                end_of_stream = true;
198            else
199                throw std::runtime_error("av_read_frame() failed");
200        }
201
202        if (end_of_stream)
203        {
204            // If we reach the end of the stream, change the decoder state
205            if (loop())
206                rewindButDontFlushQueues();
207            else
208                m_state = END_OF_STREAM;
209
210            return false;
211        }
212        else
213        {
214            // Make the packet data available beyond av_read_frame() logical scope.
215            if (av_dup_packet(&packet) < 0)
216                throw std::runtime_error("av_dup_packet() failed");
217
218            m_pending_packet = FFmpegPacket(packet);           
219        }
220    }
221
222    // Send data packet
223    if (m_pending_packet.type == FFmpegPacket::PACKET_DATA)
224    {
225        if (m_pending_packet.packet.stream_index == m_audio_index)
226        {
227            if (m_audio_queue.timedPush(m_pending_packet, 10)) {
228                m_pending_packet.release();
229                return true;
230            }
231        }
232        else if (m_pending_packet.packet.stream_index == m_video_index)
233        {
234            if (m_video_queue.timedPush(m_pending_packet, 10)) {
235                m_pending_packet.release();
236                return true;
237            }
238        }
239        else
240        {
241            m_pending_packet.clear();
242            return true;
243        }
244    }
245
246    return false;
247}
248
249
250
251bool FFmpegDecoder::readNextPacketEndOfStream()
252{
253    const FFmpegPacket packet(FFmpegPacket::PACKET_END_OF_STREAM);
254
255    m_audio_queue.timedPush(packet, 10);
256    m_video_queue.timedPush(packet, 10);
257
258    return false;
259}
260   
261
262
263bool FFmpegDecoder::readNextPacketRewinding()
264{
265    const FFmpegPacket packet(FFmpegPacket::PACKET_FLUSH);
266
267    if (m_audio_queue.timedPush(packet, 10) && m_video_queue.timedPush(packet, 10))
268        m_state = NORMAL;
269
270    return false;
271}
272
273
274
275void FFmpegDecoder::rewindButDontFlushQueues()
276{
277    const AVRational AvTimeBaseQ = { 1, AV_TIME_BASE }; // = AV_TIME_BASE_Q
278
279    const int64_t pos = m_clocks.getStartTime() * AV_TIME_BASE;
280    const int64_t seek_target = av_rescale_q(pos, AvTimeBaseQ, m_video_stream->time_base);
281
282    if (av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/) < 0)
283        throw std::runtime_error("av_seek_frame failed()");
284
285    m_state = REWINDING;
286}
287
288
289
290} // namespace osgFFmpeg
Note: See TracBrowser for help on using the browser.