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

Revision 9816, 7.0 kB (checked in by robert, 6 years ago)

From Tanguy Fautre (Aris Technologies), ffmpeg plugin

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