/*
* $Id$
*
* (C) 2003-2006 Gabest
* (C) 2006-2012 see Authors.txt
*
* This file is part of MPC-HC.
*
* MPC-HC is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* MPC-HC 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "stdafx.h"
#include
#include "MatroskaMuxer.h"
#include "../../../DSUtil/DSUtil.h"
#ifdef STANDALONE_FILTER
#include
#endif
#include "moreuuids.h"
using namespace MatroskaWriter;
#ifdef STANDALONE_FILTER
const AMOVIESETUP_MEDIATYPE sudPinTypesOut[] = {
{&MEDIATYPE_Stream, &MEDIASUBTYPE_Matroska}
};
const AMOVIESETUP_PIN sudpPins[] = {
{L"Input", FALSE, FALSE, FALSE, TRUE, &CLSID_NULL, NULL, 0, NULL},
{L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, _countof(sudPinTypesOut), sudPinTypesOut}
};
const AMOVIESETUP_FILTER sudFilter[] = {
{&__uuidof(CMatroskaMuxerFilter), MatroskaMuxerName, MERIT_DO_NOT_USE, _countof(sudpPins), sudpPins, CLSID_LegacyAmFilterCategory}
};
CFactoryTemplate g_Templates[] = {
{sudFilter[0].strName, sudFilter[0].clsID, CreateInstance, NULL, &sudFilter[0]}
};
int g_cTemplates = _countof(g_Templates);
STDAPI DllRegisterServer()
{
return AMovieDllRegisterServer2(TRUE);
}
STDAPI DllUnregisterServer()
{
return AMovieDllRegisterServer2(FALSE);
}
#include "../../FilterApp.h"
CFilterApp theApp;
#endif
//
// CMatroskaMuxerFilter
//
CMatroskaMuxerFilter::CMatroskaMuxerFilter(LPUNKNOWN pUnk, HRESULT* phr)
: CBaseFilter(NAME("CMatroskaMuxerFilter"), pUnk, this, __uuidof(this))
, m_rtCurrent(0)
, m_fNegative(true), m_fPositive(false)
{
if (phr) {
*phr = S_OK;
}
m_pOutput.Attach(DNew CMatroskaMuxerOutputPin(NAME("CMatroskaMuxerOutputPin"), this, this, phr));
AddInput();
srand(clock());
}
CMatroskaMuxerFilter::~CMatroskaMuxerFilter()
{
CAutoLock cAutoLock(this);
}
STDMETHODIMP CMatroskaMuxerFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
CheckPointer(ppv, E_POINTER);
*ppv = NULL;
return
// QI(IAMFilterMiscFlags)
QI(IMediaSeeking)
QI(IMatroskaMuxer)
__super::NonDelegatingQueryInterface(riid, ppv);
}
UINT CMatroskaMuxerFilter::GetTrackNumber(CBasePin* pPin)
{
UINT nTrackNumber = 0;
POSITION pos = m_pInputs.GetHeadPosition();
while (pos) {
nTrackNumber++;
if (m_pInputs.GetNext(pos) == pPin) {
return nTrackNumber;
}
}
return 0;
}
void CMatroskaMuxerFilter::AddInput()
{
POSITION pos = m_pInputs.GetHeadPosition();
while (pos) {
CBasePin* pPin = m_pInputs.GetNext(pos);
if (!pPin->IsConnected()) {
return;
}
}
CStringW name;
name.Format(L"Track %d", m_pInputs.GetCount() + 1);
HRESULT hr;
CAutoPtr pPin(DNew CMatroskaMuxerInputPin(name, this, this, &hr));
m_pInputs.AddTail(pPin);
}
int CMatroskaMuxerFilter::GetPinCount()
{
return m_pInputs.GetCount() + (m_pOutput ? 1 : 0);
}
CBasePin* CMatroskaMuxerFilter::GetPin(int n)
{
CAutoLock cAutoLock(this);
if (n >= 0 && n < (int)m_pInputs.GetCount()) {
if (POSITION pos = m_pInputs.FindIndex(n)) {
return m_pInputs.GetAt(pos);
}
}
if (n == m_pInputs.GetCount() && m_pOutput) {
return m_pOutput;
}
return NULL;
}
STDMETHODIMP CMatroskaMuxerFilter::Stop()
{
CAutoLock cAutoLock(this);
HRESULT hr;
if (FAILED(hr = __super::Stop())) {
return hr;
}
CallWorker(CMD_EXIT);
return hr;
}
STDMETHODIMP CMatroskaMuxerFilter::Pause()
{
CAutoLock cAutoLock(this);
FILTER_STATE fs = m_State;
HRESULT hr;
if (FAILED(hr = __super::Pause())) {
return hr;
}
if (fs == State_Stopped && m_pOutput) {
CAMThread::Create();
CallWorker(CMD_RUN);
}
return hr;
}
STDMETHODIMP CMatroskaMuxerFilter::Run(REFERENCE_TIME tStart)
{
CAutoLock cAutoLock(this);
HRESULT hr;
if (FAILED(hr = __super::Run(tStart))) {
return hr;
}
return hr;
}
// IAMFilterMiscFlags
STDMETHODIMP_(ULONG) CMatroskaMuxerFilter::GetMiscFlags()
{
return AM_FILTER_MISC_FLAGS_IS_RENDERER;
}
// IMediaSeeking
STDMETHODIMP CMatroskaMuxerFilter::GetCapabilities(DWORD* pCapabilities)
{
return pCapabilities ? *pCapabilities =
AM_SEEKING_CanGetDuration |
AM_SEEKING_CanGetCurrentPos, S_OK : E_POINTER;
}
STDMETHODIMP CMatroskaMuxerFilter::CheckCapabilities(DWORD* pCapabilities)
{
CheckPointer(pCapabilities, E_POINTER);
if (*pCapabilities == 0) {
return S_OK;
}
DWORD caps;
GetCapabilities(&caps);
caps &= *pCapabilities;
return caps == 0 ? E_FAIL : caps == *pCapabilities ? S_OK : S_FALSE;
}
STDMETHODIMP CMatroskaMuxerFilter::IsFormatSupported(const GUID* pFormat)
{
return !pFormat ? E_POINTER : *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
}
STDMETHODIMP CMatroskaMuxerFilter::QueryPreferredFormat(GUID* pFormat)
{
return GetTimeFormat(pFormat);
}
STDMETHODIMP CMatroskaMuxerFilter::GetTimeFormat(GUID* pFormat)
{
return pFormat ? *pFormat = TIME_FORMAT_MEDIA_TIME, S_OK : E_POINTER;
}
STDMETHODIMP CMatroskaMuxerFilter::IsUsingTimeFormat(const GUID* pFormat)
{
return IsFormatSupported(pFormat);
}
STDMETHODIMP CMatroskaMuxerFilter::SetTimeFormat(const GUID* pFormat)
{
return S_OK == IsFormatSupported(pFormat) ? S_OK : E_INVALIDARG;
}
STDMETHODIMP CMatroskaMuxerFilter::GetDuration(LONGLONG* pDuration)
{
CheckPointer(pDuration, E_POINTER);
*pDuration = 0;
POSITION pos = m_pInputs.GetHeadPosition();
while (pos) {
REFERENCE_TIME rt = m_pInputs.GetNext(pos)->m_rtDur;
if (rt > *pDuration) {
*pDuration = rt;
}
}
return S_OK;
}
STDMETHODIMP CMatroskaMuxerFilter::GetStopPosition(LONGLONG* pStop)
{
return E_NOTIMPL;
}
STDMETHODIMP CMatroskaMuxerFilter::GetCurrentPosition(LONGLONG* pCurrent)
{
CheckPointer(pCurrent, E_POINTER);
*pCurrent = m_rtCurrent;
return S_OK;
}
STDMETHODIMP CMatroskaMuxerFilter::ConvertTimeFormat(LONGLONG* pTarget, const GUID* pTargetFormat, LONGLONG Source, const GUID* pSourceFormat)
{
return E_NOTIMPL;
}
STDMETHODIMP CMatroskaMuxerFilter::SetPositions(LONGLONG* pCurrent, DWORD dwCurrentFlags, LONGLONG* pStop, DWORD dwStopFlags)
{
return E_NOTIMPL;
}
STDMETHODIMP CMatroskaMuxerFilter::GetPositions(LONGLONG* pCurrent, LONGLONG* pStop)
{
return E_NOTIMPL;
}
STDMETHODIMP CMatroskaMuxerFilter::GetAvailable(LONGLONG* pEarliest, LONGLONG* pLatest)
{
return E_NOTIMPL;
}
STDMETHODIMP CMatroskaMuxerFilter::SetRate(double dRate)
{
return E_NOTIMPL;
}
STDMETHODIMP CMatroskaMuxerFilter::GetRate(double* pdRate)
{
return E_NOTIMPL;
}
STDMETHODIMP CMatroskaMuxerFilter::GetPreroll(LONGLONG* pllPreroll)
{
return E_NOTIMPL;
}
// IMatroskaMuxer
STDMETHODIMP CMatroskaMuxerFilter::CorrectTimeOffset(bool fNegative, bool fPositive)
{
m_fNegative = fNegative;
m_fPositive = fPositive;
return S_OK;
}
//
ULONGLONG GetStreamPosition(IStream* pStream)
{
ULARGE_INTEGER pos = {0, 0};
pStream->Seek(*(LARGE_INTEGER*)&pos, STREAM_SEEK_CUR, &pos);
return pos.QuadPart;
}
ULONGLONG SetStreamPosition(IStream* pStream, ULONGLONG seekpos)
{
LARGE_INTEGER pos;
pos.QuadPart = seekpos;
ULARGE_INTEGER posnew;
posnew.QuadPart = GetStreamPosition(pStream);
pStream->Seek(pos, STREAM_SEEK_SET, &posnew);
return posnew.QuadPart;
}
DWORD CMatroskaMuxerFilter::ThreadProc()
{
CComQIPtr pStream;
if (!m_pOutput || !(pStream = m_pOutput->GetConnected())) {
for (;;) {
DWORD cmd = GetRequest();
if (cmd == CMD_EXIT) {
CAMThread::m_hThread = NULL;
}
Reply(S_OK);
if (cmd == CMD_EXIT) {
return 0;
}
}
}
REFERENCE_TIME rtDur = 0;
GetDuration(&rtDur);
SetStreamPosition(pStream, 0);
ULARGE_INTEGER uli = {0};
pStream->SetSize(uli);
EBML hdr;
hdr.DocType.Set("matroska");
hdr.DocTypeVersion.Set(1);
hdr.DocTypeReadVersion.Set(1);
hdr.Write(pStream);
Segment().Write(pStream);
ULONGLONG segpos = GetStreamPosition(pStream);
// TODO
MatroskaWriter::QWORD voidlen = 100;
if (rtDur > 0) {
voidlen += int(1.0 * rtDur / MAXCLUSTERTIME / 10000 + 0.5) * 20;
} else {
voidlen += int(1.0 * 1000 * 60 * 60 * 24 / MAXCLUSTERTIME + 0.5) * 20; // when no duration is known, allocate for 24 hours (~340k)
}
ULONGLONG voidpos = GetStreamPosition(pStream);
{
Void v(voidlen);
voidlen = v.Size();
v.Write(pStream);
}
// Meta Seek
Seek seek;
CAutoPtr sh;
// Segment Info
sh.Attach(DNew SeekHead());
sh->ID.Set(0x1549A966);
sh->Position.Set(GetStreamPosition(pStream) - segpos);
seek.SeekHeads.AddTail(sh);
ULONGLONG infopos = GetStreamPosition(pStream);
Info info;
info.MuxingApp.Set(L"DirectShow Matroska Muxer");
info.TimeCodeScale.Set(1000000);
info.Duration.Set((float)rtDur / 10000);
struct tm _2001 = {0, 0, 0, 1, 0, 101, 0, 0, 1};
info.DateUTC.Set((_time64(NULL) - _mktime64(&_2001)) * 1000000000);
info.Write(pStream);
// Tracks
sh.Attach(DNew SeekHead());
sh->ID.Set(0x1654AE6B);
sh->Position.Set(GetStreamPosition(pStream) - segpos);
seek.SeekHeads.AddTail(sh);
UINT64 TrackNumber = 0;
/*
CNode