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

📄 ffmpeg_video.cpp

📁 彩信浏览器
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// 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_video.h" #include "ambulant/net/ffmpeg_audio.h" #include "ambulant/net/ffmpeg_common.h" #include "ambulant/net/ffmpeg_factory.h" #include "ambulant/net/demux_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 // How many video frames we would like to buffer at least. This number should// not be too low, otherwise a fast consumer will see only I and P frames// because these are produced before the B frames.// On second thoughts this seems a bad idea, so setting MIN_VIDEO_FRAMES to zero.#define MIN_VIDEO_FRAMES 0// How many video frames we would like to buffer at most.#define MAX_VIDEO_FRAMES 100// 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;typedef lib::no_arg_callback<ffmpeg_video_decoder_datasource> framedone_callback;video_datasource_factory *ambulant::net::get_ffmpeg_video_datasource_factory(){#if 0	static video_datasource_factory *s_factory;		if (!s_factory) s_factory = new ffmpeg_video_datasource_factory();	return s_factory;#else	return new ffmpeg_video_datasource_factory();#endif}video_datasource* ffmpeg_video_datasource_factory::new_video_datasource(const net::url& url, timestamp_t clip_begin, timestamp_t clip_end){		AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_datasource_factory::new_video_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) {		AM_DBG 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 video in the file.	if (thread->video_stream_nr() < 0) {		thread->cancel();		lib::logger::get_logger()->trace("ffmpeg: No video stream in %s", repr(url).c_str());		return NULL;	}		// Next, if there is video we check that we can decode this type of video	// stream.	video_format fmt = thread->get_video_format();	//fmt.parameters = (void*) context;	AVCodecContext *enc = (AVCodecContext *)fmt.parameters;		AM_DBG lib::logger::get_logger()->debug("ffmpeg: Stream type %d, codec_id %d", enc->codec_type, enc->codec_id);   	if (!ffmpeg_video_decoder_datasource::supported(fmt)) {		thread->cancel();		lib::logger::get_logger()->trace("ffmpeg: Unsupported video stream in %s", repr(url).c_str());		return NULL;	}		// All seems well. Create the demux reader and the decoder.	video_datasource *ds = demux_video_datasource::new_demux_video_datasource(url, thread);	if (!ds) {		lib::logger::get_logger()->debug("ffmpeg_video_datasource_factory::new_video_datasource: could not allocate ffmpeg_demux_video_datasource");		thread->cancel();		return NULL;	}	video_datasource *dds =  new ffmpeg_video_decoder_datasource(ds, fmt);	if (dds == NULL) {		lib::logger::get_logger()->debug("ffmpeg_video_datasource_factory::new_video_datasource: could not allocate ffmpeg_video_datasource");		thread->cancel();		return NULL;	}	// Finally, tell the demux datasource to skip ahead to clipBegin, if	// it can do so. No harm done if it can't: the decoder will then skip	// any unneeded frames.	ds->read_ahead(clip_begin);		AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_datasource_factory::new_video_datasource (dds = 0x%x)", (void*) dds);	return dds;		}		// **************************** ffmpeg_video_decoder_datasource ********************boolffmpeg_video_decoder_datasource::supported(const video_format& fmt){	if (fmt.name != "ffmpeg") return false;	AVCodecContext *enc = (AVCodecContext *)fmt.parameters;	if (enc->codec_type != CODEC_TYPE_VIDEO) {		AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_datasource_factory::supported: not a video stream !(%d, %d)", enc->codec_type, CODEC_TYPE_VIDEO);		return false;	}	if (avcodec_find_decoder(enc->codec_id) == NULL) {		AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_datasource_factory::supported: cannot open video codec (codec_id: %d)", enc->codec_id);		return false;	}	return true;}ffmpeg_video_decoder_datasource::ffmpeg_video_decoder_datasource(video_datasource* src, video_format fmt):	m_src(src),	m_con(NULL),	m_event_processor(NULL),	m_client_callback(NULL),	m_pts_last_frame(0),	m_last_p_pts(0),	m_video_clock(0), // XXX Mod by Jack (unsure). Was: src->get_clip_begin()	m_frame_count(0),	m_dropped_count(0),	m_elapsed(0),	m_start_input(true){			AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::ffmpeg_video_decoder_datasource() (this = 0x%x)", (void*)this);		ffmpeg_init();	AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_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_video_decoder_datasource: could not select %s(0x%x) decoder"), fmt.name.c_str(), fmt.parameters);	m_fmt = fmt;	m_old_frame = ts_pointer_pair(0, NULL);}ffmpeg_video_decoder_datasource::~ffmpeg_video_decoder_datasource(){	AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::~ffmpeg_video_decoder_datasource(0x%x)", (void*)this);	stop();	if (m_dropped_count) lib::logger::get_logger()->debug("ffmpeg_video_decoder: dropped %d of %d frames", m_dropped_count, m_frame_count);}voidffmpeg_video_decoder_datasource::stop(){	m_lock.enter();	if (m_src) {		m_src->stop();		int rem = m_src->release();		if (rem) lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::stop(0x%x): m_src refcount=%d", (void*)this, rem); 	}	m_src = NULL;	AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::stop(0x%x)", (void*)this);	if (m_con) {		avcodec_close(m_con);		AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::stop(): avcodec_close(m_con=0x%x) called", m_con);		m_con = NULL;	}	if (m_client_callback) delete m_client_callback;	m_client_callback = NULL;	// And delete any frames left	while ( ! m_frames.empty() ) {		_pop_top_frame();	}	if (m_old_frame.second) {		free(m_old_frame.second);		m_old_frame.second = NULL;	}	m_lock.leave();}	boolffmpeg_video_decoder_datasource::has_audio(){	m_lock.enter();	bool rv = m_src && m_src->has_audio();	m_lock.leave();	return rv;}audio_datasource *ffmpeg_video_decoder_datasource::get_audio_datasource(){	m_lock.enter();	audio_datasource *rv = NULL;	if (m_src) 		rv = m_src->get_audio_datasource();	m_lock.leave();	return rv;}void ffmpeg_video_decoder_datasource::start_frame(ambulant::lib::event_processor *evp, 	ambulant::lib::event *callbackk, timestamp_t timestamp){	m_lock.enter();	AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::start_frame: (this = 0x%x)", (void*) this);	if (m_client_callback != NULL) {		delete m_client_callback;		m_client_callback = NULL;		lib::logger::get_logger()->error("ffmpeg_video_decoder_datasource::start(): m_client_callback already set!");	}	if (m_frames.size() > 0 /* XXXX Check timestamp! */ || _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);			if (timestamp < 0) timestamp = 0;			lib::timer::time_type timestamp_milli = timestamp/1000; // micro to milli			lib::timer::time_type now_milli = evp->get_timer()->elapsed();			lib::timer::time_type delta_milli = 0;			if (now_milli < timestamp_milli)				delta_milli = timestamp_milli - now_milli;			AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::start: trigger client callback timestamp_milli=%d delta_milli=%d, now_milli=%d", (int)timestamp_milli, (int)delta_milli, (int)now_milli);			// Sanity check: we don't want this to be more than a second into the future			if (delta_milli > 1000) {				lib::logger::get_logger()->trace("ffmpeg_video: frame is %f seconds in the future", delta_milli / 1000.0);				lib::logger::get_logger()->debug("ffmpeg_video: elapsed()=%dms, timestamp=%dms", now_milli, timestamp_milli);			}			evp->add_event(callbackk, delta_milli+1, ambulant::lib::ep_high);		} else {			lib::logger::get_logger()->debug("Internal error: ffmpeg_video_decoder_datasource::start(): no client callback!");			lib::logger::get_logger()->warn(gettext("Programmer error encountered during video playback"));		}	} else {		// We have no data available. Start our source, and in our data available callback we		// will signal the client.		m_client_callback = callbackk;		m_event_processor = evp;	}	// Don't restart our source if we are at end of file.	if ( _end_of_file() ) m_start_input = false;		if (m_start_input) {		AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::start_frame() Calling m_src->start_frame(..)");		lib::event *e = new framedone_callback(this, &ffmpeg_video_decoder_datasource::data_avail);		assert(m_src);		m_src->start_frame(evp, e, 0);		m_start_input = false;	}	m_lock.leave();}voidprint_frames(sorted_frames frames) {  	sorted_frames f(frames);	while (f.size()) {		ts_pointer_pair e = f.top();		printf("e.first=%d ", (int) e.first);		f.pop();	}	printf("\n");	return;}ts_pointer_pair ffmpeg_video_decoder_datasource::_pop_top_frame() {	// pop a frame, return the new top frame	// the old top frame is remembered in m_old_frame	// old data in m_old_frame is freed.  	if (m_old_frame.second) {		free (m_old_frame.second);		m_old_frame.second = NULL;		AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource._pop_top_frame(): free'n m_old_frame.second");	}		if (m_frames.empty()) {		AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource._pop_top_frame():no more frames left returning m_old_frame");		return m_old_frame;	}	m_old_frame = m_frames.top();	m_frames.pop();	return m_frames.top();}void ffmpeg_video_decoder_datasource::frame_done(timestamp_t now, bool keepdata){	m_lock.enter();	if (m_frames.size() == 0) {		m_lock.leave();		return;	}	AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.frame_done(%d)", (int)now);	while ( m_frames.size() && m_old_frame.first <= now) {		AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::frame_done: discarding m_old_frame timestamp=%d, now=%d, data ptr = 0x%x",(int)m_old_frame.first,(int)now, m_old_frame.second);		_pop_top_frame();	}	if (!keepdata) {		AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::frame_done(%d): free(0x%x)", (int)now, m_old_frame.second);		free(m_old_frame.second);		m_old_frame.second = NULL;	}	m_lock.leave();}int ffmpeg_video_decoder_datasource::width(){		m_lock.enter();	_need_fmt_uptodate();	m_lock.leave();	return m_fmt.width;}int ffmpeg_video_decoder_datasource::height(){	m_lock.enter();	_need_fmt_uptodate();	m_lock.leave();	return m_fmt.height;}int ffmpeg_video_decoder_datasource::frameduration(){	if(m_fmt.frameduration <=0)		_need_fmt_uptodate();	/* frame rates > 100 fps are unlikely	   For mp4 H263 video, ffmpeg fills its time_base.den with 1000,	   resulting in frameduration == 1000 musec, which is wrong.	   ffplay gets the correct frame rate from its stream, only takes	   it from the codec if the stream doesn't have that information	   See: ffmpeg/libavformat/utils.c, function dump_format().	*/	if(m_fmt.frameduration <= 9999)		m_fmt.frameduration = 33000;	return m_fmt.frameduration;}voidffmpeg_video_decoder_datasource::_need_fmt_uptodate(){	// Private method: no locking	if (m_fmt.height == 0) {			m_fmt.height = m_con->height;	}	if (m_fmt.width == 0) {			m_fmt.width = m_con->width;	}			AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::_need_fmt_uptodate(): frameduration = %lld", m_fmt. frameduration);

⌨️ 快捷键说明

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