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

Revision 9990, 7.5 kB (checked in by robert, 5 years ago)

Reduced the default live video feed size to accomodate reading from the Minoru stereo wecam.

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{
[9869]34    close(true);
[9816]35}
36
37
38bool FFmpegDecoder::open(const std::string & filename)
39{
40    try
41    {
42        // Open video file
43        AVFormatContext * p_format_context = 0;
44
[9865]45        if (filename.compare(0, 5, "/dev/")==0)
46        {
47            avdevice_register_all();
48       
49            osg::notify(osg::NOTICE)<<"Attempting to stream "<<filename<<std::endl;
[9816]50
[9865]51            AVFormatParameters formatParams;
52            memset(&formatParams, 0, sizeof(AVFormatParameters));
53            AVInputFormat *iformat;
54
55            formatParams.channel = 0;
56            formatParams.standard = 0;
[9969]57#if 1
[9990]58            formatParams.width = 320;
59            formatParams.height = 240;
[9969]60#else
61            formatParams.width = 640;
62            formatParams.height = 480;
63#endif           
[9865]64            formatParams.time_base.num = 1;
[9990]65            formatParams.time_base.den = 30;
[9865]66
67            iformat = av_find_input_format("video4linux2");
68           
69            if (iformat)
70            {
71                osg::notify(osg::NOTICE)<<"Found input format"<<std::endl;
72            }
73            else
74            {
75                osg::notify(osg::NOTICE)<<"Failed to find input_format"<<std::endl;
76            }
77
78            if (av_open_input_file(&p_format_context, filename.c_str(), iformat, 0, &formatParams) != 0)
79                throw std::runtime_error("av_open_input_file() failed");
80        }
81        else
82        {
83            if (av_open_input_file(&p_format_context, filename.c_str(), 0, 0, 0) !=0 )
84                throw std::runtime_error("av_open_input_file() failed");
85        }
86       
[9826]87        m_format_context.reset(p_format_context);
[9816]88
89        // Retrieve stream info
90        if (av_find_stream_info(p_format_context) < 0)
91            throw std::runtime_error("av_find_stream_info() failed");
92
93        m_duration = double(m_format_context->duration) / AV_TIME_BASE;
94        m_start = double(m_format_context->start_time) / AV_TIME_BASE;
95
96        // TODO move this elsewhere
97        m_clocks.reset(m_start);
98
99        // Dump info to stderr
100        dump_format(p_format_context, 0, filename.c_str(), false);
101
102        // Find and open the first video and audio streams (note that audio stream is optional and only opened if possible)
103
104        findVideoStream();
105        findAudioStream();
106
107        m_video_decoder.open(m_video_stream);
108
109        try
110        {
111            m_audio_decoder.open(m_audio_stream);
112        }
113
114        catch (const std::runtime_error & error)
115        {
116            osg::notify(osg::WARN) << "FFmpegImageStream::open audio failed, audio stream will be disabled: " << error.what() << std::endl;
117        }
118    }
119
120    catch (const std::runtime_error & error)
121    {
122        osg::notify(osg::WARN) << "FFmpegImageStream::open : " << error.what() << std::endl;
123        return false;
124    }
125   
126    return true;
127}
128
129
130
[9869]131void FFmpegDecoder::close(bool waitForThreadToExit)
[9816]132{
133    flushAudioQueue();
134    flushVideoQueue();
[9869]135   
136    m_audio_decoder.close(waitForThreadToExit);
137    m_video_decoder.close(waitForThreadToExit);
[9816]138}
139
140
141
142bool FFmpegDecoder::readNextPacket()
143{
144    switch (m_state)
145    {
146    case NORMAL:
147        return readNextPacketNormal();
148
149    case END_OF_STREAM:
150        return readNextPacketEndOfStream();
151
152    case REWINDING:
153        return readNextPacketRewinding();
154
155    default:
156        assert(false);
157        return false;
158    }
159}
160
161
162
163void FFmpegDecoder::rewind()
164{
165    m_pending_packet.clear();
166
167    flushAudioQueue();
168    flushVideoQueue();
169    rewindButDontFlushQueues();
170}
171
172
173
174
175void FFmpegDecoder::findAudioStream()
176{
177    for (unsigned int i = 0; i < m_format_context->nb_streams; ++i)
178    {
179        if (m_format_context->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
180        {
181            m_audio_stream = m_format_context->streams[i];
182            m_audio_index = i;
183            return;
184        }
185    }
186
187    m_audio_stream = 0;
188    m_audio_index = std::numeric_limits<unsigned int>::max();
189}
190
191
192
193void FFmpegDecoder::findVideoStream()
194{
195    for (unsigned int i = 0; i < m_format_context->nb_streams; ++i)
196    {
197        if (m_format_context->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
198        {
199            m_video_stream = m_format_context->streams[i];
200            m_video_index = i;
201            return;
202        }
203    }
204
205    throw std::runtime_error("could not find a video stream");
206}
207
208
209
210inline void FFmpegDecoder::flushAudioQueue()
211{
212    FFmpegPacketClear pc;
213    m_audio_queue.flush(pc);
214}
215
216
217
218inline void FFmpegDecoder::flushVideoQueue()
219{
220    FFmpegPacketClear pc;
221    m_video_queue.flush(pc);
222}
223
224
225
226bool FFmpegDecoder::readNextPacketNormal()
227{
228    AVPacket packet;
229
230    if (! m_pending_packet)
231    {
232        bool end_of_stream = false;
233
234        // Read the next frame packet
235        if (av_read_frame(m_format_context.get(), &packet) < 0)
236        {
237            if (url_ferror(m_format_context->pb) == 0)
238                end_of_stream = true;
239            else
240                throw std::runtime_error("av_read_frame() failed");
241        }
242
243        if (end_of_stream)
244        {
245            // If we reach the end of the stream, change the decoder state
246            if (loop())
247                rewindButDontFlushQueues();
248            else
249                m_state = END_OF_STREAM;
250
251            return false;
252        }
253        else
254        {
255            // Make the packet data available beyond av_read_frame() logical scope.
256            if (av_dup_packet(&packet) < 0)
257                throw std::runtime_error("av_dup_packet() failed");
258
259            m_pending_packet = FFmpegPacket(packet);           
260        }
261    }
262
263    // Send data packet
264    if (m_pending_packet.type == FFmpegPacket::PACKET_DATA)
265    {
266        if (m_pending_packet.packet.stream_index == m_audio_index)
267        {
268            if (m_audio_queue.timedPush(m_pending_packet, 10)) {
269                m_pending_packet.release();
270                return true;
271            }
272        }
273        else if (m_pending_packet.packet.stream_index == m_video_index)
274        {
275            if (m_video_queue.timedPush(m_pending_packet, 10)) {
276                m_pending_packet.release();
277                return true;
278            }
279        }
280        else
281        {
282            m_pending_packet.clear();
283            return true;
284        }
285    }
286
287    return false;
288}
289
290
291
292bool FFmpegDecoder::readNextPacketEndOfStream()
293{
294    const FFmpegPacket packet(FFmpegPacket::PACKET_END_OF_STREAM);
295
296    m_audio_queue.timedPush(packet, 10);
297    m_video_queue.timedPush(packet, 10);
298
299    return false;
300}
301   
302
303
304bool FFmpegDecoder::readNextPacketRewinding()
305{
306    const FFmpegPacket packet(FFmpegPacket::PACKET_FLUSH);
307
308    if (m_audio_queue.timedPush(packet, 10) && m_video_queue.timedPush(packet, 10))
309        m_state = NORMAL;
310
311    return false;
312}
313
314
315
316void FFmpegDecoder::rewindButDontFlushQueues()
317{
318    const AVRational AvTimeBaseQ = { 1, AV_TIME_BASE }; // = AV_TIME_BASE_Q
319
320    const int64_t pos = m_clocks.getStartTime() * AV_TIME_BASE;
321    const int64_t seek_target = av_rescale_q(pos, AvTimeBaseQ, m_video_stream->time_base);
322
323    if (av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/) < 0)
324        throw std::runtime_error("av_seek_frame failed()");
325
326    m_state = REWINDING;
327}
328
329
330
331} // namespace osgFFmpeg
Note: See TracBrowser for help on using the browser.