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

Revision 9869, 7.4 kB (checked in by robert, 5 years ago)

Fixed thread exit problems

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