Click here to Skip to main content
15,884,472 members
Articles / Desktop Programming / MFC

DirectShow Editing Services (DES) and combining AVI files

Rate me:
Please Sign up or sign in to vote.
4.64/5 (4 votes)
9 Sep 2011CPOL5 min read 35.8K   5.8K   12  
A sample C++ project that uses DES to combine two or more AVI files.
#ifndef c_DESCombine_h
#define c_DESCombine_h

/* 
	The classes declared in this file are used to concatenate (i.e. combine) 2 or more
	AVI files into a single AVI file.  The code is based on the C# sample project 
	DESCombine made available with the DirectShowNET Library (http://directshownet.sourceforge.net/index.html).

	

*/

#pragma once

#ifndef _DEQUE_
#include <deque>
#endif
#ifndef _MEMORY_
#include <memory>
#endif
#ifndef _STRING_
#include <string>
#endif

#ifndef __qedit_h__
#include <Qedit.h>
#endif

#ifndef c_ErrorLog_h
#include "ErrorLog.h"
#endif
#ifndef c_InputFile_h
#include "InputFile.h"
#endif
#ifndef c_RunningObjectTable_h
#include "RunningObjectTable.h"
#endif

#define EC_AudioFileComplete	0x8001
#define EC_VideoFileComplete	0x8002

_COM_SMARTPTR_TYPEDEF( IAMErrorLog, IID_IAMSetErrorLog );
_COM_SMARTPTR_TYPEDEF( IAMTimeline, IID_IAMTimeline );
_COM_SMARTPTR_TYPEDEF( IAMTimelineTrack,  IID_IAMTimelineTrack );
_COM_SMARTPTR_TYPEDEF( IMediaDet, IID_IMediaDet );
_COM_SMARTPTR_TYPEDEF( IMediaEventSink, IID_IMediaEventSink );
_COM_SMARTPTR_TYPEDEF( IRenderEngine, IID_IRenderEngine ); 

namespace DESCombineLib {

typedef std::deque< std::wstring > Log;

/*
	=====================================================================
	=====================================================================
	VideoInformation

	This class manages basic information about a single video stream
	identified in a media file.  For example, if an AVI file contains
	3 video streams then information each stream is manages be a separate
	instance of this class (i.e. 3 instances of this class are required).
	=====================================================================
	=====================================================================
*/
class VideoInformation 
{
public:
	VideoInformation() {
		width = height = ctBits = fps = 0;
	}
	VideoInformation( const VideoInformation& src ) {
		operator=( src );
	}

	// modifiers
	void set( int width, int height, int ctBits, int fps ) {
		this->width = width;
		this->height = height;
		this->ctBits = ctBits;
		this->fps = fps;
	}

	// accessors
	void get( int& width, int& height, int& ctBits, int& fps ) const {
		width = this->width;
		height = this->height;
		ctBits = this->ctBits;
		fps = this->fps;
	}
	int getImageSize() const {
		return width * height;
	}
	bool isDefined() const {
		return width  &&  height  &&  ctBits  &&  fps ? true : false;
	}

	// operators
	VideoInformation& operator=( const VideoInformation& src ) {
		if( this != &src ) {
			width = src.width;
			height = src.height;
			ctBits = src.ctBits;
			fps = src.fps;
		}

		return *this;
	}

public:
	int width;		// pixel width of video
	int height;		// pixel height of video 
	int ctBits;		// number of bit planes in video 
	int fps;		// frame rate used to store video 

}; // VideoInformation


/*
	=====================================================================
	=====================================================================
	StreamInformation

	Manages information about a single stream in a media file.  For example,
	if a media files contains 1 audio stream and 3 video streams then information
	about each stream is manages by a separate instance of this class (i.e. 4 
	instances of this class are required).
	=====================================================================
	=====================================================================
*/
class StreamInformation 
{
public:
	StreamInformation() {
		_length = 0;
	}
	StreamInformation( const StreamInformation& src ) {
		operator=( src );
	}

	// modifiers
	void setLength( LONGLONG src ) {
		_length = src;
	}
	void setMediaType( const CMediaType& src ) {
		_mediaType = src;
	}
	void setVideoInformation( const VideoInformation& src ) { 
		_videoInformation = src;
	}

	// accessors
	LONGLONG getLength() const {
		return _length;
	}
	CMediaType getMediaType() const {
		return _mediaType;
	}
	VideoInformation getVideoInformation() const {
		return _videoInformation;
	}

	// operators
	StreamInformation& operator=( const StreamInformation& src ) {
		if( this != &src ) {
			_length = src._length;
			_mediaType = src._mediaType;
			_videoInformation = src._videoInformation;
		}

		return *this;
	}

private:
	LONGLONG _length;
		// actual media length (in 100ns) as reported by IMediaDet
	CMediaType _mediaType;
		// actual media type as reported by IMediaDet
	VideoInformation _videoInformation;
		// if applicable, information about the video stream

}; // StreamInformation

/*
	=====================================================================
	=====================================================================
	StreamsInformation

	Manages information about all streams in a media file.
	=====================================================================
	=====================================================================
*/
typedef std::deque< StreamInformation > StreamsInformation;

/*
	=====================================================================
	=====================================================================
	MediaFile

	Manages information obtained from a media file.
	=====================================================================
	=====================================================================
*/
class CLASS_DECL MediaFile
{
public:
	MediaFile();
	MediaFile( const InputFile& inputFile );

