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

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

From Tanguy Fautre,

Clean up of the FFmpeg plugin's class API/AudioStream API.
Implementation of isImageTransparent().
Implementation of Image:g/setPixelAspectRatio()

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    uint8_t * dst_buffer = reinterpret_cast<uint8_t*>(buffer);
134
135    while (size != 0)
136    {
137        if (m_audio_buf_index == m_audio_buf_size)
138        {
139            m_audio_buf_index = 0;
140
141            // Pre-fetch audio buffer is empty, refill it.
142            const size_t bytes_decoded = decodeFrame(&m_audio_buffer[0], m_audio_buffer.size());
143
144            // If nothing could be decoded (e.g. error or no packet available), output a bit of silence
145            if (bytes_decoded == 0)
146            {
147                m_audio_buf_size = std::min(Buffer::size_type(1024), m_audio_buffer.size());
148                memset(&m_audio_buffer[0], 0, m_audio_buf_size);
149            }
150            else
151            {
152                m_audio_buf_size = bytes_decoded;
153            }
154        }
155
156        const size_t fill_size = std::min(m_audio_buf_size - m_audio_buf_index, size);
157
158        memcpy(dst_buffer, &m_audio_buffer[m_audio_buf_index], fill_size);
159
160        size -= fill_size;
161        dst_buffer += fill_size;
162
163        m_audio_buf_index += fill_size;
164
165        adjustBufferEndTps(fill_size);
166    }
167}
168
169
170
171void FFmpegDecoderAudio::decodeLoop()
172{
173    const bool skip_audio = ! validContext() || ! m_audio_sink.valid();
174   
175    if (! skip_audio && ! m_audio_sink->playing())
176    {
177        m_clocks.audioSetDelay(m_audio_sink->getDelay());
178        m_audio_sink->startPlaying();
179    }
180    else
181    {
182        m_clocks.audioDisable();
183    }
184
185    while (! m_exit)
186    {
187        // If skipping audio, make sure the audio stream is still consumed.
188        if (skip_audio)
189        {
190            bool is_empty;
191            FFmpegPacket packet = m_packets.timedPop(is_empty, 10);
192
193            if (packet.valid())
194                packet.clear();
195        }
196
197        // Else, just idle in this thread.
198        // Note: If m_audio_sink has an audio callback, this thread will still be awaken
199        // from time to time to refill the audio buffer.
200        else
201        {
202            OpenThreads::Thread::microSleep(10000);
203        }
204    }
205}
206
207
208
209void FFmpegDecoderAudio::adjustBufferEndTps(const size_t buffer_size)
210{
211    int sample_size = nbChannels() * frequency();
212
213    switch (sampleFormat())
214    {
[9827]215    case osg::AudioStream::SAMPLE_FORMAT_U8:
[9816]216        sample_size *= 1;
217        break;
218
[9827]219    case osg::AudioStream::SAMPLE_FORMAT_S16:
[9816]220        sample_size *= 2;
221        break;
222
[9827]223    case osg::AudioStream::SAMPLE_FORMAT_S24:
[9816]224        sample_size *= 3;
225        break;
226
[9827]227    case osg::AudioStream::SAMPLE_FORMAT_S32:
[9816]228        sample_size *= 4;
229        break;
230
[9827]231    case osg::AudioStream::SAMPLE_FORMAT_F32:
[9816]232        sample_size *= 4;
233        break;
234
235    default:
236        throw std::runtime_error("unsupported audio sample format");
237    }
238
239    m_clocks.audioAdjustBufferEndPts(double(buffer_size) / double(sample_size));
240}
241
242
243
244size_t FFmpegDecoderAudio::decodeFrame(void * const buffer, const size_t size)
245{
246    for (;;)
247    {
248        // Decode current packet
249
250        while (m_bytes_remaining > 0)
251        {
252            int data_size = size;
253
254            const int bytes_decoded = avcodec_decode_audio2(m_context, reinterpret_cast<int16_t*>(buffer), &data_size, m_packet_data, m_bytes_remaining);
255
256            if (bytes_decoded < 0)
257            {
258                // if error, skip frame
259                m_bytes_remaining = 0;
260                break;
261            }
262
263            m_bytes_remaining -= bytes_decoded;
264            m_packet_data += bytes_decoded;
265
266            // If we have some data, return it and come back for more later.
267            if (data_size > 0)
268                return data_size;
269        }
270
271        // Get next packet
272
273        if (m_packet.valid())
274            m_packet.clear();
275
276        if (m_exit)
277            return 0;
278
279        bool is_empty = true;
280        m_packet = m_packets.tryPop(is_empty);
281
282        if (is_empty)
283            return 0;
284
285        if (m_packet.type == FFmpegPacket::PACKET_DATA)
286        {
287            if (m_packet.packet.pts != AV_NOPTS_VALUE)
288            {
289                const double pts = av_q2d(m_stream->time_base) * m_packet.packet.pts;
290                m_clocks.audioSetBufferEndPts(pts);
291            }
292
293            m_bytes_remaining = m_packet.packet.size;
294            m_packet_data = m_packet.packet.data;
295        }
296        else if (m_packet.type == FFmpegPacket::PACKET_END_OF_STREAM)
297        {
298            m_end_of_stream = true;
299        }
300        else if (m_packet.type == FFmpegPacket::PACKET_FLUSH)
301        {
302            avcodec_flush_buffers(m_context);
303            m_clocks.rewindAudio();
304        }
305
306        // just output silence when we reached the end of stream
307        if (m_end_of_stream)
308        {
309            memset(buffer, 0, size);
310            return size;
311        }
312    }
313}
314
315
316
317} // namespace osgFFmpeg
Note: See TracBrowser for help on using the browser.