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

Revision 9827, 7.1 kB (checked in by robert, 5 years ago)

Introduced osg::AudioStream? class to help manage audio streams coming in from movie reading plugins

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