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

Revision 13041, 9.5 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
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
15static int decode_audio(AVCodecContext *avctx, int16_t *samples,
16                         int *frame_size_ptr,
17                         const uint8_t *buf, int buf_size)
18{
19#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=32)
20
21    // following code segment copied from ffmpeg's avcodec_decode_audio2()
22    // implementation to avoid warnings about deprecated function usage.
23    AVPacket avpkt;
24    av_init_packet(&avpkt);
25    avpkt.data = const_cast<uint8_t *>(buf);
26    avpkt.size = buf_size;
27
28    return avcodec_decode_audio3(avctx, samples, frame_size_ptr, &avpkt);
29#else
30    // fallback for older versions of ffmpeg that don't have avcodec_decode_audio3.
31    return avcodec_decode_audio2(avctx, samples, frame_size_ptr, buf, buf_size);
32#endif
33}
34
35
36FFmpegDecoderAudio::FFmpegDecoderAudio(PacketQueue & packets, FFmpegClocks & clocks) :
37    m_packets(packets),
38    m_clocks(clocks),
39    m_stream(0),
40    m_context(0),
41    m_packet_data(0),
42    m_bytes_remaining(0),
43    m_audio_buffer((AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2),
44    m_audio_buf_size(0),
45    m_audio_buf_index(0),
46    m_end_of_stream(false),
47    m_paused(true),
48    m_exit(false)
49{
50
51}
52
53
54
55FFmpegDecoderAudio::~FFmpegDecoderAudio()
56{
57    this->close(true);
58}
59
60
61
62void FFmpegDecoderAudio::open(AVStream * const stream)
63{
64    try
65    {
66        // Sound can be optional (i.e. no audio stream is present)
67        if (stream == 0)
68            return;
69
70        m_stream = stream;
71        m_context = stream->codec;
72
73        m_frequency = m_context->sample_rate;
74        m_nb_channels = m_context->channels;
75        switch (m_context->sample_fmt)
76        {
77        case AV_SAMPLE_FMT_NONE:
78            throw std::runtime_error("invalid audio format AV_SAMPLE_FMT_NONE");
79        case AV_SAMPLE_FMT_U8:
80            m_sample_format = osg::AudioStream::SAMPLE_FORMAT_U8;
81            break;
82        case AV_SAMPLE_FMT_S16:
83            m_sample_format = osg::AudioStream::SAMPLE_FORMAT_S16;
84            break;
85        case AV_SAMPLE_FMT_S32:
86            m_sample_format = osg::AudioStream::SAMPLE_FORMAT_S32;
87            break;
88        case AV_SAMPLE_FMT_FLT:
89            m_sample_format = osg::AudioStream::SAMPLE_FORMAT_F32;
90            break;
91        case AV_SAMPLE_FMT_DBL:
92            throw std::runtime_error("unhandled audio format AV_SAMPLE_FMT_DBL");
93        default:
94            throw std::runtime_error("unknown audio format");
95        }
96
97        // Check stream sanity
98        if (m_context->codec_id == CODEC_ID_NONE)
99            throw std::runtime_error("invalid audio codec");;
100
101        // Find the decoder for the audio stream
102        AVCodec * const p_codec = avcodec_find_decoder(m_context->codec_id);
103
104        if (p_codec == 0)
105            throw std::runtime_error("avcodec_find_decoder() failed");
106
107        // Inform the codec that we can handle truncated bitstreams
108        //if (p_codec->capabilities & CODEC_CAP_TRUNCATED)
109        //    m_context->flags |= CODEC_FLAG_TRUNCATED;
110
111        // Open codec
112        if (avcodec_open(m_context, p_codec) < 0)
113            throw std::runtime_error("avcodec_open() failed");
114    }
115
116    catch (...)
117    {
118        m_context = 0;
119        throw;
120    }
121}
122
123void FFmpegDecoderAudio::pause(bool pause)
124{
125    if (pause != m_paused)
126    {
127        m_paused = pause;
128        if (m_audio_sink.valid())
129        {
130            if (m_paused) m_audio_sink->pause();
131            else m_audio_sink->play();
132        }
133    }
134}
135
136void FFmpegDecoderAudio::close(bool waitForThreadToExit)
137{
138    if (isRunning())
139    {
140        m_exit = true;
141        if (waitForThreadToExit)
142            join();
143    }
144}
145
146void FFmpegDecoderAudio::setVolume(float volume)
147{
148    if (m_audio_sink.valid())
149    {
150        m_audio_sink->setVolume(volume);
151    }
152}
153
154float FFmpegDecoderAudio::getVolume() const
155{
156    if (m_audio_sink.valid())
157    {
158        return m_audio_sink->getVolume();
159    }
160    return 0.0f;
161}
162
163void FFmpegDecoderAudio::run()
164{
165    try
166    {
167        decodeLoop();
168    }
169
170    catch (const std::exception & error)
171    {
172        OSG_WARN << "FFmpegDecoderAudio::run : " << error.what() << std::endl;
173    }
174
175    catch (...)
176    {
177        OSG_WARN << "FFmpegDecoderAudio::run : unhandled exception" << std::endl;
178    }
179}
180
181
182void FFmpegDecoderAudio::setAudioSink(osg::ref_ptr<osg::AudioSink> audio_sink)
183{
184    // The FFmpegDecoderAudio object takes the responsability of destroying the audio_sink.
185    OSG_NOTICE<<"Assigning "<<audio_sink<<std::endl;
186    m_audio_sink = audio_sink;
187}
188
189
190
191void FFmpegDecoderAudio::fillBuffer(void * const buffer, size_t size)
192{
193    uint8_t * dst_buffer = reinterpret_cast<uint8_t*>(buffer);
194
195    while (size != 0)
196    {
197        if (m_audio_buf_index == m_audio_buf_size)
198        {
199            m_audio_buf_index = 0;
200
201            // Pre-fetch audio buffer is empty, refill it.
202            const size_t bytes_decoded = decodeFrame(&m_audio_buffer[0], m_audio_buffer.size());
203
204            // If nothing could be decoded (e.g. error or no packet available), output a bit of silence
205            if (bytes_decoded == 0)
206            {
207                m_audio_buf_size = std::min(Buffer::size_type(1024), m_audio_buffer.size());
208                memset(&m_audio_buffer[0], 0, m_audio_buf_size);
209            }
210            else
211            {
212                m_audio_buf_size = bytes_decoded;
213            }
214        }
215
216        const size_t fill_size = std::min(m_audio_buf_size - m_audio_buf_index, size);
217
218        memcpy(dst_buffer, &m_audio_buffer[m_audio_buf_index], fill_size);
219
220        size -= fill_size;
221        dst_buffer += fill_size;
222
223        m_audio_buf_index += fill_size;
224
225        adjustBufferEndTps(fill_size);
226    }
227}
228
229
230
231void FFmpegDecoderAudio::decodeLoop()
232{
233    const bool skip_audio = ! validContext() || ! m_audio_sink.valid();
234
235    if (! skip_audio && ! m_audio_sink->playing())
236    {
237        m_clocks.audioSetDelay(m_audio_sink->getDelay());
238        m_audio_sink->play();
239    }
240    else
241    {
242        m_clocks.audioDisable();
243    }
244
245    while (! m_exit)
246    {
247
248        if(m_paused)
249        {
250            m_clocks.pause(true);
251            m_pause_timer.setStartTick();
252
253            while(m_paused && !m_exit)
254            {
255                microSleep(10000);
256            }
257
258            m_clocks.setPauseTime(m_pause_timer.time_s());
259            m_clocks.pause(false);
260        }
261
262        // If skipping audio, make sure the audio stream is still consumed.
263        if (skip_audio)
264        {
265            bool is_empty;
266            FFmpegPacket packet = m_packets.timedPop(is_empty, 10);
267
268            if (packet.valid())
269                packet.clear();
270        }
271        // Else, just idle in this thread.
272        // Note: If m_audio_sink has an audio callback, this thread will still be awaken
273        // from time to time to refill the audio buffer.
274        else
275        {
276            OpenThreads::Thread::microSleep(10000);
277        }
278    }
279}
280
281
282
283void FFmpegDecoderAudio::adjustBufferEndTps(const size_t buffer_size)
284{
285    int sample_size = nbChannels() * frequency();
286
287    switch (sampleFormat())
288    {
289    case osg::AudioStream::SAMPLE_FORMAT_U8:
290        sample_size *= 1;
291        break;
292
293    case osg::AudioStream::SAMPLE_FORMAT_S16:
294        sample_size *= 2;
295        break;
296
297    case osg::AudioStream::SAMPLE_FORMAT_S24:
298        sample_size *= 3;
299        break;
300
301    case osg::AudioStream::SAMPLE_FORMAT_S32:
302        sample_size *= 4;
303        break;
304
305    case osg::AudioStream::SAMPLE_FORMAT_F32:
306        sample_size *= 4;
307        break;
308
309    default:
310        throw std::runtime_error("unsupported audio sample format");
311    }
312
313    m_clocks.audioAdjustBufferEndPts(double(buffer_size) / double(sample_size));
314}
315
316
317
318size_t FFmpegDecoderAudio::decodeFrame(void * const buffer, const size_t size)
319{
320    for (;;)
321    {
322        // Decode current packet
323
324        while (m_bytes_remaining > 0)
325        {
326            int data_size = size;
327
328            const int bytes_decoded = decode_audio(m_context, reinterpret_cast<int16_t*>(buffer), &data_size, m_packet_data, m_bytes_remaining);
329
330            if (bytes_decoded < 0)
331            {
332                // if error, skip frame
333                m_bytes_remaining = 0;
334                break;
335            }
336
337            m_bytes_remaining -= bytes_decoded;
338            m_packet_data += bytes_decoded;
339
340            // If we have some data, return it and come back for more later.
341            if (data_size > 0)
342                return data_size;
343        }
344
345        // Get next packet
346
347        if (m_packet.valid())
348            m_packet.clear();
349
350        if (m_exit)
351            return 0;
352
353        bool is_empty = true;
354        m_packet = m_packets.tryPop(is_empty);
355
356        if (is_empty)
357            return 0;
358
359        if (m_packet.type == FFmpegPacket::PACKET_DATA)
360        {
361            if (m_packet.packet.pts != int64_t(AV_NOPTS_VALUE))
362            {
363                const double pts = av_q2d(m_stream->time_base) * m_packet.packet.pts;
364                m_clocks.audioSetBufferEndPts(pts);
365            }
366
367            m_bytes_remaining = m_packet.packet.size;
368            m_packet_data = m_packet.packet.data;
369        }
370        else if (m_packet.type == FFmpegPacket::PACKET_END_OF_STREAM)
371        {
372            m_end_of_stream = true;
373        }
374        else if (m_packet.type == FFmpegPacket::PACKET_FLUSH)
375        {
376            avcodec_flush_buffers(m_context);
377        }
378
379        // just output silence when we reached the end of stream
380        if (m_end_of_stream)
381        {
382            memset(buffer, 0, size);
383            return size;
384        }
385    }
386}
387
388
389
390} // namespace osgFFmpeg
Note: See TracBrowser for help on using the browser.