#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