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

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

Fixed thread exit problems

RevLine 
[9816]1#include "FFmpegDecoderAudio.hpp"
2
3#include <osg/Notify>
4
5#include <stdexcept>
6#include <string.h>
7
8//DEBUG
9//#include <iostream>
10
11
12
13namespace osgFFmpeg {
14
15
16
17FFmpegDecoderAudio::FFmpegDecoderAudio(PacketQueue & packets, FFmpegClocks & clocks) :
18    m_packets(packets),
19    m_clocks(clocks),
20    m_stream(0),
21    m_context(0),
22    m_packet_data(0),
23    m_bytes_remaining(0),
24    m_audio_buffer((AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2),
25    m_audio_buf_size(0),
26    m_audio_buf_index(0),
27    m_end_of_stream(false),
28    m_exit(false)
29{
30
31}
32
33
34
35FFmpegDecoderAudio::~FFmpegDecoderAudio()
36{
37    if (isRunning())
38    {
39        m_exit = true;
[9869]40#if 0       
41        while(isRunning()) { OpenThreads::YieldCurrentThread(); }
42#else       
[9816]43        join();
[9869]44#endif
[9816]45    }
46}
47
48
49
50void FFmpegDecoderAudio::open(AVStream * const stream)
51{
52    try
53    {
54        // Sound can be optional (i.e. no audio stream is present)
55        if (stream == 0)
56            return;
57
58        m_stream = stream;
59        m_context = stream->codec;
60
61        m_frequency = m_context->sample_rate;
62        m_nb_channels = m_context->channels;
[9827]63        m_sample_format = osg::AudioStream::SampleFormat(m_context->sample_fmt);
[9816]64
65        // Check stream sanity
66        if (m_context->codec_id == CODEC_ID_NONE)
67            throw std::runtime_error("invalid audio codec");;
68
69        // Find the decoder for the audio stream
70        AVCodec * const p_codec = avcodec_find_decoder(m_context->codec_id);
71
72        if (p_codec == 0)
73            throw std::runtime_error("avcodec_find_decoder() failed");
74
75        // Inform the codec that we can handle truncated bitstreams
76        //if (p_codec->capabilities & CODEC_CAP_TRUNCATED)
77        //    m_context->flags |= CODEC_FLAG_TRUNCATED;
78
79        // Open codec
80        if (avcodec_open(m_context, p_codec) < 0)
81            throw std::runtime_error("avcodec_open() failed");
82    }
83
84    catch (...)
85    {
86        m_context = 0;
87        throw;
88    }
89}
90
91
[9869]92void FFmpegDecoderAudio::close(bool waitForThreadToExit)
93{
94    m_exit = true;
95   
96    if (isRunning() && waitForThreadToExit)
97    {
98        while(isRunning()) { OpenThreads::Thread::YieldCurrentThread(); }
99    }
100}
[9816]101
[9869]102
[9816]103void FFmpegDecoderAudio::run()
104{
105    try
106    {
107        decodeLoop();
108    }
109
110    catch (const std::exception & error)
111    {
112        osg::notify(osg::WARN) << "FFmpegDecoderAudio::run : " << error.what() << std::endl;
113    }
114
115    catch (...)
116    {
117        osg::notify(osg::WARN) << "FFmpegDecoderAudio::run : unhandled exception" << std::endl;
118    }
119}
120
121
[9847]122void FFmpegDecoderAudio::setAudioSink(osg::ref_ptr<osg::AudioSink> audio_sink)
[9816]123{
124    // The FFmpegDecoderAudio object takes the responsability of destroying the audio_sink.
[9847]125    osg::notify(osg::NOTICE)<<"Assigning "<<audio_sink<<std::endl;
[9816]126    m_audio_sink = audio_sink;
127}
128
129
130
131void FFmpegDecoderAudio::fillBuffer(void * const buffer, size_t size)
132{
133    size_t filled = 0;
134    uint8_t * dst_buffer = reinterpret_cast<uint8_t*>(buffer);
135
136    while (size != 0)
137    {
138        if (m_audio_buf_index == m_audio_buf_size)
139        {
140            m_audio_buf_index = 0;
141
142            // Pre-fetch audio buffer is empty, refill it.
143            const size_t bytes_decoded = decodeFrame(&m_audio_buffer[0], m_audio_buffer.size());
144
145            // If nothing could be decoded (e.g. error or no packet available), output a bit of silence
146            if (bytes_decoded == 0)
147            {
148                m_audio_buf_size = std::min(Buffer::size_type(1024), m_audio_buffer.size());
149                memset(&m_audio_buffer[0], 0, m_audio_buf_size);
150            }
151            else
152            {
153                m_audio_buf_size = bytes_decoded;
154            }
155        }
156
157        const size_t fill_size = std::min(m_audio_buf_size - m_audio_buf_index, size);
158
159        memcpy(dst_buffer, &m_audio_buffer[m_audio_buf_index], fill_size);
160
161        size -= fill_size;
162        dst_buffer += fill_size;
163
164        m_audio_buf_index += fill_size;
165
166        adjustBufferEndTps(fill_size);
167    }
168}
169
170
171
172void FFmpegDecoderAudio::decodeLoop()
173{
174    const bool skip_audio = ! validContext() || ! m_audio_sink.valid();
175   
176    if (! skip_audio && ! m_audio_sink->playing())
177    {
178        m_clocks.audioSetDelay(m_audio_sink->getDelay());
179        m_audio_sink->startPlaying();
180    }
181    else
182    {
183        m_clocks.audioDisable();
184    }
185
186    while (! m_exit)
187    {
188        // If skipping audio, make sure the audio stream is still consumed.
189        if (skip_audio)
190        {
191            bool is_empty;
192            FFmpegPacket packet = m_packets.timedPop(is_empty, 10);
193
194            if (packet.valid())
195                packet.clear();
196        }
197
198        // Else, just idle in this thread.
199        // Note: If m_audio_sink has an audio callback, this thread will still be awaken
200        // from time to time to refill the audio buffer.
201        else
202        {
203            OpenThreads::Thread::microSleep(10000);
204        }
205    }
206}
207
208
209
210void FFmpegDecoderAudio::adjustBufferEndTps(const size_t buffer_size)
211{
212    int sample_size = nbChannels() * frequency();
213
214    switch (sampleFormat())
215    {
[9827]216    case osg::AudioStream::SAMPLE_FORMAT_U8:
[9816]217        sample_size *= 1;
218        break;
219
[9827]220    case osg::AudioStream::SAMPLE_FORMAT_S16:
[9816]221        sample_size *= 2;
222        break;
223
[9827]224    case osg::AudioStream::SAMPLE_FORMAT_S24:
[9816]225        sample_size *= 3;
226        break;
227
[9827]228    case osg::AudioStream::SAMPLE_FORMAT_S32:
[9816]229        sample_size *= 4;
230        break;
231
[9827]232    case osg::AudioStream::SAMPLE_FORMAT_F32:
[9816]233        sample_size *= 4;
234        break;
235
236    default:
237        throw std::runtime_error("unsupported audio sample format");
238    }
239
240    m_clocks.audioAdjustBufferEndPts(double(buffer_size) / double(sample_size));
241}
242
243
244
245size_t FFmpegDecoderAudio::decodeFrame(void * const buffer, const size_t size)
246{
247    for (;;)
248    {
249        // Decode current packet
250
251        while (m_bytes_remaining > 0)
252        {
253            int data_size = size;
254
255            const int bytes_decoded = avcodec_decode_audio2(m_context, reinterpret_cast<int16_t*>(buffer), &data_size, m_packet_data, m_bytes_remaining);
256
257            if (bytes_decoded < 0)
258            {
259                // if error, skip frame
260                m_bytes_remaining = 0;
261                break;
262            }
263
264            m_bytes_remaining -= bytes_decoded;
265            m_packet_data += bytes_decoded;
266
267            // If we have some data, return it and come back for more later.
268            if (data_size > 0)
269                return data_size;
270        }
271
272        // Get next packet
273
274        if (m_packet.valid())
275            m_packet.clear();
276
277        if (m_exit)
278            return 0;
279
280        bool is_empty = true;
281        m_packet = m_packets.tryPop(is_empty);
282
283        if (is_empty)
284            return 0;
285
286        if (m_packet.type == FFmpegPacket::PACKET_DATA)
287        {
288            if (m_packet.packet.pts != AV_NOPTS_VALUE)
289            {
290                const double pts = av_q2d(m_stream->time_base) * m_packet.packet.pts;
291                m_clocks.audioSetBufferEndPts(pts);
292            }
293
294            m_bytes_remaining = m_packet.packet.size;
295            m_packet_data = m_packet.packet.data;
296        }
297        else if (m_packet.type == FFmpegPacket::PACKET_END_OF_STREAM)
298        {
299            m_end_of_stream = true;
300        }
301        else if (m_packet.type == FFmpegPacket::PACKET_FLUSH)
302        {
303            avcodec_flush_buffers(m_context);
304            m_clocks.rewindAudio();
305        }
306
307        // just output silence when we reached the end of stream
308        if (m_end_of_stream)
309        {
310            memset(buffer, 0, size);
311            return size;
312        }
313    }
314}
315
316
317
318} // namespace osgFFmpeg
Note: See TracBrowser for help on using the browser.