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

Revision 9969, 7.5 kB (checked in by robert, 6 years ago)

Added new virtual reseveElements, setElement, getElment and addElement methods to DrawElements?
to make is easier to write code that can work on DrawElementUByte, UShort or UInt.

Changed the osgTerrain::GeometryTechnique? so that it automatically chooses
the use of DrawElementUShort or DrawElementsUInt accordining to the size of the tile.

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
[9865]58            formatParams.width = 640;
59            formatParams.height = 480;
[9969]60#else
61            formatParams.width = 640;
62            formatParams.height = 480;
63#endif           
[9865]64            formatParams.time_base.num = 1;
[9969]65            formatParams.time_base.den = 15;
[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.