📄 ffmpeg_audio.cpp
字号:
// This file is part of Ambulant Player, www.ambulantplayer.org.//// Copyright (C) 2003-2007 Stichting CWI, // Kruislaan 413, 1098 SJ Amsterdam, The Netherlands.//// Ambulant Player is free software; you can redistribute it and/or modify// it under the terms of the GNU Lesser General Public License as published by// the Free Software Foundation; either version 2.1 of the License, or// (at your option) any later version.//// Ambulant Player is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU Lesser General Public License for more details.//// You should have received a copy of the GNU Lesser General Public License// along with Ambulant Player; if not, write to the Free Software// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA //#include <math.h>//#include <map>#include "ambulant/config/config.h"#include "ambulant/net/ffmpeg_common.h"#include "ambulant/net/ffmpeg_audio.h" #include "ambulant/net/ffmpeg_factory.h" #include "ambulant/net/demux_datasource.h" // WARNING: turning on AM_DBG globally for the ffmpeg code seems to trigger// a condition that makes the whole player hang or collapse. So you probably// shouldn't do it:-)//#define AM_DBG#ifndef AM_DBG#define AM_DBG if(0)#endif using namespace ambulant;using namespace net;typedef lib::no_arg_callback<ffmpeg_decoder_datasource> readdone_callback;typedef lib::no_arg_callback<ffmpeg_resample_datasource> resample_callback;#define INBUF_SIZE 4096// Factory functionsaudio_datasource_factory *ambulant::net::get_ffmpeg_audio_datasource_factory(){#if 0 static audio_datasource_factory *s_factory; if (!s_factory) s_factory = new ffmpeg_audio_datasource_factory(); return s_factory;#else return new ffmpeg_audio_datasource_factory();#endif}audio_decoder_finder *ambulant::net::get_ffmpeg_audio_decoder_finder(){#if 0 static audio_parser_finder *s_factory; if (!s_factory) s_factory = new ffmpeg_audio_decoder_finder(); return s_factory;#else return new ffmpeg_audio_decoder_finder();#endif}audio_filter_finder *ambulant::net::get_ffmpeg_audio_filter_finder(){#if 0 static audio_filter_finder *s_factory; if (!s_factory) s_factory = new ffmpeg_audio_filter_finder(); return s_factory;#else return new ffmpeg_audio_filter_finder();#endif}audio_datasource* ffmpeg_audio_datasource_factory::new_audio_datasource(const net::url& url, const audio_format_choices& fmts, timestamp_t clip_begin, timestamp_t clip_end){ AM_DBG lib::logger::get_logger()->debug("ffmpeg_audio_datasource_factory::new_audio_datasource(%s)", repr(url).c_str()); // First we check that the file format is supported by the file reader. // If it is we create a file reader (demux head end). AVFormatContext *context = ffmpeg_demux::supported(url); if (!context) { lib::logger::get_logger()->trace("ffmpeg: no support for %s", repr(url).c_str()); return NULL; } ffmpeg_demux *thread = new ffmpeg_demux(context, clip_begin, clip_end); // Now, we can check that there is actually audio in the file. if (thread->audio_stream_nr() < 0) { thread->cancel(); lib::logger::get_logger()->trace("ffmpeg: No audio stream in %s", repr(url).c_str()); return NULL; } // Next, if there is audio we check that it is either in a format our caller wants, // or we can decode this type of audio stream. // // XXX This code is incomplete. There could be more audio streams in the file, // and we could have trouble decoding the first one but not others.... Oh well... audio_format fmt = thread->get_audio_format(); if (!fmts.contains(fmt) && !ffmpeg_decoder_datasource::supported(fmt)) { thread->cancel(); lib::logger::get_logger()->trace("ffmpeg: Unsupported audio stream in %s", repr(url).c_str()); return NULL; } // All seems well. Create the demux reader, the decoder and optionally the resampler. pkt_audio_datasource *pds = demux_audio_datasource::new_demux_audio_datasource(url, thread); if (pds == NULL) { AM_DBG lib::logger::get_logger()->debug("fdemux_audio_datasource_factory::new_audio_datasource: could not allocate ffmpeg_video_datasource"); thread->cancel(); return NULL; } pds->read_ahead(clip_begin); AM_DBG lib::logger::get_logger()->debug("ffmpeg_audio_datasource_factory::new_audio_datasource: parser ds = 0x%x", (void*)pds); // XXXX This code should become generalized in datasource_factory // XXXX It is also unclear whether this code will work for, say, wav or aiff streams. audio_datasource *dds = new ffmpeg_decoder_datasource(pds); AM_DBG lib::logger::get_logger()->debug("ffmpeg_audio_datasource_factory::new_audio_datasource: decoder ds = 0x%x", (void*)dds); if (dds == NULL) { int rem = pds->release(); assert(rem == 0); return NULL; } if (fmts.contains(dds->get_audio_format())) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_audio_datasource_factory::new_audio_datasource: matches!"); return dds; } audio_datasource *rds = new ffmpeg_resample_datasource(dds, fmts); AM_DBG lib::logger::get_logger()->debug("ffmpeg_audio_datasource_factory::new_audio_datasource: resample ds = 0x%x", (void*)rds); if (rds == NULL) { int rem = dds->release(); assert(rem == 0); return NULL; } if (fmts.contains(rds->get_audio_format())) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_audio_datasource_factory::new_audio_datasource: matches!"); return rds; } lib::logger::get_logger()->error(gettext("%s: unable to create audio resampler")); int rem = rds->release(); assert(rem == 0); return NULL; }audio_datasource* ffmpeg_audio_decoder_finder::new_audio_decoder(pkt_audio_datasource *src, const audio_format_choices& fmts){ if (src == NULL) return NULL; audio_datasource *ds = NULL; if (!ffmpeg_decoder_datasource::supported(src->get_audio_format())) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_audio_parser_finder::new_audio_parser: no support for format"); return NULL; } ds = new ffmpeg_decoder_datasource(src); if (ds == NULL) { return NULL; } return ds; }audio_datasource*ffmpeg_audio_filter_finder::new_audio_filter(audio_datasource *src, const audio_format_choices& fmts){ audio_format& fmt = src->get_audio_format(); // First check that we understand the source format if (fmt.bits != 16) { lib::logger::get_logger()->warn(gettext("No support for %d-bit audio, only 16"), fmt.bits); return NULL; } if (fmts.contains(fmt)) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_audio_filter_finder::new_audio_datasource: matches!"); return src; } // XXXX Check that there is at least one destination format we understand too return new ffmpeg_resample_datasource(src, fmts);}// **************************** ffpmeg_decoder_datasource *****************************boolffmpeg_decoder_datasource::supported(const audio_format& fmt){ if (fmt.name == "ffmpeg"){ AVCodecContext *enc = (AVCodecContext *)fmt.parameters; if (enc->codec_type != CODEC_TYPE_AUDIO) return false; if (avcodec_find_decoder(enc->codec_id) == NULL) return false; return true; } if (fmt.name == "live"){ //AVCodecContext *enc = (AVCodecContext *)fmt.parameters; const char* codec_name = (char*) fmt.parameters; ffmpeg_codec_id* codecid = ffmpeg_codec_id::instance(); AVCodec *codec = avcodec_find_decoder(codecid->get_codec_id(codec_name)); if( !codec) { return false; } return true; } return false;}// Hack, hack. Get extension of a URL.static const char *getext(const net::url &url){ const char *curl = url.get_path().c_str(); const char *dotpos = rindex(curl, '.'); if (dotpos) return dotpos+1; return NULL;}boolffmpeg_decoder_datasource::supported(const net::url& url){ const char *ext = getext(url); if (ext == NULL || avcodec_find_decoder_by_name(ext) == NULL) return false; return true;}ffmpeg_decoder_datasource::ffmpeg_decoder_datasource(const net::url& url, pkt_audio_datasource *const src): m_con(NULL), m_fmt(audio_format(0,0,0)), m_event_processor(NULL), m_src(src), m_elapsed(m_src->get_start_time()), m_is_audio_ds(false), m_client_callback(NULL){ AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::ffmpeg_decoder_datasource() -> 0x%x m_buffer=0x%x", (void*)this, (void*)&m_buffer); ffmpeg_init(); const char *ext = getext(url); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource: Selecting \"%s\" decoder", ext); if (!_select_decoder(ext)) lib::logger::get_logger()->error(gettext("%s: audio decoder \"%s\" not supported"), url.get_url().c_str(), ext);}ffmpeg_decoder_datasource::ffmpeg_decoder_datasource(pkt_audio_datasource *const src): m_con(NULL), m_fmt(src->get_audio_format()), m_event_processor(NULL), m_src(src), m_elapsed(m_src->get_start_time()), m_is_audio_ds(true), m_client_callback(NULL){ AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::ffmpeg_decoder_datasource() -> 0x%x m_buffer=0x%x", (void*)this, (void*)&m_buffer); ffmpeg_init(); audio_format fmt = src->get_audio_format(); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource: Looking for %s(0x%x) decoder", fmt.name.c_str(), fmt.parameters); if (!_select_decoder(fmt)) { lib::logger::get_logger()->error(gettext("ffmpeg_decoder_datasource: could not select %s(0x%x) decoder"), fmt.name.c_str(), fmt.parameters); }}ffmpeg_decoder_datasource::~ffmpeg_decoder_datasource(){ stop();}voidffmpeg_decoder_datasource::stop(){ m_lock.enter(); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::stop(0x%x)", (void*)this); if (m_con) { avcodec_close(m_con); av_free(m_con); } m_con = NULL; if (m_src) { m_src->stop(); int rem = m_src->release(); if (rem) lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::stop(0x%x): m_src refcount=%d", (void*)this, rem); } m_src = NULL; if (m_client_callback) delete m_client_callback; m_client_callback = NULL; m_lock.leave();} void ffmpeg_decoder_datasource::start(ambulant::lib::event_processor *evp, ambulant::lib::event *callbackk){ m_lock.enter(); bool restart_input = false; if (m_client_callback != NULL) { delete m_client_callback; m_client_callback = NULL; AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::start(): m_client_callback already set!"); } if (m_buffer.buffer_not_empty() || _end_of_file() ) { // We have data (or EOF) available. Don't bother starting up our source again, in stead // immedeately signal our client again if (callbackk) { assert(evp); AM_DBG lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::start: trigger client callback"); evp->add_event(callbackk, 0, ambulant::lib::ep_med); } else { lib::logger::get_logger()->debug("ffmpeg_decoder_datasource::start(): no client callback!"); lib::logger::get_logger()->warn(gettext("Programmer error encountered during audio playback")); } } else { // We have no data available. Start our source, and in our data available callback we // will signal the client. restart_input = true; m_client_callback = callbackk; m_event_processor = evp; } // Also restart our source if we still have room and there is // data to read. if ( !_end_of_file() && !m_buffer.buffer_full() ) restart_input = true; if (restart_input) { lib::event *e = new readdone_callback(this, &ffmpeg_decoder_datasource::data_avail);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -