diff options
Diffstat (limited to 'modules/pdp_mp4audiosync.cpp')
-rw-r--r-- | modules/pdp_mp4audiosync.cpp | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/modules/pdp_mp4audiosync.cpp b/modules/pdp_mp4audiosync.cpp new file mode 100644 index 0000000..e364c0f --- /dev/null +++ b/modules/pdp_mp4audiosync.cpp @@ -0,0 +1,505 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is MPEG4IP. + * + * The Initial Developer of the Original Code is Cisco Systems Inc. + * Portions created by Cisco Systems Inc. are + * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved. + * + * Contributor(s): + * Bill May wmay@cisco.com + * + * Adapted to PD/PDP by Yves Degoyon (ydegoyon@free.fr) + */ + +/* + * audio.cpp provides an interface (CPDPAudioSync) between the codec and + * the SDL audio APIs. + */ +#include <stdlib.h> +#include <string.h> +#include "pdp_mp4playersession.h" +#include "pdp_mp4audiosync.h" +#include "player_util.h" +#include "our_config_file.h" +#include "m_pd.h" + +#define audio_message(loglevel, fmt...) message(loglevel, "audiosync", fmt) + +static void pdp_audio_callback (void *userdata, Uint8 *stream, int len) +{ + CPDPAudioSync *a = (CPDPAudioSync *)userdata; + a->audio_callback(stream, len); +} + +CPDPAudioSync::CPDPAudioSync (CPlayerSession *psptr, t_pdp_mp4player *pdp_father) : CAudioSync(psptr) +{ + m_fill_index = m_play_index = 0; + for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { + m_buffer_filled[ix] = 0; + m_sample_buffer[ix] = NULL; + } + m_buffer_size = 0; + m_config_set = 0; + m_audio_initialized = 0; + m_audio_paused = 1; + m_resync_required = 0; + m_dont_fill = 0; + m_consec_no_buffers = 0; + m_audio_waiting_buffer = 0; + m_skipped_buffers = 0; + m_didnt_fill_buffers = 0; + m_play_time = 0 ; + m_buffer_latency = 0; + m_first_time = 1; + m_first_filled = 1; + m_buffer_offset_on = 0; + m_buffer_ts = 0; + m_load_audio_do_next_resync = 0; + m_convert_buffer = NULL; + m_father = pdp_father; +} + +CPDPAudioSync::~CPDPAudioSync (void) +{ + for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { + if (m_sample_buffer[ix] != NULL) + free(m_sample_buffer[ix]); + m_sample_buffer[ix] = NULL; + } + CHECK_AND_FREE(m_convert_buffer); + audio_message(LOG_NOTICE, + "Audio sync skipped %u buffers", + m_skipped_buffers); + audio_message(LOG_NOTICE, "didn't fill %u buffers", m_didnt_fill_buffers); +} + +void CPDPAudioSync::set_config (int freq, + int channels, + int format, + uint32_t sample_size) +{ + if (m_config_set != 0) + return; + + if (format == AUDIO_U8 || format == AUDIO_S8) + m_bytes_per_sample = 1; + else + m_bytes_per_sample = 2; + + if (sample_size == 0) { + int temp; + temp = freq; + while ((temp & 0x1) == 0) temp >>= 1; + sample_size = temp; + while (sample_size < 1024) sample_size *= 2; + while (((sample_size * 1000) % freq) != 0) sample_size *= 2; + } + + m_buffer_size = channels * sample_size * m_bytes_per_sample; + + for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { + m_buffer_filled[ix] = 0; + m_sample_buffer[ix] = (uint8_t *)malloc(2 * m_buffer_size); + } + m_freq = freq; + m_channels = channels; + m_format = format; + if (m_format == AUDIO_U8) { + m_silence = 0x80; + } else { + m_silence = 0x00; + } + m_config_set = 1; + m_msec_per_frame = (sample_size * 1000) / m_freq; + audio_message(LOG_DEBUG, "buffer size %d msec per frame %d", m_buffer_size, m_msec_per_frame); +}; + +uint8_t *CPDPAudioSync::get_audio_buffer (void) +{ + int ret; + int locked = 0; + if (m_dont_fill == 1) { + return (NULL); + } + + if (m_audio_initialized != 0) { + locked = 1; + } + ret = m_buffer_filled[m_fill_index]; + if (ret == 1) { + m_audio_waiting_buffer = 1; + m_audio_waiting_buffer = 0; + if (m_dont_fill != 0) { + return (NULL); + } + locked = 0; + if (m_audio_initialized != 0) { + locked = 1; + } + ret = m_buffer_filled[m_fill_index]; + if (locked) + if (ret == 1) { + post("pdp_mp4audiosync : no buffer"); + return (NULL); + } + } + return (m_sample_buffer[m_fill_index]); +} + +void CPDPAudioSync::load_audio_buffer (uint8_t *from, + uint32_t bytes, + uint64_t ts, + int resync) +{ + uint8_t *to; + uint32_t copied; + copied = 0; + if (m_buffer_offset_on == 0) { + int64_t diff = ts - m_buffer_ts; + + if (m_buffer_ts != 0 && diff > 1) { + m_load_audio_do_next_resync = 1; + audio_message(LOG_DEBUG, "timeslot doesn't match - %llu %llu", + ts, m_buffer_ts); + } + m_buffer_ts = ts; + } else { + int64_t check; + check = ts - m_loaded_next_ts; + if (check > m_msec_per_frame) { + audio_message(LOG_DEBUG, "potential resync at ts "U64" should be ts "U64, + ts, m_loaded_next_ts); + uint32_t left; + left = m_buffer_size - m_buffer_offset_on; + to = get_audio_buffer(); + memset(to + m_buffer_offset_on, 0, left); + filled_audio_buffer(m_buffer_ts, 0); + m_buffer_offset_on = 0; + m_load_audio_do_next_resync = 1; + m_buffer_ts = ts; + } + } + m_loaded_next_ts = bytes * M_64; + m_loaded_next_ts /= m_bytes_per_sample; + m_loaded_next_ts /= m_freq; + m_loaded_next_ts += ts; + + while ( bytes > 0) { + to = get_audio_buffer(); + if (to == NULL) { + return; + } + int copy; + uint32_t left; + + left = m_buffer_size - m_buffer_offset_on; + copy = MIN(left, bytes); + memcpy(to + m_buffer_offset_on, from, copy); + bytes -= copy; + copied += copy; + from += copy; + m_buffer_offset_on += copy; + if (m_buffer_offset_on >= m_buffer_size) { + m_buffer_offset_on = 0; + filled_audio_buffer(m_buffer_ts, resync | m_load_audio_do_next_resync); + m_buffer_ts += m_msec_per_frame; + resync = 0; + m_load_audio_do_next_resync = 0; + } + } + return; +} + +void CPDPAudioSync::filled_audio_buffer (uint64_t ts, int resync) +{ + uint32_t fill_index; + int locked; + // m_dont_fill will be set when we have a pause + if (m_dont_fill == 1) { + return; + } + // resync = 0; + fill_index = m_fill_index; + m_fill_index++; + m_fill_index %= DECODE_BUFFERS_MAX; + + locked = 0; + if (m_audio_initialized != 0) { + locked = 1; + } + if (m_first_filled != 0) { + m_first_filled = 0; + resync = 0; + m_resync_required = 0; + } else { + int64_t diff; + diff = ts - m_last_fill_timestamp; + if (diff - m_msec_per_frame > m_msec_per_frame) { + // have a hole here - don't want to resync + if (diff > ((m_msec_per_frame + 1) * 4)) { + resync = 1; + } else { + // try to fill the holes + m_last_fill_timestamp += m_msec_per_frame + 1; // fill plus extra + int64_t ts_diff; + do { + uint8_t *retbuffer; + // Get and swap buffers. + retbuffer = get_audio_buffer(); + if (retbuffer == NULL) { + return; + } + if (retbuffer != m_sample_buffer[m_fill_index]) { + audio_message(LOG_ERR, "retbuffer not fill index in audio sync"); + return; + } + locked = 0; + if (m_audio_initialized != 0) { + locked = 1; + } + m_sample_buffer[m_fill_index] = m_sample_buffer[fill_index]; + m_sample_buffer[fill_index] = retbuffer; + memset(retbuffer, m_silence, m_buffer_size); + m_buffer_time[fill_index] = m_last_fill_timestamp; + m_buffer_filled[fill_index] = 1; + m_samples_loaded += m_buffer_size; + fill_index++; + fill_index %= DECODE_BUFFERS_MAX; + m_fill_index++; + m_fill_index %= DECODE_BUFFERS_MAX; + audio_message(LOG_NOTICE, "Filling timestamp %llu with silence", + m_last_fill_timestamp); + m_last_fill_timestamp += m_msec_per_frame + 1; // fill plus extra + ts_diff = ts - m_last_fill_timestamp; + audio_message(LOG_DEBUG, "diff is %lld", ts_diff); + } while (ts_diff > 0); + locked = 0; + if (m_audio_initialized != 0) { + locked = 1; + } + } + } else { + if (m_last_fill_timestamp == ts) { + audio_message(LOG_NOTICE, "Repeat timestamp with audio %llu", ts); + return; + } + } + } + m_last_fill_timestamp = ts; + m_buffer_filled[fill_index] = 1; + m_samples_loaded += m_buffer_size; + m_buffer_time[fill_index] = ts; + if (resync) { + m_resync_required = 1; + m_resync_buffer = fill_index; +#ifdef DEBUG_AUDIO_FILL + audio_message(LOG_DEBUG, "Resync from filled_audio_buffer"); +#endif + } + + // Check this - we might not want to do this unless we're resyncing + if (resync) m_psptr->wake_sync_thread(); +#ifdef DEBUG_AUDIO_FILL + audio_message(LOG_DEBUG, "Filling " LLU " %u %u", ts, fill_index, m_samples_loaded); +#endif +} + +void CPDPAudioSync::set_eof(void) +{ + uint8_t *to; + if (m_buffer_offset_on != 0) { + to = get_audio_buffer(); + if (to != NULL) { + uint32_t left; + left = m_buffer_size - m_buffer_offset_on; + memset(to + m_buffer_offset_on, 0, left); + m_buffer_offset_on = 0; + filled_audio_buffer(m_buffer_ts, 0); + m_buffer_ts += m_msec_per_frame; + } + } + CAudioSync::set_eof(); +} + +int CPDPAudioSync::initialize_audio (int have_video) +{ + return (1); +} + +int CPDPAudioSync::is_audio_ready (uint64_t &disptime) +{ + disptime = m_buffer_time[m_play_index]; + return (m_dont_fill == 0 && m_buffer_filled[m_play_index] == 1); +} + +uint64_t CPDPAudioSync::check_audio_sync (uint64_t current_time, int &have_eof) +{ + return (0); +} + +void CPDPAudioSync::audio_callback (Uint8 *stream, int ilen) +{ + int freed_buffer = 0; + uint32_t bufferBytes = (uint32_t)ilen; + uint64_t this_time; + int delay = 0; + int playtime; + +} + +void CPDPAudioSync::play_audio (void) +{ + m_first_time = 1; + m_audio_paused = 0; + m_play_sample_index = 0; +} + +void CPDPAudioSync::flush_sync_buffers (void) +{ + clear_eof(); + m_dont_fill = 1; + if (m_audio_waiting_buffer) { + m_audio_waiting_buffer = 0; + } +} + +void CPDPAudioSync::flush_decode_buffers (void) +{ + int locked = 0; + if (m_audio_initialized != 0) { + locked = 1; + } + m_dont_fill = 0; + m_first_filled = 1; + for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { + m_buffer_filled[ix] = 0; + } + m_buffer_offset_on = 0; + m_play_index = m_fill_index = 0; + m_audio_paused = 1; + m_resync_buffer = 0; + m_samples_loaded = 0; +} + +void CPDPAudioSync::set_volume (int volume) +{ + m_volume = (volume * SDL_MIX_MAXVOLUME)/100; +} + +void CPDPAudioSync::audio_convert_data (void *from, uint32_t samples) +{ + if (m_obtained.format == AUDIO_U8 || m_obtained.format == AUDIO_S8) { + // bytewise - easy + int8_t *src, *dst; + src = (int8_t *) from; + dst = (int8_t *) m_convert_buffer; + if (m_channels == 2) { + // we got 1, wanted 2 + for (uint32_t ix = 0; ix < samples; ix++) { + int16_t sum = *src++; + sum += *src++; + sum /= 2; + if (sum < -128) sum = -128; + else if (sum > 128) sum = 128; + *dst++ = sum & 0xff; + } + } else { + // we got 2, wanted 1 + for (uint32_t ix = 0; ix < samples; ix++) { + *dst++ = *src; + *dst++ = *src++; + } + } + } else { + int16_t *src, *dst; + src = (int16_t *) from; + dst = (int16_t *) m_convert_buffer; + samples /= 2; + if (m_channels == 1) { + // 1 channel to 2 + for (uint32_t ix = 0; ix < samples; ix++) { + *dst++ = *src; + *dst++ = *src; + src++; + } + } else { + // 2 channels to 1 + for (uint32_t ix = 0; ix < samples; ix++) { + int32_t sum = *src++; + sum += *src++; + sum /= 2; + if (sum < -32768) sum = -32768; + else if (sum > 32767) sum = 32767; + *dst++ = sum & 0xffff; + } + } + + } +} + +static void pdp_audio_config (void *ifptr, int freq, + int chans, int format, uint32_t max_buffer_size) +{ + ((CPDPAudioSync *)ifptr)->set_config(freq, + chans, + format, + max_buffer_size); +} + +static uint8_t *pdp_get_audio_buffer (void *ifptr) +{ + return ((CPDPAudioSync *)ifptr)->get_audio_buffer(); +} + +static void pdp_filled_audio_buffer (void *ifptr, + uint64_t ts, + int resync_req) +{ + ((CPDPAudioSync *)ifptr)->filled_audio_buffer(ts, + resync_req); +} + +static void pdp_load_audio_buffer (void *ifptr, + uint8_t *from, + uint32_t bytes, + uint64_t ts, + int resync) +{ + ((CPDPAudioSync *)ifptr)->load_audio_buffer(from, + bytes, + ts, + resync); +} + +audio_vft_t audio_vft = { + message, + pdp_audio_config, + pdp_get_audio_buffer, + pdp_filled_audio_buffer, + pdp_load_audio_buffer +}; + +audio_vft_t *get_audio_vft (void) +{ + return &audio_vft; +} + +CPDPAudioSync *pdp_create_audio_sync (CPlayerSession *psptr, t_pdp_mp4player *pdp_father) +{ + return new CPDPAudioSync(psptr, pdp_father); +} + +int do_we_have_audio (void) +{ + return 1; +} |