static void mad_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { mad_decoder_t *this = (mad_decoder_t *) this_gen; int bytes_in_buffer_at_pts; lprintf ("decode data, size: %d, decoder_flags: %d\n", buf->size, buf->decoder_flags); if (buf->size>(INPUT_BUF_SIZE-this->bytes_in_buffer)) { xprintf (this->xstream->xine, XINE_VERBOSITY_DEBUG, "libmad: ALERT input buffer too small (%d bytes, %d avail)!\n", buf->size, INPUT_BUF_SIZE-this->bytes_in_buffer); buf->size = INPUT_BUF_SIZE-this->bytes_in_buffer; } if ((buf->decoder_flags & BUF_FLAG_HEADER) == 0) { /* reset decoder on leaving preview mode */ if ((buf->decoder_flags & BUF_FLAG_PREVIEW) == 0) { if (this->preview_mode) { mad_reset (this_gen); } } else { this->preview_mode = 1; } bytes_in_buffer_at_pts = this->bytes_in_buffer; xine_fast_memcpy (&this->buffer[this->bytes_in_buffer], buf->content, buf->size); this->bytes_in_buffer += buf->size; /* printf ("libmad: decode data - doing it\n"); */ mad_stream_buffer (&this->stream, this->buffer, this->bytes_in_buffer); if (this->bytes_in_buffer < MAD_MIN_SIZE && buf->pts == 0) return; if (!this->needs_more_data) { this->pts = buf->pts; if (buf->decoder_flags & BUF_FLAG_AUDIO_PADDING) { this->start_padding = buf->decoder_info[1]; this->end_padding = buf->decoder_info[2]; } else { this->start_padding = 0; this->end_padding = 0; } } while (1) { if (mad_frame_decode (&this->frame, &this->stream) != 0) { if (this->stream.next_frame) { int num_bytes = this->buffer + this->bytes_in_buffer - this->stream.next_frame; /* printf("libmad: MAD_ERROR_BUFLEN\n"); */ memmove(this->buffer, this->stream.next_frame, num_bytes); this->bytes_in_buffer = num_bytes; } switch (this->stream.error) { case MAD_ERROR_BUFLEN: /* libmad wants more data */ this->needs_more_data = 1; return; default: lprintf ("error 0x%04X, mad_stream_buffer %d bytes\n", this->stream.error, this->bytes_in_buffer); mad_stream_buffer (&this->stream, this->buffer, this->bytes_in_buffer); } } else { int mode = (this->frame.header.mode == MAD_MODE_SINGLE_CHANNEL) ? AO_CAP_MODE_MONO : AO_CAP_MODE_STEREO; if (!this->output_open || (this->output_sampling_rate != this->frame.header.samplerate) || (this->output_mode != mode)) { lprintf ("audio sample rate %d mode %08x\n", this->frame.header.samplerate, mode); /* the mpeg audio demuxer can set audio bitrate */ if (! _x_stream_info_get(this->xstream, XINE_STREAM_INFO_AUDIO_BITRATE)) { _x_stream_info_set(this->xstream, XINE_STREAM_INFO_AUDIO_BITRATE, this->frame.header.bitrate); } /* the mpeg audio demuxer can set this meta info */ if (! _x_meta_info_get(this->xstream, XINE_META_INFO_AUDIOCODEC)) { switch (this->frame.header.layer) { case MAD_LAYER_I: _x_meta_info_set_utf8(this->xstream, XINE_META_INFO_AUDIOCODEC, "MPEG audio layer 1 (lib: MAD)"); break; case MAD_LAYER_II: _x_meta_info_set_utf8(this->xstream, XINE_META_INFO_AUDIOCODEC, "MPEG audio layer 2 (lib: MAD)"); break; case MAD_LAYER_III: _x_meta_info_set_utf8(this->xstream, XINE_META_INFO_AUDIOCODEC, "MPEG audio layer 3 (lib: MAD)"); break; default: _x_meta_info_set_utf8(this->xstream, XINE_META_INFO_AUDIOCODEC, "MPEG audio (lib: MAD)"); } } if (this->output_open) { this->xstream->audio_out->close (this->xstream->audio_out, this->xstream); this->output_open = 0; } if (!this->output_open) { this->output_open = (this->xstream->audio_out->open) (this->xstream->audio_out, this->xstream, 16, this->frame.header.samplerate, mode) ; } if (!this->output_open) { return; } this->output_sampling_rate = this->frame.header.samplerate; this->output_mode = mode; } mad_synth_frame (&this->synth, &this->frame); if ( (buf->decoder_flags & BUF_FLAG_PREVIEW) == 0 ) { unsigned int nchannels, nsamples; mad_fixed_t const *left_ch, *right_ch; struct mad_pcm *pcm = &this->synth.pcm; audio_buffer_t *audio_buffer; uint16_t *output; int bitrate; int pts_offset; audio_buffer = this->xstream->audio_out->get_buffer (this->xstream->audio_out); output = audio_buffer->mem; nchannels = pcm->channels; nsamples = pcm->length; left_ch = pcm->samples[0]; right_ch = pcm->samples[1]; /* padding */ if (this->start_padding || this->end_padding) { /* check padding validity */ if (nsamples < (this->start_padding + this->end_padding)) { lprintf("invalid padding data"); this->start_padding = 0; this->end_padding = 0; } lprintf("nsamples=%d, start_padding=%d, end_padding=%d\n", nsamples, this->start_padding, this->end_padding); nsamples -= this->start_padding + this->end_padding; left_ch += this->start_padding; right_ch += this->start_padding; } audio_buffer->num_frames = nsamples; audio_buffer->vpts = this->pts; while (nsamples--) { /* output sample(s) in 16-bit signed little-endian PCM */ *output++ = scale(*left_ch++); if (nchannels == 2) *output++ = scale(*right_ch++); } audio_buffer->num_frames = pcm->length; /* pts computing */ if (this->frame.header.bitrate > 0) { bitrate = this->frame.header.bitrate; } else { bitrate = _x_stream_info_get(this->xstream, XINE_STREAM_INFO_AUDIO_BITRATE); lprintf("offset %d bps\n", bitrate); } audio_buffer->vpts = buf->pts; if (audio_buffer->vpts && (bitrate > 0)) { pts_offset = (bytes_in_buffer_at_pts * 8 * 90) / (bitrate / 1000); lprintf("pts: %"PRId64", offset: %d pts, %d bytes\n", buf->pts, pts_offset, bytes_in_buffer_at_pts); if (audio_buffer->vpts < pts_offset) pts_offset = audio_buffer->vpts; audio_buffer->vpts -= pts_offset; } this->xstream->audio_out->put_buffer (this->xstream->audio_out, audio_buffer, this->xstream); this->pts = buf->pts; buf->pts = 0; if (buf->decoder_flags & BUF_FLAG_AUDIO_PADDING) { this->start_padding = buf->decoder_info[1]; this->end_padding = buf->decoder_info[2]; buf->decoder_info[1] = 0; buf->decoder_info[2] = 0; } else { this->start_padding = 0; this->end_padding = 0; } } lprintf ("decode worked\n"); } } } }
/* Try to compute the length of the fifo in 1/1000 s * 2 methods : * if the bitrate is known * use the size of the fifo * else * use the the first and the last pts of the fifo */ static void nbc_compute_fifo_length(nbc_t *this, fifo_buffer_t *fifo, buf_element_t *buf, int action) { int fifo_free, fifo_fill, fifo_div; int64_t video_br, audio_br, diff; int has_video, has_audio; has_video = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_VIDEO); has_audio = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_AUDIO); video_br = _x_stream_info_get(this->stream, XINE_STREAM_INFO_VIDEO_BITRATE); audio_br = _x_stream_info_get(this->stream, XINE_STREAM_INFO_AUDIO_BITRATE); fifo_free = fifo->buffer_pool_num_free; fifo_fill = fifo->fifo_size; fifo_div = fifo_fill + fifo_free - 1; if (fifo_div == 0) fifo_div = 1; /* avoid a possible divide-by-zero */ if (fifo == this->video_fifo) { this->video_fifo_free = fifo_free; this->video_fifo_fill = (100 * fifo_fill) / fifo_div; this->video_fifo_size = fifo->fifo_data_size;
/* Put callback the fifo mutex is locked */ static void enigma_nbc_put_cb (fifo_buffer_t *fifo, buf_element_t *buf, void *this_gen) { nbc_t *this = (nbc_t*)this_gen; int64_t progress = 0; int64_t video_p = 0; int64_t audio_p = 0; int has_video, has_audio; lprintf("enter enigma_nbc_put_cb\n"); pthread_mutex_lock(&this->mutex); if ((buf->type & BUF_MAJOR_MASK) != BUF_CONTROL_BASE) { if (this->enabled) { if (this->dvbspeed) dvbspeed_put (this, fifo, buf); else { nbc_compute_fifo_length(this, fifo, buf, FIFO_PUT); if (this->buffering) { has_video = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_VIDEO); has_audio = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_AUDIO); /* restart playing if high_water_mark is reached by all fifos * do not restart if has_video and has_audio are false to avoid * a yoyo effect at the beginning of the stream when these values * are not yet known. * * be sure that the next buffer_pool_alloc() call will not deadlock, * we need at least 2 buffers (see buffer.c) */ //printf("this->video_last_pts %lld this->audio_last_pts %lld\n", this->video_last_pts, this->audio_last_pts); int64_t first_pts = this->video_first_pts>this->audio_first_pts?this->video_first_pts:this->audio_first_pts; int64_t last_pts = this->video_last_pts<this->audio_last_pts?this->video_last_pts:this->audio_last_pts; //printf("AAA first_pts %lld last_pts %lld\n", first_pts, last_pts); if ( has_video && has_audio && (last_pts-first_pts)>DEFAULT_PTS_START ) { this->progress = 100; //report_progress (this->stream, 100); this->buffering = 0; nbc_set_speed_normal(this); } else if ((((!has_video) || (this->video_fifo_length > this->high_water_mark)) && ((!has_audio) || (this->audio_fifo_length > this->high_water_mark)) && (has_video || has_audio))) { this->progress = 100; //report_progress (this->stream, 100); this->buffering = 0; xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: enigma_nbc_put_cb: stops buffering\n"); nbc_set_speed_normal(this); this->high_water_mark += this->high_water_mark / 2; } else { /* compute the buffering progress * 50%: video * 50%: audio */ video_p = ((this->video_fifo_length * 50) / this->high_water_mark); if (video_p > 50) video_p = 50; audio_p = ((this->audio_fifo_length * 50) / this->high_water_mark); if (audio_p > 50) audio_p = 50; if ((has_video) && (has_audio)) { progress = video_p + audio_p; } else if (has_video) { progress = 2 * video_p; } else { progress = 2 * audio_p; } /* if the progress can't be computed using the fifo length, use the number of buffers */ if (!progress) { video_p = this->video_fifo_fill; audio_p = this->audio_fifo_fill; progress = (video_p > audio_p) ? video_p : audio_p; } if (progress > this->progress) { //report_progress (this->stream, progress); this->progress = progress; } } } //if(this->stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) // display_stats(this); //report_stats(this, 0); } } } else { switch (buf->type) { case BUF_CONTROL_START: lprintf("BUF_CONTROL_START\n"); if (!this->enabled) { /* a new stream starts */ xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: enigma_nbc_put_cb: starts buffering\n"); this->enabled = 1; this->buffering = 1; this->video_first_pts = 0; this->video_last_pts = 0; this->audio_first_pts = 0; this->audio_last_pts = 0; this->video_fifo_length = 0; this->audio_fifo_length = 0; dvbspeed_init (this, 1); if (!this->dvbspeed) nbc_set_speed_pause(this); /* this->progress = 0; report_progress (this->stream, progress);*/ } break; case BUF_CONTROL_NOP: if (!(buf->decoder_flags & BUF_FLAG_END_USER) && !(buf->decoder_flags & BUF_FLAG_END_STREAM)) { break; } /* fall through */ case BUF_CONTROL_END: case BUF_CONTROL_QUIT: lprintf("BUF_CONTROL_END\n"); dvbspeed_close (this); if (this->enabled) { /* end of stream : * - disable the nbc * - unpause the engine if buffering */ this->enabled = 0; lprintf("DISABLE netbuf\n"); if (this->buffering) { this->buffering = 0; this->progress = 100; //report_progress (this->stream, this->progress); xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: enigma_nbc_put_cb: stops buffering\n"); nbc_set_speed_normal(this); } } break; case BUF_CONTROL_NEWPTS: /* discontinuity management */ if (fifo == this->video_fifo) { this->video_in_disc++; xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: enigma_nbc_put_cb video disc %d\n", this->video_in_disc); } else { this->audio_in_disc++; xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: enigma_nbc_put_cb audio disc %d\n", this->audio_in_disc); } break; } if (fifo == this->video_fifo) { this->video_fifo_free = fifo->buffer_pool_num_free; this->video_fifo_size = fifo->fifo_data_size; } else { this->audio_fifo_free = fifo->buffer_pool_num_free; this->audio_fifo_size = fifo->fifo_data_size; } } pthread_mutex_unlock(&this->mutex); lprintf("exit enigma_nbc_put_cb\n"); }
static void *audio_decoder_loop (void *stream_gen) { buf_element_t *buf = NULL; buf_element_t *first_header = NULL; buf_element_t *last_header = NULL; int replaying_headers = 0; xine_stream_t *stream = (xine_stream_t *) stream_gen; xine_ticket_t *running_ticket = stream->xine->port_ticket; int running = 1; int prof_audio_decode = -1; uint32_t buftype_unknown = 0; int audio_channel_user = stream->audio_channel_user; if (prof_audio_decode == -1) prof_audio_decode = xine_profiler_allocate_slot ("audio decoder/output"); while (running) { lprintf ("audio_loop: waiting for package...\n"); if( !replaying_headers ) buf = stream->audio_fifo->get (stream->audio_fifo); lprintf ("audio_loop: got package pts = %"PRId64", type = %08x\n", buf->pts, buf->type); _x_extra_info_merge( stream->audio_decoder_extra_info, buf->extra_info ); stream->audio_decoder_extra_info->seek_count = stream->video_seek_count; switch (buf->type) { case BUF_CONTROL_HEADERS_DONE: pthread_mutex_lock (&stream->counter_lock); stream->header_count_audio++; pthread_cond_broadcast (&stream->counter_changed); pthread_mutex_unlock (&stream->counter_lock); break; case BUF_CONTROL_START: lprintf ("start\n"); /* decoder dispose might call port functions */ running_ticket->acquire(running_ticket, 0); if (stream->audio_decoder_plugin) { lprintf ("close old decoder\n"); stream->keep_ao_driver_open = !!(buf->decoder_flags & BUF_FLAG_GAPLESS_SW); _x_free_audio_decoder (stream, stream->audio_decoder_plugin); stream->audio_decoder_plugin = NULL; stream->audio_track_map_entries = 0; stream->audio_type = 0; stream->keep_ao_driver_open = 0; } running_ticket->release(running_ticket, 0); if( !(buf->decoder_flags & BUF_FLAG_GAPLESS_SW) ) stream->metronom->handle_audio_discontinuity (stream->metronom, DISC_STREAMSTART, 0); buftype_unknown = 0; break; case BUF_CONTROL_END: /* free all held header buffers, see comments below */ if( first_header ) { buf_element_t *cur, *next; cur = first_header; while( cur ) { next = cur->next; cur->free_buffer (cur); cur = next; } first_header = last_header = NULL; } /* * wait the output fifos to run dry before sending the notification event * to the frontend. this test is only valid if there is only a single * stream attached to the current output port. */ while(1) { int num_bufs, num_streams; running_ticket->acquire(running_ticket, 0); num_bufs = stream->audio_out->get_property(stream->audio_out, AO_PROP_BUFS_IN_FIFO); num_streams = stream->audio_out->get_property(stream->audio_out, AO_PROP_NUM_STREAMS); running_ticket->release(running_ticket, 0); if( num_bufs > 0 && num_streams == 1 && !stream->early_finish_event) xine_usec_sleep (10000); else break; } /* wait for video to reach this marker, if necessary */ pthread_mutex_lock (&stream->counter_lock); stream->finished_count_audio++; lprintf ("reached end marker # %d\n", stream->finished_count_audio); pthread_cond_broadcast (&stream->counter_changed); if (stream->video_thread_created) { while (stream->finished_count_video < stream->finished_count_audio) { struct timeval tv; struct timespec ts; gettimeofday(&tv, NULL); ts.tv_sec = tv.tv_sec + 1; ts.tv_nsec = tv.tv_usec * 1000; /* use timedwait to workaround buggy pthread broadcast implementations */ pthread_cond_timedwait (&stream->counter_changed, &stream->counter_lock, &ts); } } pthread_mutex_unlock (&stream->counter_lock); stream->audio_channel_auto = -1; break; case BUF_CONTROL_QUIT: /* decoder dispose might call port functions */ running_ticket->acquire(running_ticket, 0); if (stream->audio_decoder_plugin) { _x_free_audio_decoder (stream, stream->audio_decoder_plugin); stream->audio_decoder_plugin = NULL; stream->audio_track_map_entries = 0; stream->audio_type = 0; } running_ticket->release(running_ticket, 0); running = 0; break; case BUF_CONTROL_NOP: break; case BUF_CONTROL_RESET_DECODER: lprintf ("reset\n"); _x_extra_info_reset( stream->audio_decoder_extra_info ); if (stream->audio_decoder_plugin) { running_ticket->acquire(running_ticket, 0); stream->audio_decoder_plugin->reset (stream->audio_decoder_plugin); running_ticket->release(running_ticket, 0); } break; case BUF_CONTROL_DISCONTINUITY: if (stream->audio_decoder_plugin) { running_ticket->acquire(running_ticket, 0); stream->audio_decoder_plugin->discontinuity (stream->audio_decoder_plugin); running_ticket->release(running_ticket, 0); } stream->metronom->handle_audio_discontinuity (stream->metronom, DISC_RELATIVE, buf->disc_off); break; case BUF_CONTROL_NEWPTS: if (stream->audio_decoder_plugin) { running_ticket->acquire(running_ticket, 0); stream->audio_decoder_plugin->discontinuity (stream->audio_decoder_plugin); running_ticket->release(running_ticket, 0); } if (buf->decoder_flags & BUF_FLAG_SEEK) { stream->metronom->handle_audio_discontinuity (stream->metronom, DISC_STREAMSEEK, buf->disc_off); } else { stream->metronom->handle_audio_discontinuity (stream->metronom, DISC_ABSOLUTE, buf->disc_off); } break; case BUF_CONTROL_AUDIO_CHANNEL: { xprintf(stream->xine, XINE_VERBOSITY_DEBUG, "audio_decoder: suggested switching to stream_id %02x\n", buf->decoder_info[0]); stream->audio_channel_auto = buf->decoder_info[0] & 0xff; } break; case BUF_CONTROL_RESET_TRACK_MAP: if (stream->audio_track_map_entries) { xine_event_t ui_event; stream->audio_track_map_entries = 0; ui_event.type = XINE_EVENT_UI_CHANNELS_CHANGED; ui_event.data_length = 0; xine_event_send(stream, &ui_event); } break; default: if (_x_stream_info_get(stream, XINE_STREAM_INFO_IGNORE_AUDIO)) break; xine_profiler_start_count (prof_audio_decode); running_ticket->acquire(running_ticket, 0); if ( (buf->type & 0xFF000000) == BUF_AUDIO_BASE ) { uint32_t audio_type = 0; int i,j; uint32_t chan=buf->type&0x0000FFFF; /* printf("audio_decoder: buf_type=%08x auto=%08x user=%08x\n", buf->type, stream->audio_channel_auto, audio_channel_user); */ /* update track map */ i = 0; while ( (i<stream->audio_track_map_entries) && ((stream->audio_track_map[i]&0x0000FFFF)<chan) ) i++; if ( (i==stream->audio_track_map_entries) || ((stream->audio_track_map[i]&0x0000FFFF)!=chan) ) { xine_event_t ui_event; j = stream->audio_track_map_entries; if (j >= 50) break; while (j>i) { stream->audio_track_map[j] = stream->audio_track_map[j-1]; j--; } stream->audio_track_map[i] = buf->type; stream->audio_track_map_entries++; /* implicit channel change - reopen decoder below */ if ((i == 0) && (audio_channel_user == -1) && (stream->audio_channel_auto < 0)) stream->audio_decoder_streamtype = -1; ui_event.type = XINE_EVENT_UI_CHANNELS_CHANGED; ui_event.data_length = 0; xine_event_send (stream, &ui_event); } /* find out which audio type to decode */ lprintf ("audio_channel_user = %d, map[0]=%08x\n", audio_channel_user, stream->audio_track_map[0]); if (audio_channel_user > -2) { if (audio_channel_user == -1) { /* auto */ lprintf ("audio_channel_auto = %d\n", stream->audio_channel_auto); if (stream->audio_channel_auto>=0) { if ((buf->type & 0xFF) == stream->audio_channel_auto) { audio_type = buf->type; } else audio_type = -1; } else audio_type = stream->audio_track_map[0]; } else { if (audio_channel_user <= stream->audio_track_map_entries) audio_type = stream->audio_track_map[audio_channel_user]; else audio_type = -1; } /* now, decode stream buffer if it's the right audio type */ if (buf->type == audio_type) { int streamtype = (buf->type>>16) & 0xFF; /* close old decoder of audio type has changed */ if( buf->type != buftype_unknown && (stream->audio_decoder_streamtype != streamtype || !stream->audio_decoder_plugin) ) { if (stream->audio_decoder_plugin) { _x_free_audio_decoder (stream, stream->audio_decoder_plugin); } stream->audio_decoder_streamtype = streamtype; stream->audio_decoder_plugin = _x_get_audio_decoder (stream, streamtype); _x_stream_info_set(stream, XINE_STREAM_INFO_AUDIO_HANDLED, (stream->audio_decoder_plugin != NULL)); } if (audio_type != stream->audio_type) { if (stream->audio_decoder_plugin) { xine_event_t event; stream->audio_type = audio_type; event.type = XINE_EVENT_UI_CHANNELS_CHANGED; event.data_length = 0; xine_event_send(stream, &event); } } /* finally - decode data */ if (stream->audio_decoder_plugin) stream->audio_decoder_plugin->decode_data (stream->audio_decoder_plugin, buf); if (buf->type != buftype_unknown && !_x_stream_info_get(stream, XINE_STREAM_INFO_AUDIO_HANDLED)) { xine_log (stream->xine, XINE_LOG_MSG, _("audio_decoder: no plugin available to handle '%s'\n"), _x_buf_audio_name( buf->type ) ); if( !_x_meta_info_get(stream, XINE_META_INFO_AUDIOCODEC) ) _x_meta_info_set_utf8(stream, XINE_META_INFO_AUDIOCODEC, _x_buf_audio_name( buf->type )); buftype_unknown = buf->type; /* fatal error - dispose plugin */ if (stream->audio_decoder_plugin) { _x_free_audio_decoder (stream, stream->audio_decoder_plugin); stream->audio_decoder_plugin = NULL; } } } } } else if( buf->type != buftype_unknown ) {