	// initialization
	void initialize( const InputFile& inputFile );

	// modifiers
	void setFrameLength( int length ) {
		_frameLength = length;
	}

	// accessors
	InputFile getInputFile() const { 
		return _inputFile; 
	}
	int getFrameLength() const {
		return _frameLength;
	}
	int getMaximumFPS() const;
	LONGLONG getMaximumStreamLength() const;
	VideoInformation getMaximumVideoInformation() const;
	int getStreamCount() const {
		return (int)_streamsInformation.size();
	}
	StreamInformation getStreamInformation( int xStream ) const;
	int getStreamNumber( int xStream ) const;
	StreamsInformation getStreamsInformation() const {
		return _streamsInformation;
	}

	// operators
	MediaFile& operator=( const MediaFile& src );

private:
	InputFile _inputFile;
		//
	StreamsInformation _streamsInformation;	
		// stores information about each stream in the media file 
	int _frameLength;			
		/* Media length in number of video frames.  This attribute is only 
			defined at runtime when DES is actually concatenating media files.
			It is used by the application to determine when a media file 
			has been completely processed (i.e. concatenated).
		*/

}; // MediaFile

/*
	=====================================================================
	=====================================================================
	MediaFiles

	Manages information about all media files to be processed.
	=====================================================================
	=====================================================================
*/
class CLASS_DECL MediaFiles : public std::deque< MediaFile >
{
public:
	void initialize( const InputFiles& inputs );

	int getMaximumFPS() const;
	VideoInformation getMaximumVideoInformation() const;
};

/*
	=====================================================================
	=====================================================================
	Group

	Manages information for a single timeline group.
	=====================================================================
	=====================================================================
*/
class CLASS_DECL Group
{
public:
	Group();
	Group( const CMediaType& mtype, const IAMTimelinePtr& timeline, double fps );
	Group( const Group& src ) {
		operator=( src );
	}
#ifdef _DEBUG
	~Group();
#endif

	// initialization
	void initialize( const CMediaType& mtype, const IAMTimelinePtr& timeline, double fps );

	// modifiers
	void add( const MediaFile& mediaFile, long streamNumber, LONGLONG start = 0, LONGLONG end = -1LL );
	void clear();

	// accessors
	MediaFile& file( int xFile );
	int getCount() const {
		return (int) _files.size();
	}
	LONGLONG getLength() const {
		return _length;
	}

	// operators
	Group& operator=( const Group& src );

private:
	LONGLONG _length;			// used to store current length
	MediaFiles _files;			// manages names and durations of files to be processed
	IAMTimelineTrackPtr _track;	// reference to timeline track
	double _fps;				// group video frame rate 
	IAMTimelinePtr _timeline;	// refernce to DES timeline 

}; // Group

/*
	=====================================================================
	=====================================================================
	IDESCombineCB

	A callback interface that can be implemented by callers to DESCombine who wish to 
	perform processing on video or audio frames.

	Classes which implement this interfaces can be passed to DESCombine.RenderToWindow or 
	DESCombine.RenderToAVI.  Each audio or video frame that is processed by DES will be 
	passed to this callback which can perform additional processing.
	=====================================================================
	=====================================================================
*/
class CLASS_DECL IDESCombineCB
{
public:
	virtual HRESULT BufferCB( 
		const std::wstring& filename,	// filename currently being processed
		double sampleTime,				// time stamp in seconds
		BYTE* buffer,					// reference to buffer containing media data
		int bufferLength				// length of buffer containing media data
	) = 0;

}; // IDESCombineCB

/*
	=====================================================================
	=====================================================================
	SampleGrabberCB

	Implementation of DS ISampleGrabberCB.
	=====================================================================
	=====================================================================
*/
class CLASS_DECL SampleGrabberCB 
: public ISampleGrabberCB
, public CUnknown
{
public:
	typedef CUnknown inherited;

public:
	SampleGrabberCB();
	SampleGrabberCB( 
		const Group& group, 
		IDESCombineCB* desCombineCB, 
		IMediaEventSinkPtr& sink, 
		int endOfFileEventCode 
	);
	SampleGrabberCB( const SampleGrabberCB& src );
	~SampleGrabberCB() {
		clear();
	}

	void initialize( 
		const Group& group,				// used to identify files that are being processed
		IDESCombineCB* desCombineCB,	// can be used to perform application specific processing
		IMediaEventSinkPtr& sink,		// used to notify application a file has been processed
		int endOfFileEventCode			// used to notify application of media type that was processed
	);
	void clear();

	SampleGrabberCB& operator=( const SampleGrabberCB& src );

public: // IUnknown overrides
	DECLARE_IUNKNOWN;
	STDMETHODIMP NonDelegatingQueryInterface( REFIID riid, void** ppv );

public: // ISampleGrabberCB overrides
	STDMETHODIMP BufferCB( double sampleTime, BYTE *buffer, long bufferLength );
	STDMETHODIMP SampleCB( double sampleTime, IMediaSample *sample );

private:
	std::wstring _getType();
	void _logError( const std::wstring& msg, HRESULT hr );

private:
	Group _group;
	IDESCombineCB* _desCombineCB;
	IMediaEventSinkPtr _mediaEventSink;
	int _endOfFileEventCode;

	int _xCurrentFile;
	int _xCurrentFrame;
	int _ctMaximumFrames;
	std::wstring _currentFilename;

}; // SampleGrabberCB

/*
	=====================================================================
	=====================================================================
	DESCombine

	Concatenates 1 or more AVI media files. The result is output to a new AVI file.
	=====================================================================
	=====================================================================
*/
class CLASS_DECL DESCombine
{
public:
	typedef long EventCode;
	typedef std::deque< EventCode > EventCodes;
	typedef std::deque< Group > TimelineGroups;
	typedef std::deque< CRect > VideoDisplayAreas;
	typedef IBaseFilterPtr (*GetEncoderCallbackFunction)();

