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

Revision 10925, 7.9 kB (checked in by robert, 4 years ago)

Added virtual pause() method into osg::AudioSink? to support pausing of a movie thread and it's associated audio.

Updated osgmovie plugin to use the pause support.

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