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

Revision 10892, 7.8 kB (checked in by robert, 5 years ago)

From Julen Garcia, "So here is the code with a proper audio sync (at least in my computer)"

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