	enum eClassState {
		exUndefined,
		exConstructed,
		exFilesAdded,
		exRenderSelected,
		exGraphStarted,
		exCancelled,
		exGraphCompleted
	};

public:
	DESCombine();
	~DESCombine();

	// modifiers
	void initialize( const InputFiles& inputs );
	void renderToWindow( 
		CWnd* wndDisplay, 
		IDESCombineCB* videoCallback, 
		IDESCombineCB* audioCallback 
	);
	void renderToAVI( 
		const std::wstring& outputMediaFilename, 
		GetEncoderCallbackFunction videoCompressorCB,
		GetEncoderCallbackFunction audioCompressorCB,
		IDESCombineCB* videoCallback, 
		IDESCombineCB* audioCallback
	);
	void startRendering( CWnd* wndNotify );
	void getEventCodes( EventCodes& dst );
	void onGraphNotify( EventCodes& unprocessedEventCodes, WPARAM wParam, LPARAM lParam );
	void cancel();
	void cleanUp();

	// accessors
	LONGLONG getTimelineDuration();
	std::wstring getXML();
	bool initialized() const {
		return exConstructed <= _state ? true : false;
	}

private:
	void _addMediaFilesToTimeline( const MediaFiles& mediaFiles );
	void _addMediaFileToTimeline( const MediaFile& mediaFile, LONGLONG start = 0, LONGLONG stop = -1LL );
	void _buildFilterGraph(
		ICaptureGraphBuilder2Ptr& captureGraphBuilder2,
		SampleGrabberCB* sampleGrabberCB,
		const std::wstring& type,
		IPinPtr& pin,
		IBaseFilterPtr& compressor,
		IBaseFilterPtr& renderer
	);
	void _buildFilterGraph( 
		ICaptureGraphBuilder2Ptr& captureGraphBuilder2,
		SampleGrabberCB* sampleGrabberCB,
		const std::wstring& type,
		IPinPtr& pin,
		IBaseFilterPtr& renderer
	);
	void _changeState( eClassState newState ) {
		_state = newState;
	}
	void _cleanUpRenderEngine();
	void _disableEventNotification();
	void _enableEventNotification();
	CMediaType _getGroupAudioMediaType();
	int _getFPS( const CMediaType& src ) const;
	CMediaType _getGroupVideoMediaType( const MediaFiles& src );
	int _getVideoCount() const;
	void _getVideoDisplayAreas( VideoDisplayAreas& dst ) const;
	VideoInformation _getVideoInformation( const MediaFiles& src ) const {
		return src.getMaximumVideoInformation();
	}
	void _initializeRenderEngine();
	void _initializeTimelineGroups( 
		const MediaFiles& mediaFiles,
		const int fps,
		const CMediaType& audioMT,
		const CMediaType& videoMT
	);
	bool _isVideo( IPinPtr& pin ) const;
	void _postMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 );
	void _postString( const wchar_t* message );
	void _setErrorLog();
	void _startRendering();
	void _stopRendering();
	void _writeFilterGraph( const std::wstring& filename );

private:
	CWnd* _wndNotify;		// window where event notifications are sent to 
	CWnd* _wndDisplay;		// window used to preview the rendered timeline
	eClassState _state;
	IAMTimelinePtr _timeline;
	IGraphBuilderPtr _graphBuilder;
	IMediaControlPtr _mediaControl;
	IMediaEventExPtr _mediaEvent;
	IRenderEnginePtr _renderEngine;
	RunningObjectTable _runningObjectTable;

	TimelineGroups _timelineGroups;
	IAMErrorLogPtr _errorLog;

	CCriticalSection _criticalSection;

}; // DESCombine

} // namespace DESCombineLib {

#endif // #ifndef c_DESCombine_h

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Retired
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions