📄 ffmpeg_audio.cpp
字号:
AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::start(): calling m_src->start(0x%x, 0x%x)", m_event_processor, e); m_src->start(evp, e); } else { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::start(): not restarting, eof=%d, buffer_full=%d", (int)_end_of_file(), (int)m_buffer.buffer_full()); } m_lock.leave();} void ffmpeg_decoder_datasource::readdone(int len){ m_lock.enter(); m_buffer.readdone(len); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource.readdone : done with %d bytes", len);/* if(( !(buffer_full()) && !m_src->end_of_file() )) { * lib::event *e = new readdone_callback(this, &ffmpeg_decoder_datasource::data_avail); * m_src->start(m_event_processor, e); * } */ m_lock.leave();}void ffmpeg_decoder_datasource::data_avail(){ m_lock.enter(); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource.data_avail: called : m_src->get_read_ptr() m_src=0x%x, this=0x%x", (void*) m_src, (void*) this); int sz; if (m_con) { if (m_src == NULL) { lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::data_avail(): No datasource !"); lib::logger::get_logger()->warn(gettext("Programmer error encountered during audio playback")); return; } if (!m_buffer.buffer_full()) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource.data_avail: m_src->get_read_ptr() m_src=0x%x, this=0x%x", (void*) m_src, (void*) this); ts_packet_t audio_packet = m_src->get_ts_packet_t(); uint8_t *inbuf = (uint8_t*) audio_packet.data; sz = audio_packet.size; AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource.data_avail: %d bytes available", sz); // Note: outsize is only written by avcodec_decode_audio, not read! // You must always supply a buffer that is AVCODEC_MAX_AUDIO_FRAME_SIZE // bytes big! int outsize = AVCODEC_MAX_AUDIO_FRAME_SIZE; uint8_t *outbuf = (uint8_t*) m_buffer.get_write_ptr(outsize); if (outbuf) { if(inbuf) { // Don't feed to much data to the decoder, it doesn't like to do lists ;-) int cursz = sz; if (cursz > AVCODEC_MAX_AUDIO_FRAME_SIZE/2) cursz = AVCODEC_MAX_AUDIO_FRAME_SIZE/2; AM_DBG lib::logger::get_logger()->debug("avcodec_decode_audio(0x%x, 0x%x, 0x%x(%d), 0x%x, %d)", (void*)m_con, (void*)outbuf, (void*)&outsize, outsize, (void*)inbuf, sz); int decoded = avcodec_decode_audio(m_con, (short*) outbuf, &outsize, inbuf, cursz); free(inbuf); _need_fmt_uptodate(); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource.data_avail : %d bps, %d channels",m_fmt.samplerate, m_fmt.channels); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource.data_avail : %d bytes decoded to %d bytes", decoded,outsize ); assert(m_fmt.samplerate); double duration = ((double) outsize)* sizeof(uint8_t)*8 / (m_fmt.samplerate* m_fmt.channels * m_fmt.bits); timestamp_t old_elapsed = m_elapsed; m_elapsed += (timestamp_t) (duration*1000000); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource.data_avail elapsed = %d ", m_elapsed); // We need to do some tricks to handle clip_begin falling within this buffer. // First we push all the data we have into the buffer, then we check whether the beginning // should have been skipped and, if so, read out the bytes. if (m_elapsed > m_src->get_clip_begin()) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource.data_avail We passed clip_begin : (outsize = %d) ", outsize); if (outsize > 0) { m_buffer.pushdata(outsize); } else { m_buffer.pushdata(0); } if (old_elapsed < m_src->get_clip_begin()) { timestamp_t delta_t_unwanted = m_src->get_clip_begin() - old_elapsed; assert(delta_t_unwanted > 0); int bytes_unwanted = (delta_t_unwanted * ((m_fmt.samplerate* m_fmt.channels * m_fmt.bits)/(sizeof(uint8_t)*8)))/1000000; bytes_unwanted &= ~3; AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource: clip_begin within buffer, dropping %lld us, %d bytes", delta_t_unwanted, bytes_unwanted); (void)m_buffer.get_read_ptr(); assert(m_buffer.size() > bytes_unwanted); m_buffer.readdone(bytes_unwanted); } } else { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource.data_avail: m_elapsed = %lld < clip_begin = %lld, skipped %d bytes", m_elapsed, m_src->get_clip_begin(), outsize); m_buffer.pushdata(0); } AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource.data_avail : m_src->readdone(%d) called m_src=0x%x, this=0x%x", decoded,(void*) m_src, (void*) this ); } else { m_buffer.pushdata(0); } } else { lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::data_avail: no room in output buffer"); lib::logger::get_logger()->warn(gettext("Programmer error encountered during audio playback")); m_buffer.pushdata(0); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::data_avail m_src->readdone(0) called this=0x%x"); } // sz = m_src->size(); } // Restart reading if we still have room to accomodate more data // XXX The note regarding m_elapsed holds here as well. if (!m_src->end_of_file() && m_event_processor && !m_buffer.buffer_full() && !_clip_end() ) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::data_avail(): calling m_src->start() again"); lib::event *e = new readdone_callback(this, &ffmpeg_decoder_datasource::data_avail); m_src->start(m_event_processor, e); } else { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::data_avail: not calling start: eof=%d m_ep=0x%x buffull=%d", (int)m_src->end_of_file(), (void*)m_event_processor, (int)m_buffer.buffer_full()); } if ( m_client_callback && (m_buffer.buffer_not_empty() || _end_of_file() || _clip_end() ) ) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::data_avail(): calling client callback (%d, %d)", m_buffer.size(), _end_of_file()); assert(m_event_processor); if (m_elapsed >= m_src->get_clip_begin()) { m_event_processor->add_event(m_client_callback, 0, ambulant::lib::ep_med); m_client_callback = NULL; } //m_event_processor = NULL; } else { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::data_avail(): No client callback!"); } } else { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::data_avail(): No decoder, flushing available data"); } m_lock.leave();}bool ffmpeg_decoder_datasource::end_of_file(){ m_lock.enter(); if (_clip_end()) { m_lock.leave(); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::end_of_file(): clip_end reached"); return true; } bool rv = _end_of_file(); m_lock.leave(); return rv;}bool ffmpeg_decoder_datasource::_end_of_file(){ // private method - no need to lock if (m_buffer.buffer_not_empty()) return false; return m_src->end_of_file();}bool ffmpeg_decoder_datasource::_clip_end() const{ // private method - no need to lock timestamp_t clip_end = m_src->get_clip_end(); if (clip_end == -1) return false; AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::_clip_end(): m_elapsed=%lld , clip_end=%lld", m_elapsed, clip_end); if (m_elapsed > clip_end) { return true; } return false;}void ffmpeg_decoder_datasource::read_ahead(timestamp_t clip_begin){ m_src->read_ahead(clip_begin);} bool ffmpeg_decoder_datasource::buffer_full(){ m_lock.enter(); bool rv = m_buffer.buffer_full(); m_lock.leave(); return rv;} char* ffmpeg_decoder_datasource::get_read_ptr(){ m_lock.enter(); char *rv = m_buffer.get_read_ptr(); m_lock.leave(); return rv;}int ffmpeg_decoder_datasource::size() const{ const_cast <ffmpeg_decoder_datasource*>(this)->m_lock.enter(); int rv = m_buffer.size(); if (_clip_end()) { // clip end falls within the current buffer (or maybe even before it) timestamp_t clip_end = m_src->get_clip_end(); timestamp_t delta_t_unwanted = m_elapsed - clip_end; assert(delta_t_unwanted >= 0); // ((double) outsize)* sizeof(uint8_t)*8 / (m_fmt.samplerate* m_fmt.channels * m_fmt.bits); int bytes_unwanted = (delta_t_unwanted * ((m_fmt.samplerate* m_fmt.channels * m_fmt.bits)/(sizeof(uint8_t)*8)))/1000000; assert(bytes_unwanted >= 0); rv -= bytes_unwanted; rv &= ~3; if (rv < 0) rv = 0; } const_cast <ffmpeg_decoder_datasource*>(this)->m_lock.leave(); return rv;} timestamp_tffmpeg_decoder_datasource::get_clip_end(){ m_lock.enter(); timestamp_t clip_end; clip_end = m_src->get_clip_end(); m_lock.leave(); return clip_end;}timestamp_tffmpeg_decoder_datasource::get_clip_begin(){ m_lock.enter(); timestamp_t clip_begin; clip_begin = m_src->get_clip_begin(); m_lock.leave(); return clip_begin;}bool ffmpeg_decoder_datasource::_select_decoder(const char* file_ext){ // private method - no need to lock AVCodec *codec = avcodec_find_decoder_by_name(file_ext); if (codec == NULL) { lib::logger::get_logger()->trace("ffmpeg_decoder_datasource._select_decoder: Failed to find codec for \"%s\"", file_ext); lib::logger::get_logger()->error(gettext("No support for \"%s\" audio"), file_ext); return false; } m_con = avcodec_alloc_context(); if(avcodec_open(m_con,codec) < 0) { lib::logger::get_logger()->trace("ffmpeg_decoder_datasource._select_decoder: Failed to open avcodec for \"%s\"", file_ext); lib::logger::get_logger()->error(gettext("No support for \"%s\" audio"), file_ext); return false; } return true;}bool ffmpeg_decoder_datasource::_select_decoder(audio_format &fmt){ // private method - no need to lock if (fmt.name == "ffmpeg") { AVCodecContext *enc = (AVCodecContext *)fmt.parameters; if (enc == NULL) { lib::logger::get_logger()->debug("Internal error: ffmpeg_decoder_datasource._select_decoder: Parameters missing for %s(0x%x)", fmt.name.c_str(), fmt.parameters); return false; } if (enc->codec_type != CODEC_TYPE_AUDIO) { lib::logger::get_logger()->debug("Internal error: ffmpeg_decoder_datasource._select_decoder: Non-audio stream for %s(0x%x)", fmt.name.c_str(), enc->codec_type); return false; } AVCodec *codec = avcodec_find_decoder(enc->codec_id); if (codec == NULL) { lib::logger::get_logger()->debug("Internal error: ffmpeg_decoder_datasource._select_decoder: Failed to find codec for %s(0x%x)", fmt.name.c_str(), enc->codec_id); return false; } m_con = avcodec_alloc_context(); m_con->channels = 0; if(avcodec_open(m_con,codec) < 0) { lib::logger::get_logger()->debug("Internal error: ffmpeg_decoder_datasource._select_decoder: Failed to open avcodec for %s(0x%x)", fmt.name.c_str(), enc->codec_id); av_free(m_con); m_con = NULL; return false; } AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::_select_decoder: codec_name=%s, codec_id=%d", m_con->codec_name, m_con->codec_id); m_fmt = audio_format(enc->sample_rate, enc->channels, 16); return true; } else if (fmt.name == "live") { const char* codec_name = (char*) fmt.parameters; AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::selectdecoder(): audio codec : %s", codec_name); ffmpeg_codec_id* codecid = ffmpeg_codec_id::instance(); AVCodec *codec = avcodec_find_decoder(codecid->get_codec_id(codec_name)); if( !codec) { //lib::logger::get_logger()->error(gettext("%s: Audio codec %d(%s) not supported"), repr(url).c_str(), m_con->codec_id, m_con->codec_name); return false; } else { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::selectdecoder(): codec found!"); } m_con = avcodec_alloc_context(); m_con->channels = 0; if((avcodec_open(m_con,codec) < 0) ) { //lib::logger::get_logger()->error(gettext("%s: Cannot open audio codec %d(%s)"), repr(url).c_str(), m_con->codec_id, m_con->codec_name); av_free(m_con); m_con = NULL; return false; } else { AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::ffmpeg_decoder_datasource(): succesfully opened codec"); } m_con->codec_type = CODEC_TYPE_AUDIO; return true; } // Could add support here for raw mp3, etc. return false;}audio_format&ffmpeg_decoder_datasource::get_audio_format(){ m_lock.enter(); _need_fmt_uptodate(); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::get_audio_format: rate=%d, bits=%d,channels=%d",m_fmt.samplerate, m_fmt.bits, m_fmt.channels); m_lock.leave(); return m_fmt;}voidffmpeg_decoder_datasource::_need_fmt_uptodate(){ // Private method - no locking if (m_fmt.samplerate == 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -