⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ffmpeg_common.cpp

📁 彩信浏览器
💻 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/net/ffmpeg_common.h" #include "ambulant/net/datasource.h"#include "ambulant/lib/logger.h"#include "ambulant/net/url.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 // This construction is needed to get the CVS version of ffmpeg to work:// AVStream.codec got changed from AVCodecContext to AVCodecContext*#if LIBAVFORMAT_BUILD > 4628	#define am_get_codec_var(codec,var) codec->var	#define am_get_codec(codec) codec#else	#define am_get_codec_var(codec,var) codec.var	#define am_get_codec(codec) &codec#endifusing namespace ambulant;using namespace net;void ambulant::net::ffmpeg_init(){	static bool is_inited = false;	if (is_inited) return;	avcodec_init();	av_register_all();	is_inited = true;}// Static initializer function shared among ffmpeg classesffmpeg_codec_id* ambulant::net::ffmpeg_codec_id::m_uniqueinstance = NULL;ffmpeg_codec_id*ffmpeg_codec_id::instance(){	if (m_uniqueinstance == NULL) {        m_uniqueinstance = new ffmpeg_codec_id();		AM_DBG lib::logger::get_logger()->debug("ffmpeg_codec_id::instance() returning 0x%x", (void*) m_uniqueinstance);	}    return m_uniqueinstance;}voidffmpeg_codec_id::add_codec(const char* codec_name, CodecID id){  std::string str(codec_name);  m_codec_id.insert(std::pair<std::string, CodecID>(str, id));}CodecIDffmpeg_codec_id::get_codec_id(const char* codec_name) {	std::string str(codec_name);	std::map<std::string, CodecID>::iterator i;	i = m_codec_id.find(str);	if (i != m_codec_id.end()) {		AM_DBG lib::logger::get_logger()->debug("get_codec_id(%s) -> %d", str.c_str(), i->second);		return i->second;	}		return CODEC_ID_NONE;}ffmpeg_codec_id::ffmpeg_codec_id(){		// XXX Lots of formats missing here obviously	// Live.com formats	add_codec("MPA", CODEC_ID_MP3);	add_codec("MPA-ROBUST", CODEC_ID_MP3);	add_codec("X_MP3-DRAFT-00", CODEC_ID_MP3);	add_codec("X-MP3-DRAFT-00", CODEC_ID_MP3);	add_codec("MPV", CODEC_ID_MPEG2VIDEO);	add_codec("L16", CODEC_ID_PCM_S16LE);	add_codec("MP4V-ES", CODEC_ID_MPEG4);	add_codec("MPEG4-GENERIC", CODEC_ID_MPEG4AAC);	add_codec("X-QT", CODEC_ID_MP3); //XXXX}// **************************** ffmpeg_demux *****************************ffmpeg_demux::ffmpeg_demux(AVFormatContext *con, timestamp_t clip_begin, timestamp_t clip_end):   m_con(con),	m_nstream(0),	m_clip_begin(clip_begin),	m_clip_end(clip_end),	m_clip_begin_set(false){#if LIBAVFORMAT_BUILD > 4609		assert(m_clip_begin >= 0);	if ( m_clip_begin > 0 #if 1	// Bug in ffmpeg 49.2.0: seeking in mov/mp4 files fails. Reported 5-Dec-05.	&& !strstr(con->iformat->name, "mp4")#endif		) {		assert (m_con);		assert (m_con->iformat);#if LIBAVFORMAT_BUILD > 4628		int seekresult = av_seek_frame(m_con, -1, m_clip_begin, 0);#else		int seekresult = av_seek_frame(m_con, -1, m_clip_begin);#endif		if (seekresult < 0) {			lib::logger::get_logger()->debug("ffmpeg_demux: av_seek_frame() returned %d", seekresult);//#if LIBAVFORMAT_BUILD > 4628//			// ffmpeg has discarded data if av_seek_frame() failed//			av_seek_frame(m_con, -1, 0, AVSEEK_FLAG_BYTE);//#endif		}	} #endif		m_audio_fmt = audio_format("ffmpeg");	m_audio_fmt.bits = 16;	int audio_idx = audio_stream_nr();	if ( audio_idx >= 0) {		m_audio_fmt.parameters = (void *) am_get_codec(con->streams[audio_idx]->codec);		AM_DBG lib::logger::get_logger()->debug("ffmpeg_demux::supported: audio_codec_name=%s", am_get_codec_var(m_con->streams[audio_idx]->codec, codec_name));		m_audio_fmt.samplerate = am_get_codec_var(con->streams[audio_idx]->codec, sample_rate);		m_audio_fmt.channels = am_get_codec_var(con->streams[audio_idx]->codec, channels);		m_audio_fmt.bits = 16;		AM_DBG lib::logger::get_logger()->debug("ffmpeg_demux::ffmpeg_demux(): samplerate=%d, channels=%d, m_con->codec (0x%x)", m_audio_fmt.samplerate, m_audio_fmt.channels, m_audio_fmt.parameters  );	} 	m_video_fmt = video_format("ffmpeg");	int video_idx = video_stream_nr();	if (video_idx >= 0) {		m_video_fmt.parameters = (void *) am_get_codec(m_con->streams[video_stream_nr()]->codec);		AM_DBG lib::logger::get_logger()->debug("ffmpeg_demux::supported: video_codec_name=%s", am_get_codec_var(m_con->streams[video_stream_nr()]->codec, codec_name));	} else {		AM_DBG lib::logger::get_logger()->debug("ffmpeg_demux::supported: No Video stream ?");		m_video_fmt.parameters = NULL;	}	memset(m_sinks, 0, sizeof m_sinks);}ffmpeg_demux::~ffmpeg_demux(){	AM_DBG lib::logger::get_logger()->debug("ffmpeg_demux::~ffmpeg_demux()");	if (m_con) av_close_input_file(m_con);	m_con = NULL;}timestamp_tffmpeg_demux::get_clip_end(){		AM_DBG lib::logger::get_logger()->debug("ffmpeg_demux::get_clip_end(): %lld", m_clip_end);	return m_clip_end;}timestamp_tffmpeg_demux::get_clip_begin(){		AM_DBG lib::logger::get_logger()->debug("ffmpeg_demux::get_clip_begin(): %lld", m_clip_begin);	return m_clip_begin;}AVFormatContext *ffmpeg_demux::supported(const net::url& url){	ffmpeg_init();	// Setup struct to allow ffmpeg to determine whether it supports this	AVInputFormat *fmt;	AVProbeData probe_data;	std::string url_str(url.get_url());	std::string ffmpeg_name = url_str;	if (url.is_local_file())		ffmpeg_name = url.get_file();	#if 0	// There appears to be some support for RTSP in ffmpeg, but it doesn'	// seem to work yet. Disable it so we don't get confused by error messages.	if (url_str.substr(0, 5) == "rtsp:") return NULL;#endif	probe_data.filename = ffmpeg_name.c_str();	probe_data.buf = NULL;	probe_data.buf_size = 0;	fmt = av_probe_input_format(&probe_data, 0);	if (!fmt && url.is_local_file()) {		// get the file extension		std::string short_name = ffmpeg_name.substr(ffmpeg_name.length()-3,3);		if (short_name == "mov" || short_name == "mp4")			// ffmpeg's id for QuickTime/MPEG4/Motion JPEG 2000 format			short_name = "mov,mp4,m4a,3gp,3g2,mj2";		fmt = av_find_input_format(short_name.c_str());			}	AM_DBG lib::logger::get_logger()->debug("ffmpeg_demux::supported(%s): (%s) av_probe_input_format: 0x%x", url_str.c_str(), ffmpeg_name.c_str(), (void*)fmt);	AVFormatContext *ic = NULL;	int err = av_open_input_file(&ic, ffmpeg_name.c_str(), fmt, 0, 0);	if (err) {		lib::logger::get_logger()->trace("ffmpeg_demux::supported(%s): av_open_input_file returned error %d, ic=0x%x", url_str.c_str(), err, (void*)ic);		if (ic) av_close_input_file(ic);		return NULL;	}	//err = av_read_play(ic); 	err = av_find_stream_info(ic);	if (err < 0) {		lib::logger::get_logger()->trace("ffmpeg_demux::supported(%s): av_find_stream_info returned error %d, ic=0x%x", url_str.c_str(), err, (void*)ic);		if (ic) av_close_input_file(ic);		return NULL;	}		AM_DBG dump_format(ic, 0, ffmpeg_name.c_str(), 0);	AM_DBG lib::logger::get_logger()->debug("ffmpeg_demux::supported: rate=%d, channels=%d", am_get_codec_var(ic->streams[0]->codec,sample_rate), am_get_codec_var(ic->streams[0]->codec,channels));	assert(ic);	return ic;}voidffmpeg_demux::cancel(){	if (is_running())		stop();	release();}int ffmpeg_demux::audio_stream_nr() {	int stream_index;	for (stream_index=0; stream_index < m_con->nb_streams; stream_index++) {		if (am_get_codec_var(m_con->streams[stream_index]->codec, codec_type) == CODEC_TYPE_AUDIO)			return stream_index;	}		return -1;}int ffmpeg_demux::video_stream_nr() {	int stream_index;	for (stream_index=0; stream_index < m_con->nb_streams; stream_index++) {		if (am_get_codec_var(m_con->streams[stream_index]->codec, codec_type) == CODEC_TYPE_VIDEO) {			return stream_index;		}	}		return -1;}doubleffmpeg_demux::duration(){	// XXX this is a double now, later this should retrun a long long int	// XXX Note that this code knows that m_clip_{begin,end} and m_con->duration	// are both microseconds	timestamp_t dur = m_con->duration;	if (m_clip_end > 0 && m_clip_end < dur)		dur = m_clip_end;	dur -= m_clip_begin; 	return dur / (double)AV_TIME_BASE;	}	int ffmpeg_demux::nstreams(){	return m_con->nb_streams;}audio_format&ffmpeg_demux::get_audio_format(){	 return m_audio_fmt; }video_format& ffmpeg_demux::get_video_format() { 	m_lock.enter();	if (m_video_fmt.width == 0) m_video_fmt.width = am_get_codec_var(m_con->streams[video_stream_nr()]->codec, width);	if (m_video_fmt.height == 0) m_video_fmt.height = am_get_codec_var(m_con->streams[video_stream_nr()]->codec, height);	m_lock.leave();	return m_video_fmt; 	}void ffmpeg_demux::add_datasink(demux_datasink *parent, int stream_index){	m_lock.enter();	assert(stream_index >= 0 && stream_index < MAX_STREAMS);	assert(m_sinks[stream_index] == 0);	m_sinks[stream_index] = parent;	m_nstream++;	m_lock.leave();}voidffmpeg_demux::seek(timestamp_t time){	m_lock.enter();	m_clip_begin = time;	m_clip_begin_set = false;	m_lock.leave();}voidffmpeg_demux::remove_datasink(int stream_index){	m_lock.enter();	assert(stream_index >= 0 && stream_index < MAX_STREAMS);	assert(m_sinks[stream_index] != 0);	if (m_sinks[stream_index])		// signal EOF		m_sinks[stream_index]->data_avail(0, 0, 0);	m_sinks[stream_index] = 0;	m_nstream--;	m_lock.leave();	if (m_nstream <= 0) cancel();}unsigned longffmpeg_demux::run(){	m_lock.enter();	int pkt_nr;#if LIBAVFORMAT_BUILD > 4628	int streamnr = video_stream_nr();#endif	timestamp_t pts;	pkt_nr = 0;	assert(m_con);		AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run: started");	while (!exit_requested()) {		AVPacket pkt1, *pkt = &pkt1;				pkt->pts = 0;		// Read a packet		AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run:  started");		m_lock.leave();#if LIBAVFORMAT_BUILD > 4609		int ret = av_read_frame(m_con, pkt);#else		int ret = av_read_packet(m_con, pkt);#endif		m_lock.enter();		AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run: av_read_packet returned ret= %d, (%lld, 0x%x, %d)", ret, pkt->pts ,pkt->data, pkt->size);		if (ret < 0) break;		pkt_nr++;		AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run: av_read_packet number : %d",pkt_nr);		// Find out where to send it to		assert(pkt->stream_index >= 0 && pkt->stream_index < MAX_STREAMS);		demux_datasink *sink = m_sinks[pkt->stream_index];		if (sink == NULL) {			AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run: Drop data for stream %d (%lld, 0x%x, %d)", pkt->stream_index, pts, pkt->pts ,pkt->data, pkt->size);		} else {			AM_DBG lib::logger::get_logger ()->debug ("ffmpeg_parser::run sending data to datasink (stream %d) (%lld, %lld, 0x%x, %d)", pts, pkt->stream_index, pkt->pts ,pkt->data, pkt->size);			// Wait until there is room in the buffer			while (sink && sink->buffer_full() && !exit_requested()) {				AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run: waiting for buffer space for stream %d", pkt->stream_index);				m_lock.leave();				 // sleep 10 millisec, hardly noticeable#ifdef	AMBULANT_PLATFORM_WIN32				ambulant::lib::sleep_msec(10); // XXXX should be woken by readdone()#else				usleep(10000);#endif//AMBULANT_PLATFORM_WIN32//				sleep(1);   // This is overdoing it				m_lock.enter();				sink = m_sinks[pkt->stream_index];			}			if (sink && !exit_requested()) {								pts = 0;				#if LIBAVFORMAT_BUILD > 4628				if (streamnr > -1) {					if (pkt->dts != AV_NOPTS_VALUE) {            			pts = (timestamp_t) round(( (double) m_con->streams[streamnr]->time_base.num* 1000000.0 /m_con->streams[streamnr]->time_base.den)*pkt->dts);					}				}#elif LIBAVFORMAT_BUILD > 4609				if (pkt->pts != (int64_t)AV_NOPTS_VALUE) {					pts = pkt->pts;												AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run: ffmpeg 0.4.9 pts = %lld", pts);									}#else				if (pkt->pts != (int64_t)AV_NOPTS_VALUE) {					int num = m_con->pts_num;					int den = m_con->pts_den;					pts = (timestamp_t) round(((double) pkt->pts * (((double) num)*1000000)/den));				}#endif								AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run: calling %d.data_avail(%lld, 0x%x, %d, %d) pts=%lld", pkt->stream_index, pkt->pts, pkt->data, pkt->size, pkt->duration, pts);								sink->data_avail(pts, pkt->data, pkt->size);			}		}		AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run: freeing pkt (number %d)",pkt_nr);		av_free_packet(pkt);	}	AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run: final data_avail(0, 0)");	int i;	for (i=0; i<MAX_STREAMS; i++)		if (m_sinks[i])			m_sinks[i]->data_avail(0, 0, 0);	AM_DBG lib::logger::get_logger()->debug("ffmpeg_parser::run: returning");	m_lock.leave();	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -