/*
* Copyright (C) 2003-2006 Gabest
* http://www.gabest.org
*
* This Program 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 2, or (at your option)
* any later version.
*
* This Program 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 GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
#include "stdafx.h"
#include <math.h>
#include <atlbase.h>
#include <ks.h>
#include <ksmedia.h>
#include "libmpeg2.h"
#include "Mpeg2DecFilter.h"
#include <xmmintrin.h>
#include <emmintrin.h>
#include "..\..\..\DSUtil\DSUtil.h"
#include "..\..\..\DSUtil\MediaTypes.h"
#include <initguid.h>
#include "..\..\..\..\include\moreuuids.h"
#define EPSILON 1e-4
#ifdef REGISTER_FILTER
const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] =
{
{&MEDIATYPE_DVD_ENCRYPTED_PACK, &MEDIASUBTYPE_MPEG2_VIDEO},
{&MEDIATYPE_MPEG2_PACK, &MEDIASUBTYPE_MPEG2_VIDEO},
{&MEDIATYPE_MPEG2_PES, &MEDIASUBTYPE_MPEG2_VIDEO},
{&MEDIATYPE_Video, &MEDIASUBTYPE_MPEG2_VIDEO},
{&MEDIATYPE_Video, &MEDIASUBTYPE_MPEG1Packet},
{&MEDIATYPE_Video, &MEDIASUBTYPE_MPEG1Payload},
};
const AMOVIESETUP_MEDIATYPE sudPinTypesOut[] =
{
{&MEDIATYPE_Video, &MEDIASUBTYPE_IYUV},
};
const AMOVIESETUP_PIN sudpPins[] =
{
{L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesIn), sudPinTypesIn},
{L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesOut), sudPinTypesOut}
};
const AMOVIESETUP_FILTER sudFilter[] =
{
{&__uuidof(CMpeg2DecFilter), L"Mpeg2Dec Filter", 0x40000002, countof(sudpPins), sudpPins},
};
CFactoryTemplate g_Templates[] =
{
{sudFilter[0].strName, sudFilter[0].clsID, CreateInstance<CMpeg2DecFilter>, NULL, &sudFilter[0]},
};
int g_cTemplates = countof(g_Templates);
STDAPI DllRegisterServer()
{
return AMovieDllRegisterServer2(TRUE);
}
STDAPI DllUnregisterServer()
{
return AMovieDllRegisterServer2(FALSE);
}
//
#include "..\..\..\..\include\detours\detours.h"
DETOUR_TRAMPOLINE(BOOL WINAPI Real_IsDebuggerPresent(), IsDebuggerPresent);
BOOL WINAPI Mine_IsDebuggerPresent()
{
TRACE(_T("Oops, somebody was trying to be naughty! (called IsDebuggerPresent)\n"));
return FALSE;
}
DETOUR_TRAMPOLINE(LONG WINAPI Real_ChangeDisplaySettingsExA(LPCSTR lpszDeviceName, LPDEVMODEA lpDevMode, HWND hwnd, DWORD dwFlags, LPVOID lParam), ChangeDisplaySettingsExA);
DETOUR_TRAMPOLINE(LONG WINAPI Real_ChangeDisplaySettingsExW(LPCWSTR lpszDeviceName, LPDEVMODEW lpDevMode, HWND hwnd, DWORD dwFlags, LPVOID lParam), ChangeDisplaySettingsExW);
LONG WINAPI Mine_ChangeDisplaySettingsEx(LONG ret, DWORD dwFlags, LPVOID lParam)
{
if(dwFlags&CDS_VIDEOPARAMETERS)
{
VIDEOPARAMETERS* vp = (VIDEOPARAMETERS*)lParam;
if(vp->Guid == GUIDFromCString(_T("{02C62061-1097-11d1-920F-00A024DF156E}"))
&& (vp->dwFlags&VP_FLAGS_COPYPROTECT))
{
if(vp->dwCommand == VP_COMMAND_GET)
{
if((vp->dwTVStandard&VP_TV_STANDARD_WIN_VGA) && vp->dwTVStandard != VP_TV_STANDARD_WIN_VGA)
{
TRACE(_T("Ooops, tv-out enabled? macrovision checks suck..."));
vp->dwTVStandard = VP_TV_STANDARD_WIN_VGA;
}
}
else if(vp->dwCommand == VP_COMMAND_SET)
{
TRACE(_T("Ooops, as I already told ya, no need for any macrovision bs here"));
return 0;
}
}
}
return ret;
}
LONG WINAPI Mine_ChangeDisplaySettingsExA(LPCSTR lpszDeviceName, LPDEVMODEA lpDevMode, HWND hwnd, DWORD dwFlags, LPVOID lParam)
{
return Mine_ChangeDisplaySettingsEx(Real_ChangeDisplaySettingsExA(lpszDeviceName, lpDevMode, hwnd, dwFlags, lParam), dwFlags, lParam);
}
LONG WINAPI Mine_ChangeDisplaySettingsExW(LPCWSTR lpszDeviceName, LPDEVMODEW lpDevMode, HWND hwnd, DWORD dwFlags, LPVOID lParam)
{
return Mine_ChangeDisplaySettingsEx(Real_ChangeDisplaySettingsExW(lpszDeviceName, lpDevMode, hwnd, dwFlags, lParam), dwFlags, lParam);
}
bool fDetourInited = false;
//
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
if(!fDetourInited)
{
DetourFunctionWithTrampoline((PBYTE)Real_IsDebuggerPresent, (PBYTE)Mine_IsDebuggerPresent);
DetourFunctionWithTrampoline((PBYTE)Real_ChangeDisplaySettingsExA, (PBYTE)Mine_ChangeDisplaySettingsExA);
DetourFunctionWithTrampoline((PBYTE)Real_ChangeDisplaySettingsExW, (PBYTE)Mine_ChangeDisplaySettingsExW);
fDetourInited = true;
}
return DllEntryPoint((HINSTANCE)hModule, dwReason, 0); // "DllMain" of the dshow baseclasses;
}
#endif
//
// CMpeg2DecFilter
//
CMpeg2DecFilter::CMpeg2DecFilter(LPUNKNOWN lpunk, HRESULT* phr)
: CBaseVideoFilter(NAME("CMpeg2DecFilter"), lpunk, phr, __uuidof(this), 1)
, m_fWaitForKeyFrame(true)
{
delete m_pInput;
// delete m_pOutput;
if(FAILED(*phr)) return;
if(!(m_pInput = new CMpeg2DecInputPin(this, phr, L"Video"))) *phr = E_OUTOFMEMORY;
if(FAILED(*phr)) return;
// if(!(m_pOutput = new CMpeg2DecOutputPin(this, phr, L"Output"))) *phr = E_OUTOFMEMORY;
// if(FAILED(*phr)) return;
if(!(m_pSubpicInput = new CSubpicInputPin(this, phr))) *phr = E_OUTOFMEMORY;
if(FAILED(*phr)) return;
if(!(m_pClosedCaptionOutput = new CClosedCaptionOutputPin(this, m_pLock, phr))) *phr = E_OUTOFMEMORY;
if(FAILED(*phr)) return;
SetDeinterlaceMethod(DIAuto);
SetBrightness(0.0);
SetContrast(1.0);
SetHue(0.0);
SetSaturation(1.0);
EnableForcedSubtitles(true);
EnablePlanarYUV(true);
EnableInterlaced(false);
m_rate.Rate = 10000;
m_rate.StartTime = 0;
}
CMpeg2DecFilter::~CMpeg2DecFilter()
{
delete m_pSubpicInput;
delete m_pClosedCaptionOutput;
}
STDMETHODIMP CMpeg2DecFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
return
QI(IMpeg2DecFilter)
QI(IMpeg2DecFilter2)
__super::NonDelegatingQueryInterface(riid, ppv);
}
int CMpeg2DecFilter::GetPinCount()
{
return 4;
}
CBasePin* CMpeg2DecFilter::GetPin(int n)
{
switch(n)
{
case 0: return m_pInput;
case 1: return m_pOutput;
case 2: return m_pSubpicInput;
case 3: return m_pClosedCaptionOutput;
}
return NULL;
}
HRESULT CMpeg2DecFilter::EndOfStream()
{
CAutoLock cAutoLock(&m_csReceive);
m_pClosedCaptionOutput->EndOfStream();
return __super::EndOfStream();
}
HRESULT CMpeg2DecFilter::BeginFlush()
{
m_pClosedCaptionOutput->DeliverBeginFlush();
return __super::BeginFlush();
}
HRESULT CMpeg2DecFilter::EndFlush()
{
m_pClosedCaptionOutput->DeliverEndFlush();
return __super::EndFlush();
}
HRESULT CMpeg2DecFilter::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
CAutoLock cAutoLock(&m_csReceive);
m_pClosedCaptionOutput->DeliverNewSegment(tStart, tStop, dRate);
m_fDropFrames = false;
return __super::NewSegment(tStart, tStop, dRate);
}
void CMpeg2DecFilter::InputTypeChanged()
{
CAutoLock cAutoLock(&m_csReceive);
TRACE(_T("ResetMpeg2Decoder()\n"));
for(int i = 0; i < countof(m_dec->m_pictures); i++)
{
m_dec->m_pictures[i].rtStart = m_dec->m_pictures[i].rtStop = _I64_MIN+1;
m_dec->m_pictures[i].fDelivered = false;
m_dec->m_pictures[i].flags &= ~PIC_MASK_CODING_TYPE;
}
const CMediaType& mt = m_pInput->CurrentMediaType();
BYTE* pSequenceHeader = NULL;
DWORD cbSequenceHeader = 0;
if(mt.formattype == FORMAT_MPEGVideo)
{
pSequenceHeader = ((MPEG1VIDEOINFO*)mt.Format())->bSequenceHeader;
cbSequenceHeader = ((MPEG1VIDEOINFO*)mt.Format())->cbSequenceHeader;
}
else if(mt.formattype == FORMAT_MPEG2_VIDEO)
{
pSequenceHeader = (BYTE*)((MPEG2VIDEOINFO*)mt.Format())->dwSequenceHeader;
cbSequenceHeader = ((MPEG2VIDEOINFO*)mt.Format())->cbSequenceHeader;
}
m_dec->mpeg2_close();
m_dec->mpeg2_init();
m_dec->mpeg2_buffer(pSequenceHeader, pSequenceHeader + cbSequenceHeader);
m_fWaitForKeyFrame = true;
m_fFilm = false;
m_fb.flags = 0;
}
void CMpeg2DecFilter::SetDeinterlaceMethod()
{
ASSERT(m_dec->m_info.m_sequence);
ASSERT(m_dec->m_info.m_display_picture);
DWORD seqflags = m_dec->m_info.m_sequence->flags;
DWORD oldflags = m_fb.flags;
DWORD newflags = m_dec->m_info.m_display_picture->flags;
if(!(seqflags & SEQ_FLAG_PROGRESSIVE_SEQUENCE)
&& !(oldflags & PIC_FLAG_REPEAT_FIRST_FIELD)
&& (newflags & PIC_FLAG_PROGRESSIVE_FRAME))
{
if(!m_fFilm && (newflags & PIC_FLAG_REPEAT_FIRST_FIELD))
{
TRACE(_T("m_fFilm = true\n"));
m_fFilm = true;
}
else if(m_fFilm && !(newflags & PIC_FLAG_REPEAT_FIRST_FIELD))
{
TRACE(_T("m_fFilm = false\n"));
m_fFilm = false;
}
}
const CMediaType& mt = m_pOutput->CurrentMediaType();
if(mt.formattype == FORMAT_VideoInfo2 && (((VIDEOINFOHEADER2*)mt.pbFormat)->dwInterlaceFlags & AMINTERLACE_IsInterlaced))
{
m_fb.di = DIWeave;
}
else
{
m_fb.di = GetDeinterlaceMethod();
if(m_fb.di == DIAuto || m_fb.di != DIWeave && m_fb.di != DIBlend && m_fb.di != DIBob && m_fb.di != DIFieldShift)
{
if(seqflags & SEQ_FLAG_PROGRESSIVE_SEQUENCE)
m_fb.di = DIWeave; // hurray!
else if(m_fFilm)
m_fb.di = DIWeave; // we are lucky
else if(!(m_fb.flags & PIC_FLAG_PROGRESSIVE_FRAME))
m_fb.di = DIBlend; // ok, clear thing
else
// big trouble here, the progressive_frame bit is not reliable :'(
// frames without temporal field diffs can be only detected when ntsc
// uses the repeat field flag (signaled with m_fFilm), if it's not set
// or we have pal then we might end up blending the fields unnecessarily...
m_fb.di = DIBlend;
}
}
m_fb.flags = newflags;
}
void CMpeg2DecFilter::SetTypeSpecificFlags(IMediaSample* pMS)
{
if(CComQIPtr<IMediaSample2> pMS2 = pMS)
{
AM_SAMPLE2_PROPERTIES props;
if(SUCCEEDED(pMS2->GetProperties(sizeof(props), (BYTE*)&props)))
{
props.dwTypeSpecificFlags &= ~0x7f;
const CMediaType& mt = m_pOutput->CurrentMediaType();
if(mt.formattype == FORMAT_VideoInfo2 && (((VIDEOINFOHEADER2*)mt.pbFormat)->dwInterlaceFlags & AMINTERLACE_IsInterlaced))
{
// props.dwTypeSpecificFlags |= AM_VIDEO_FLAG_WEAVE;
if(m_fb.flags & PIC_FLAG_TOP_FIELD_FIRST)
props.dwTypeSpecificFlags |= AM_VIDEO_FLAG_FIELD1FIRST;
if(m_fb.flags & PIC_FLAG_REPEAT_FIRST_FIELD)
props.dwTypeSpecificFlags |= AM_VIDEO_FLAG_REPEAT_FIELD;
if((m_fb.flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_I)
props.dwTypeSpecificFlags |= AM_VIDEO_FLAG_I_SAMPLE;
if((m_fb.flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_P)
props.dwTypeSpecificFlags |= AM_VIDEO_FLAG_P_SAMPLE;
if((m_fb.flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_B)
props.dwTypeSpecificFlags |= AM_VIDEO_FLAG_B_SAMPLE;
}
pMS2->SetProperties(sizeof(props), (BYTE*)&props);
}
}
}
HRESULT CMpeg2DecFilter::Transform(IMediaSample* pIn)
{
HRESULT hr;
BYTE* pDataIn = NULL;
if(FAILED(hr = pIn->GetPointer(&pDataIn)))
return hr;
long len = pIn->GetActualDataLength();
((CDeCSSInputPin*)m_pInput)->StripPacket(pDataIn, len);
if(pIn->IsDiscontinuity() == S_OK)
{
InputTypeChanged();
}
REFERENCE_TIME rtStart = _I64_MIN, rtStop = _I64_MIN;
hr = pIn->GetTime(&rtStart, &rtStop);
if(FAILED(hr)) rtStart = rtStop = _I64_MIN;
while(len >= 0)
{
mpeg2_state_t state = m_dec->mpeg2_parse();
__asm emms; // this one is missing somewhere in the precompiled mmx obj files
switch(state)
{
case STATE_BUFFER:
if(len > 0) {m_dec->mpeg2_buffer(pDataIn, pDataIn + len); len = 0;}
else len = -1;
break;
case STATE_INVALID:
TRACE(_T("*** STATE_INVALID\n"));
break;
case STATE_GOP:
m_pClosedCaptionOutput->Deliver(m_dec->m_info.m_user_data, m_dec->m_info.m_user_data_len);
break;
case STATE_SEQUENCE:
m_AvgTimePerFrame = m_dec->m_info.m_sequence->frame_period
? 10i64 * m_dec->m_info.m_sequence->frame_period / 27
: ((VIDEOINFOHEADER*)m_pInput->CurrentMediaType().Format())->AvgTimePerFrame;
break;
case STATE_PICTURE:
m_dec->m_picture->rtStart = rtStart; rtStart = _I64_MIN;
m_dec->m_picture->fDelivered = false;
m_dec->mpeg2_skip(m_fDropFrames && (m_dec->m_picture->flags&PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_B);
break;
case STATE_SLICE:
case STATE_END:
{
mpeg2_picture_t* picture = m_dec->m_info.m_display_picture;
mpeg2_fbuf_t* fbuf = m_dec->m_info.m_display_fbuf;
if(picture && !(picture->flags&PIC_FLAG_SKIP) && fbuf)
{
ASSERT(!picture->fDelivered);
picture->fDelivered = true;
// frame buffer
int w = m_dec->m_info.m_sequence->picture_width;
int h = m_dec->m_info.m_sequence->picture_height;
int pitch = (m_dec->m_info.m_sequence->width + 31) & ~31;
if(m_fb.w != w || m_fb.h != h || m_fb.pitch != pitch)
m_fb.alloc(w, h, pitch);
// start - end
m_fb.rtStart = picture->rtStart;
if(m_fb.rtStart == _I64_MIN) m_fb.rtStart = m_fb.rtStop;
m_fb.rtStop = m_fb.rtStart + m_AvgTimePerFrame * picture->nb_fields / (m_dec->m_info.m_display_picture_2nd ? 1 : 2);
REFERENCE_TIME rtStart = m_fb.rtStart;
REFERENCE_TIME rtStop = m_fb.rtStop;
//
SetDeinterlaceMethod();
if(S_OK != (hr = DeliverFast())
&& S_OK != (hr = DeliverNormal()))
return hr;
}
}
break;
default:
break;
}
}
return S_OK;
}
HRESULT CMpeg2DecFilter::IsVideoInterlaced()
{
return IsInterlacedEnabled();
}
HRESULT CMpeg2DecFilter::DeliverFast()
{
HRESULT hr;
CAutoLock cAutoLock(&m_csReceive);
mpeg2_fbuf_t* fbuf = m_dec->m_info.m_display_fbuf;
if(!fbuf) return S_FALSE;
{
CAutoLock cAutoLock2(&m_csProps);
if(GetCLSID(m_pInput->GetConnected()) == CLSID_DVDNavigator
|| m_pSubpicInput->HasAnythingToRender(m_fb.rtStart)
|| fabs(m_bright) > EPSILON || fabs(m_cont-1.0) > EPSILON
|| fabs(m_hue) > EPSILON || fabs(m_sat-1.0) > EPSILON)
return S_FALSE;
}
if((m_fb.flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_I)
m_fWaitForKeyFrame = false;
if(m_fb.rtStart < 0 || m_fWaitForKeyFrame)
return S_OK;
const CMediaType& mt = m_pOutput->CurrentMediaType();
if(mt.subtype != MEDIASUBTYPE_I420 && mt.subtype != MEDIASUBTYPE_IYUV && mt.subtype != MEDIASUBTYPE_YV12)
return S_FALSE;
CComPtr<IMediaSample> pOut;
BYTE* pDataOut = NULL;
if(FAILED(hr = GetDeliveryBuffer(m_fb.w, m_fb.h, &pOut))
|| FAILED(hr = pOut->GetPointer(&pDataOut)))
return hr;
if(mt.subtype != MEDIASUBTYPE_I420 && mt.subtype != MEDIASUBTYPE_IYUV && mt.subtype != MEDIASUBTYPE_YV12)
return S_FALSE;
BITMAPINFOHEADER bihOut;
ExtractBIH(&mt, &bihOut);
int w = bihOut.biWidth;
int h = abs(bihOut.biHeight);
int srcpitch = m_dec->m_info.m_sequence->width; // TODO (..+7)&~7; ?
int dstpitch = bihOut.biWidth;
BYTE* y = pDataOut;
BYTE* u = y + dstpitch*h;
BYTE* v = y + dstpitch*h*5/4;
if(bihOut.biCompression == '21VY') {BYTE* tmp = u; u = v; v = tmp;}
if(m_fb.di == DIWeave)
{
BitBltFromI420ToI420(w, h, y, u, v, dstpitch, fbuf->buf[0], fbuf->buf[1], fbuf->buf[2], srcpitch);
}
else if(m_fb.di == DIBlend)
{
DeinterlaceBlend(y, fbuf->buf[0], w, h, dstpitch, srcpitch);
DeinterlaceBlend(u, fbuf->buf[1], w/2, h/2, dstpitch/2, srcpitch/2);
DeinterlaceBlend(v, fbuf->buf[2], w/2, h/2, dstpitch/2, srcpitch/2);
}
else // TODO
{
return S_FALSE;
}
if(h == 1088)
{
memset(y + dstpitch*(h-8), 0xff, dstpitch*8);
memset(u + dstpitch*(h-8)/4, 0x80, dstpitch*8/4);
memset(v + dstpitch*(h-8)/4, 0x80, dstpitch*8/4);
}
if(CMpeg2DecInputPin* pPin = dynamic_cast<CMpeg2DecInputPin*>(m_pInput))
{
CAutoLock cAutoLock(&pPin->m_csRateLock);
if(m_rate.Rate != pPin->m_ratechange.Rate)
{
m_rate.Rate = pPin->m_ratechange.Rate;
m_rate.StartTime = m_fb.rtStart;
}
}
REFERENCE_TIME rtStart = m_fb.rtStart;
REFERENCE_TIME rtStop = m_fb.rtStop;
rtStart = m_rate.StartTime + (rtStart - m_rate.StartTime) * m_rate.Rate / 10000;
rtStop = m_rate.StartTime + (rtStop - m_rate.StartTime) * m_rate.Rate / 10000;
pOut->SetTime(&rtStart, &rtStop);
pOut->SetMediaTime(NULL, NULL);
//
SetTypeSpecificFlags(pOut);
//
return m_pOutput->Deliver(pOut);
}
HRESULT CMpeg2DecFilter::DeliverNormal()
{
HRESULT hr;
CAutoLock cAutoLock(&m_csReceive);
mpeg2_fbuf_t* fbuf = m_dec->m_info.m_display_fbuf;
if(!fbuf) return S_FALSE;
int w = m_fb.w;
int h = m_fb.h;
int spitch = m_dec->m_info.m_sequence->width; // TODO (..+7)&~7; ?
int dpitch = m_fb.pitch;
REFERENCE_TIME rtStart = m_fb.rtStart;
REFERENCE_TIME rtStop = m_fb.rtStop;
bool tff = !!(m_fb.flags&PIC_FLAG_TOP_FIELD_FIRST);
// deinterlace
if(m_fb.di == DIWeave)
{
BitBltFromI420ToI420(w, h, m_fb.buf[0], m_fb.buf[1], m_fb.buf[2], dpitch, fbuf->buf[0], fbuf->buf[1], fbuf->buf[2], spitch);
}
else if(m_fb.di == DIBlend)
{
DeinterlaceBlend(m_fb.buf[0], fbuf->buf[0], w, h, dpitch, spitch);
DeinterlaceBlend(m_fb.buf[1], fbuf->buf[1], w/2, h/2, dpitch/2, spitch/2);
DeinterlaceBlend(m_fb.buf[2], fbuf->buf[2], w/2, h/2, dpitch/2, spitch/2);
}
else if(m_fb.di == DIBob)
{
DeinterlaceBob(m_fb.buf[0], fbuf->buf[0], w, h, dpitch, spitch, tff);
DeinterlaceBob(m_fb.buf[1], fbuf->buf[1], w/2, h/2, dpitch/2, spitch/2, tff);
DeinterlaceBob(m_fb.buf[2], fbuf->buf[2], w/2, h/2, dpitch/2, spitch/2, tff);
m_fb.rtStart = rtStart;
m_fb.rtStop = (rtStart + rtStop) / 2;
}
else if(m_fb.di == DIFieldShift)
{
int soffset = tff ? 0 : spitch;
int doffset = tff ? 0 : dpitch;
BitBltFromRGBToRGB(w, h/2, m_fb.buf[0] + doffset, dpitch*2, 8, fbuf->buf[0] + soffset, spitch*2, 8);
BitBltFromRGBToRGB(w/2, h/4, m_fb.buf[1] + doffset/2, dpitch, 8, fbuf->buf[1] + soffset/2, spitch, 8);
BitBltFromRGBToRGB(w/2, h/4, m_fb.buf[2] + doffset/2, dpitch, 8, fbuf->buf[2] + soffset/2, spitch, 8);
}
// postproc
ApplyBrContHueSat(m_fb.buf[0], m_fb.buf[1], m_fb.buf[2], w, h, dpitch);
// deliver
if(FAILED(hr = Deliver(false)))
return hr;
if(m_fb.di == DIBob)
{
DeinterlaceBob(m_fb.buf[0], fbuf->buf[0], w, h, dpitch, spitch, !tff);
DeinterlaceBob(m_fb.buf[1], fbuf->buf[1], w/2, h/2, dpitch/2, spitch/2, !tff);
DeinterlaceBob(m_fb.buf[2], fbuf->buf[2], w/2, h/2, dpitch/2, spitch/2, !tff);
m_fb.rtStart = (rtStart + rtStop) / 2;
m_fb.rtStop = rtStop;
// postproc
ApplyBrContHueSat(m_fb.buf[0], m_fb.buf[1], m_fb.buf[2], w, h, dpitch);
// deliver
if(FAILED(hr = Deliver(false)))
return hr;
}
else if(m_fb.di == DIFieldShift)
{
int soffset = !tff ? 0 : spitch;
int doffset = !tff ? 0 : dpitch;
BitBltFromRGBToRGB(w, h/2, m_fb.buf[0] + doffset, dpitch*2, 8, fbuf->buf[0] + soffset, spitch*2, 8);
BitBltFromRGBToRGB(w/2, h/4, m_fb.buf[1] + doffset/2, dpitch, 8, fbuf->buf[1] + soffset/2, spitch, 8);
BitBltFromRGBToRGB(w/2, h/4, m_fb.buf[2] + doffset/2, dpitch, 8, fbuf->buf[2] + soffset/2, spitch, 8);
}
return S_OK;
}
HRESULT CMpeg2DecFilter::Deliver(bool fRepeatLast)
{
CAutoLock cAutoLock(&m_csReceive);
if((m_fb.flags&PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_I)
m_fWaitForKeyFrame = false;
if(m_fb.rtStart < 0 || m_fWaitForKeyFrame)
return S_OK;
HRESULT hr;
CComPtr<IMediaSample> pOut;
BYTE* pDataOut = NULL;
if(FAILED(hr = GetDeliveryBuffer(m_fb.w, m_fb.h, &pOut))
|| FAILED(hr = pOut->GetPointer(&pDataOut)))
return hr;
if(m_fb.h == 1088)
{
memset(m_fb.buf[0] + m_fb.w*(m_fb.h-8), 0xff, m_fb.pitch*8);
memset(m_fb.buf[1] + m_fb.w*(m_fb.h-8)/4, 0x80, m_fb.pitch*8/4);
memset(m_fb.buf[2] + m_fb.w*(m_fb.h-8)/4, 0x80, m_fb.pitch*8/4);
}
BYTE** buf = &m_fb.buf[0];
if(m_pSubpicInput->HasAnythingToRender(m_fb.rtStart))
{
BitBltFromI420ToI420(m_fb.w, m_fb.h,
m_fb.buf[3], m_fb.buf[4], m_fb.buf[5], m_fb.pitch,
m_fb.buf[0], m_fb.buf[1], m_fb.buf[2], m_fb.pitch);
buf = &m_fb.buf[3];
m_pSubpicInput->RenderSubpics(m_fb.rtStart, buf, m_fb.pitch, m_fb.h);
}
CopyBuffer(pDataOut, buf, (m_fb.w+7)&~7, m_fb.h, m_fb.pitch, MEDIASUBTYPE_I420, !(m_fb.flags & PIC_FLAG_PROGRESSIVE_FRAME));
//
if(CMpeg2DecInputPin* pPin = dynamic_cast<CMpeg2DecInputPin*>(m_pInput))
{
CAutoLock cAutoLock(&pPin->m_csRateLock);
if(m_rate.Rate != pPin->m_ratechange.Rate)
{
m_rate.Rate = pPin->m_ratechange.Rate;
m_rate.StartTime = m_fb.rtStart;
}
}
REFERENCE_TIME rtStart = m_fb.rtStart;
REFERENCE_TIME rtStop = m_fb.rtStop;
rtStart = m_rate.StartTime + (rtStart - m_rate.StartTime) * m_rate.Rate / 10000;
rtStop = m_rate.StartTime + (rtStop - m_rate.StartTime) * m_rate.Rate / 10000;
pOut->SetTime(&rtStart, &rtStop);
pOut->SetMediaTime(NULL, NULL);
//
SetTypeSpecificFlags(pOut);
//
hr = m_pOutput->Deliver(pOut);
return hr;
}
#include "..\..\..\..\include\IFilterVersion.h"
HRESULT CMpeg2DecFilter::CheckConnect(PIN_DIRECTION dir, IPin* pPin)
{
if(dir == PINDIR_OUTPUT)
{
if(GetCLSID(m_pInput->GetConnected()) == CLSID_DVDNavigator)
{
// one of these needed for dynamic format changes
CLSID clsid = GetCLSID(pPin);
DWORD ver = 0;
if(CComQIPtr<IFilterVersion> pFV = GetFilterFromPin(pPin))
ver = pFV->GetFilterVersion();
if(clsid != CLSID_OverlayMixer
/*&& clsid != CLSID_OverlayMixer2*/
&& clsid != CLSID_VideoMixingRenderer
&& clsid != CLSID_VideoMixingRenderer9
&& clsid != GUIDFromCString(_T("{04FE9017-F873-410E-871E-AB91661A4EF7}")) // ffdshow
&& (clsid != GUIDFromCString(_T("{93A22E7A-5091-45ef-BA61-6DA26156A5D0}")) || ver < 0x0234) // dvobsub
&& (clsid != GUIDFromCString(_T("{9852A670-F845-491b-9BE6-EBD841B8A613}")) || ver < 0x0234) // dvobsub auto
&& clsid != CLSID_DXR) // Haali's video renderer
return E_FAIL;
}
}
return __super::CheckConnect(dir, pPin);
}
HRESULT CMpeg2DecFilter::CheckInputType(const CMediaType* mtIn)
{
if(mtIn->formattype == FORMAT_MPEG2_VIDEO && mtIn->pbFormat)
{
MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mtIn->pbFormat;
if(vih->cbSequenceHeader > 0 && (vih->dwSequenceHeader[0] & 0x00ffffff) != 0x00010000)
return VFW_E_TYPE_NOT_ACCEPTED;
}
return (mtIn->majortype == MEDIATYPE_DVD_ENCRYPTED_PACK && mtIn->subtype == MEDIASUBTYPE_MPEG2_VIDEO
|| mtIn->majortype == MEDIATYPE_MPEG2_PACK && mtIn->subtype == MEDIASUBTYPE_MPEG2_VIDEO
|| mtIn->majortype == MEDIATYPE_MPEG2_PES && mtIn->subtype == MEDIASUBTYPE_MPEG2_VIDEO
|| mtIn->majortype == MEDIATYPE_Video && mtIn->subtype == MEDIASUBTYPE_MPEG2_VIDEO
|| mtIn->majortype == MEDIATYPE_Video && mtIn->subtype == MEDIASUBTYPE_MPEG1Packet
|| mtIn->majortype == MEDIATYPE_Video && mtIn->subtype == MEDIASUBTYPE_MPEG1Payload)
? S_OK
: VFW_E_TYPE_NOT_ACCEPTED;
}
HRESULT CMpeg2DecFilter::CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut)
{
bool fPlanarYUV = mtOut->subtype == MEDIASUBTYPE_YV12
|| mtOut->subtype == MEDIASUBTYPE_I420
|| mtOut->subtype == MEDIASUBTYPE_IYUV;
return SUCCEEDED(__super::CheckTransform(mtIn, mtOut))
&& (!fPlanarYUV || IsPlanarYUVEnabled())
? S_OK
: VFW_E_TYPE_NOT_ACCEPTED;
}
DWORD g_clock;
HRESULT CMpeg2DecFilter::StartStreaming()
{
HRESULT hr = __super::StartStreaming();
if(FAILED(hr)) return hr;
m_dec.Attach(new CMpeg2Dec());
if(!m_dec) return E_OUTOFMEMORY;
InputTypeChanged();
// g_clock = clock();
return S_OK;
}
HRESULT CMpeg2DecFilter::StopStreaming()
{
/*
CString str;
str.Format(_T("%d"), clock()-g_clock);
AfxMessageBox(str);
*/
m_dec.Free();
return __super::StopStreaming();
}
HRESULT CMpeg2DecFilter::AlterQuality(Quality q)
{
if(q.Late > 100*10000i64) m_fDropFrames = true;
else if(q.Late <= 0) m_fDropFrames = false;
// TRACE(_T("CMpeg2DecFilter::AlterQuality: Type=%d, Proportion=%d, Late=%I64d, TimeStamp=%I64d\n"), q.Type, q.Proportion, q.Late, q.TimeStamp);
return S_OK;
}
// IMpeg2DecFilter
STDMETHODIMP CMpeg2DecFilter::SetDeinterlaceMethod(ditype di)
{
CAutoLock cAutoLock(&m_csProps);
m_di = di;
return S_OK;
}
STDMETHODIMP_(ditype) CMpeg2DecFilter::GetDeinterlaceMethod()
{
CAutoLock cAutoLock(&m_csProps);
return m_di;
}
void CMpeg2DecFilter::CalcBrCont(BYTE* YTbl, double bright, double cont)
{
int Cont = (int)(cont * 512);
int Bright = (int)bright;
for(int i = 0; i < 256; i++)
{
int y = ((Cont * (i - 16)) >> 9) + Bright + 16;
YTbl[i] = min(max(y, 0), 255);
// YTbl[i] = min(max(y, 16), 235);
}
}
void CMpeg2DecFilter::CalcHueSat(BYTE* UTbl, BYTE* VTbl, double hue, double sat)
{
int Sat = (int)(sat * 512);
double Hue = (hue * 3.1415926) / 180.0;
int Sin = (int)(sin(Hue) * 4096);
int Cos = (int)(cos(Hue) * 4096);
for(int y = 0; y < 256; y++)
{
for(int x = 0; x < 256; x++)
{
int u = x - 128;
int v = y - 128;
int ux = (u * Cos + v * Sin) >> 12;
v = (v * Cos - u * Sin) >> 12;
u = ((ux * Sat) >> 9) + 128;
v = ((v * Sat) >> 9) + 128;
u = min(max(u, 16), 235);
v = min(max(v, 16), 235);
UTbl[(y << 8) | x] = u;
VTbl[(y << 8) | x] = v;
}
}
}
void CMpeg2DecFilter::ApplyBrContHueSat(BYTE* srcy, BYTE* srcu, BYTE* srcv, int w, int h, int pitch)
{
CAutoLock cAutoLock(&m_csProps);
if(fabs(m_bright) > EPSILON || fabs(m_cont-1.0) > EPSILON)
{
int size = pitch*h;
if((g_cpuid.m_flags&CCpuID::sse2) && ((DWORD_PTR)srcy & 15) == 0)
{
short Cont = (short)(min(max(m_cont, 0) * 512, (1<<16)-1));
short Bright = (short)(m_bright + 16);
__m128i bc = _mm_set_epi16(Bright, Cont, Bright, Cont, Bright, Cont, Bright, Cont);
__m128i zero = _mm_setzero_si128();
__m128i _16 = _mm_set1_epi16(16);
__m128i _512 = _mm_set1_epi16(512);
for(int i = 0, j = size>>4; i < j; i++)
{
__m128i r = _mm_load_si128((__m128i*)&srcy[i*16]);
__m128i rl = _mm_unpacklo_epi8(r, zero);
__m128i rh = _mm_unpackhi_epi8(r, zero);
rl = _mm_subs_epi16(rl, _16);
rh = _mm_subs_epi16(rh, _16);
__m128i rll = _mm_unpacklo_epi16(rl, _512);
__m128i rlh = _mm_unpackhi_epi16(rl, _512);
__m128i rhl = _mm_unpacklo_epi16(rh, _512);
__m128i rhh = _mm_unpackhi_epi16(rh, _512);
rll = _mm_madd_epi16(rll, bc);
rlh = _mm_madd_epi16(rlh, bc);
rhl = _mm_madd_epi16(rhl, bc);
rhh = _mm_madd_epi16(rhh, bc);
rll = _mm_srai_epi32(rll, 9);
rlh = _mm_srai_epi32(rlh, 9);
rhl = _mm_srai_epi32(rhl, 9);
rhh = _mm_srai_epi32(rhh, 9);
rl = _mm_packs_epi32(rll, rlh);
rh = _mm_packs_epi32(rhl, rhh);
r = _mm_packus_epi16(rl, rh);
_mm_store_si128((__m128i*)&srcy[i*16], r);
}
srcy += size>>4;
size &= 15;
}
for(; size > 0; size--)
{
*srcy++ = m_YTbl[*srcy];
}
}
pitch /= 2;
w /= 2;
h /= 2;
if(fabs(m_hue) > EPSILON || fabs(m_sat-1.0) > EPSILON)
{
for(int size = pitch*h; size > 0; size--)
{
WORD uv = (*srcv<<8)|*srcu;
*srcu++ = m_UTbl[uv];
*srcv++ = m_VTbl[uv];
}
}
}
STDMETHODIMP CMpeg2DecFilter::SetBrightness(double bright)
{
CAutoLock cAutoLock(&m_csProps);
CalcBrCont(m_YTbl, m_bright = bright, m_cont);
return S_OK;
}
STDMETHODIMP CMpeg2DecFilter::SetContrast(double cont)
{
CAutoLock cAutoLock(&m_csProps);
CalcBrCont(m_YTbl, m_bright, m_cont = cont);
return S_OK;
}
STDMETHODIMP CMpeg2DecFilter::SetHue(double hue)
{
CAutoLock cAutoLock(&m_csProps);
CalcHueSat(m_UTbl, m_VTbl, m_hue = hue, m_sat);
return S_OK;
}
STDMETHODIMP CMpeg2DecFilter::SetSaturation(double sat)
{
CAutoLock cAutoLock(&m_csProps);
CalcHueSat(m_UTbl, m_VTbl, m_hue, m_sat = sat);
return S_OK;
}
STDMETHODIMP_(double) CMpeg2DecFilter::GetBrightness()
{
CAutoLock cAutoLock(&m_csProps);
return m_bright;
}
STDMETHODIMP_(double) CMpeg2DecFilter::GetContrast()
{
CAutoLock cAutoLock(&m_csProps);
return m_cont;
}
STDMETHODIMP_(double) CMpeg2DecFilter::GetHue()
{
CAutoLock cAutoLock(&m_csProps);
return m_hue;
}
STDMETHODIMP_(double) CMpeg2DecFilter::GetSaturation()
{
CAutoLock cAutoLock(&m_csProps);
return m_sat;
}
STDMETHODIMP CMpeg2DecFilter::EnableForcedSubtitles(bool fEnable)
{
CAutoLock cAutoLock(&m_csProps);
m_fForcedSubs = fEnable;
return S_OK;
}
STDMETHODIMP_(bool) CMpeg2DecFilter::IsForcedSubtitlesEnabled()
{
CAutoLock cAutoLock(&m_csProps);
return m_fForcedSubs;
}
STDMETHODIMP CMpeg2DecFilter::EnablePlanarYUV(bool fEnable)
{
CAutoLock cAutoLock(&m_csProps);
m_fPlanarYUV = fEnable;
return S_OK;
}
STDMETHODIMP_(bool) CMpeg2DecFilter::IsPlanarYUVEnabled()
{
CAutoLock cAutoLock(&m_csProps);
return m_fPlanarYUV;
}
// IMpeg2DecFilter2
STDMETHODIMP CMpeg2DecFilter::EnableInterlaced(bool fEnable)
{
CAutoLock cAutoLock(&m_csProps);
m_fInterlaced = fEnable;
return S_OK;
}
STDMETHODIMP_(bool) CMpeg2DecFilter::IsInterlacedEnabled()
{
CAutoLock cAutoLock(&m_csProps);
return m_fInterlaced;
}
//
// CMpeg2DecInputPin
//
CMpeg2DecInputPin::CMpeg2DecInputPin(CTransformFilter* pFilter, HRESULT* phr, LPWSTR pName)
: CDeCSSInputPin(NAME("CMpeg2DecInputPin"), pFilter, phr, pName)
{
m_CorrectTS = 0;
m_ratechange.Rate = 10000;
m_ratechange.StartTime = _I64_MAX;
}
// IKsPropertySet
STDMETHODIMP CMpeg2DecInputPin::Set(REFGUID PropSet, ULONG Id, LPVOID pInstanceData, ULONG InstanceLength, LPVOID pPropertyData, ULONG DataLength)
{
if(PropSet != AM_KSPROPSETID_TSRateChange /*&& PropSet != AM_KSPROPSETID_DVD_RateChange*/)
return __super::Set(PropSet, Id, pInstanceData, InstanceLength, pPropertyData, DataLength);
if(PropSet == AM_KSPROPSETID_TSRateChange)
switch(Id)
{
case AM_RATE_SimpleRateChange:
{
AM_SimpleRateChange* p = (AM_SimpleRateChange*)pPropertyData;
if(!m_CorrectTS) return E_PROP_ID_UNSUPPORTED;
CAutoLock cAutoLock(&m_csRateLock);
m_ratechange = *p;
DbgLog((LOG_TRACE, 0, _T("StartTime=%I64d, Rate=%d"), p->StartTime, p->Rate));
}
break;
case AM_RATE_UseRateVersion:
{
WORD* p = (WORD*)pPropertyData;
if(*p > 0x0101) return E_PROP_ID_UNSUPPORTED;
}
break;
case AM_RATE_CorrectTS:
{
LONG* p = (LONG*)pPropertyData;
m_CorrectTS = *p;
}
break;
default:
return E_PROP_ID_UNSUPPORTED;
}
/*
if(PropSet == AM_KSPROPSETID_DVD_RateChange)
switch(Id)
{
case AM_RATE_ChangeRate:
{
AM_DVD_ChangeRate* p = (AM_DVD_ChangeRate*)pPropertyData;
}
break;
default:
return E_PROP_ID_UNSUPPORTED;
}
*/
return S_OK;
}
STDMETHODIMP CMpeg2DecInputPin::Get(REFGUID PropSet, ULONG Id, LPVOID pInstanceData, ULONG InstanceLength, LPVOID pPropertyData, ULONG DataLength, ULONG* pBytesReturned)
{
if(PropSet != AM_KSPROPSETID_TSRateChange /*&& PropSet != AM_KSPROPSETID_DVD_RateChange*/)
return __super::Get(PropSet, Id, pInstanceData, InstanceLength, pPropertyData, DataLength, pBytesReturned);
if(PropSet == AM_KSPROPSETID_TSRateChange)
switch(Id)
{
case AM_RATE_SimpleRateChange:
{
AM_SimpleRateChange* p = (AM_SimpleRateChange*)pPropertyData;
return E_PROP_ID_UNSUPPORTED;
}
break;
case AM_RATE_MaxFullDataRate:
{
AM_MaxFullDataRate* p = (AM_MaxFullDataRate*)pPropertyData;
*p = 8*10000;
*pBytesReturned = sizeof(AM_MaxFullDataRate);
}
break;
case AM_RATE_QueryFullFrameRate:
{
AM_QueryRate* p = (AM_QueryRate*)pPropertyData;
p->lMaxForwardFullFrame = 8*10000;
p->lMaxReverseFullFrame = 8*10000;
*pBytesReturned = sizeof(AM_QueryRate);
}
break;
case AM_RATE_QueryLastRateSegPTS:
{
REFERENCE_TIME* p = (REFERENCE_TIME*)pPropertyData;
return E_PROP_ID_UNSUPPORTED;
}
break;
default:
return E_PROP_ID_UNSUPPORTED;
}
/*
if(PropSet == AM_KSPROPSETID_DVD_RateChange)
switch(Id)
{
case AM_RATE_FullDataRateMax:
{
AM_MaxFullDataRate* p = (AM_MaxFullDataRate*)pPropertyData;
}
break;
case AM_RATE_ReverseDecode:
{
LONG* p = (LONG*)pPropertyData;
}
break;
case AM_RATE_DecoderPosition:
{
AM_DVD_DecoderPosition* p = (AM_DVD_DecoderPosition*)pPropertyData;
}
break;
case AM_RATE_DecoderVersion:
{
LONG* p = (LONG*)pPropertyData;
}
break;
default:
return E_PROP_ID_UNSUPPORTED;
}
*/
return S_OK;
}
STDMETHODIMP CMpeg2DecInputPin::QuerySupported(REFGUID PropSet, ULONG Id, ULONG* pTypeSupport)
{
if(PropSet != AM_KSPROPSETID_TSRateChange /*&& PropSet != AM_KSPROPSETID_DVD_RateChange*/)
return __super::QuerySupported(PropSet, Id, pTypeSupport);
if(PropSet == AM_KSPROPSETID_TSRateChange)
switch(Id)
{
case AM_RATE_SimpleRateChange:
*pTypeSupport = KSPROPERTY_SUPPORT_GET | KSPROPERTY_SUPPORT_SET;
break;
case AM_RATE_MaxFullDataRate:
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
break;
case AM_RATE_UseRateVersion:
*pTypeSupport = KSPROPERTY_SUPPORT_SET;
break;
case AM_RATE_QueryFullFrameRate:
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
break;
case AM_RATE_QueryLastRateSegPTS:
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
break;
case AM_RATE_CorrectTS:
*pTypeSupport = KSPROPERTY_SUPPORT_SET;
break;
default:
return E_PROP_ID_UNSUPPORTED;
}
/*
if(PropSet == AM_KSPROPSETID_DVD_RateChange)
switch(Id)
{
case AM_RATE_ChangeRate:
*pTypeSupport = KSPROPERTY_SUPPORT_SET;
break;
case AM_RATE_FullDataRateMax:
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
break;
case AM_RATE_ReverseDecode:
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
break;
case AM_RATE_DecoderPosition:
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
break;
case AM_RATE_DecoderVersion:
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
break;
default:
return E_PROP_ID_UNSUPPORTED;
}
*/
return S_OK;
}
//
// CMpeg2DecOutputPin
//
CMpeg2DecOutputPin::CMpeg2DecOutputPin(CBaseVideoFilter* pFilter, HRESULT* phr, LPWSTR pName)
: CBaseVideoOutputPin(NAME("CMpeg2DecOutputPin"), pFilter, phr, pName)
{
}
HRESULT CMpeg2DecOutputPin::Active()
{
CAutoLock cAutoLock(m_pLock);
// TODO
if(m_Connected && !m_pOutputQueue)
{
HRESULT hr = NOERROR;
m_pOutputQueue.Attach(new COutputQueue(m_Connected, &hr));
if(!m_pOutputQueue) hr = E_OUTOFMEMORY;
if(FAILED(hr))
{
m_pOutputQueue.Free();
return hr;
}
}
return __super::Active();
}
HRESULT CMpeg2DecOutputPin::Inactive()
{
CAutoLock cAutoLock(m_pLock);
m_pOutputQueue.Free();
return __super::Inactive();
}
HRESULT CMpeg2DecOutputPin::Deliver(IMediaSample* pMediaSample)
{
if(!m_pOutputQueue) return NOERROR;
pMediaSample->AddRef();
return m_pOutputQueue->Receive(pMediaSample);
}
#define CallQueue(call) \
if(!m_pOutputQueue) return NOERROR; \
m_pOutputQueue->##call; \
return NOERROR; \
HRESULT CMpeg2DecOutputPin::DeliverEndOfStream()
{
CallQueue(EOS());
}
HRESULT CMpeg2DecOutputPin::DeliverBeginFlush()
{
CallQueue(BeginFlush());
}
HRESULT CMpeg2DecOutputPin::DeliverEndFlush()
{
CallQueue(EndFlush());
}
HRESULT CMpeg2DecOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
CallQueue(NewSegment(tStart, tStop, dRate));
}
//
// CSubpicInputPin
//
#define PTS2RT(pts) (10000i64*pts/90)
CSubpicInputPin::CSubpicInputPin(CTransformFilter* pFilter, HRESULT* phr)
: CMpeg2DecInputPin(pFilter, phr, L"SubPicture")
, m_spon(TRUE)
, m_fsppal(false)
{
m_sppal[0].Y = 0x00;
m_sppal[0].U = m_sppal[0].V = 0x80;
m_sppal[1].Y = 0xe0;
m_sppal[1].U = m_sppal[1].V = 0x80;
m_sppal[2].Y = 0x80;
m_sppal[2].U = m_sppal[2].V = 0x80;
m_sppal[3].Y = 0x20;
m_sppal[3].U = m_sppal[3].V = 0x80;
}
HRESULT CSubpicInputPin::CheckMediaType(const CMediaType* mtIn)
{
return (mtIn->majortype == MEDIATYPE_DVD_ENCRYPTED_PACK
|| mtIn->majortype == MEDIATYPE_MPEG2_PACK
|| mtIn->majortype == MEDIATYPE_MPEG2_PES
|| mtIn->majortype == MEDIATYPE_Video)
&& (mtIn->subtype == MEDIASUBTYPE_DVD_SUBPICTURE
|| mtIn->subtype == MEDIASUBTYPE_CVD_SUBPICTURE
|| mtIn->subtype == MEDIASUBTYPE_SVCD_SUBPICTURE)
? S_OK
: VFW_E_TYPE_NOT_ACCEPTED;
}
HRESULT CSubpicInputPin::SetMediaType(const CMediaType* mtIn)
{
return CBasePin::SetMediaType(mtIn);
}
bool CSubpicInputPin::HasAnythingToRender(REFERENCE_TIME rt)
{
if(!IsConnected()) return(false);
CAutoLock cAutoLock(&m_csReceive);
POSITION pos = m_sps.GetHeadPosition();
while(pos)
{
spu* sp = m_sps.GetNext(pos);
if(sp->m_rtStart <= rt && rt < sp->m_rtStop && (/*sp->m_psphli ||*/ sp->m_fForced || m_spon))
return(true);
}
return(false);
}
void CSubpicInputPin::RenderSubpics(REFERENCE_TIME rt, BYTE** yuv, int w, int h)
{
CAutoLock cAutoLock(&m_csReceive);
POSITION pos;
// remove no longer needed things first
pos = m_sps.GetHeadPosition();
while(pos)
{
POSITION cur = pos;
spu* sp = m_sps.GetNext(pos);
if(sp->m_rtStop <= rt) m_sps.RemoveAt(cur);
}
pos = m_sps.GetHeadPosition();
while(pos)
{
spu* sp = m_sps.GetNext(pos);
if(sp->m_rtStart <= rt && rt < sp->m_rtStop
&& (m_spon || sp->m_fForced && (((CMpeg2DecFilter*)m_pFilter)->IsForcedSubtitlesEnabled() || sp->m_psphli)))
sp->Render(rt, yuv, w, h, m_sppal, m_fsppal);
}
}
HRESULT CSubpicInputPin::Transform(IMediaSample* pSample)
{
HRESULT hr;
AM_MEDIA_TYPE* pmt;
if(SUCCEEDED(pSample->GetMediaType(&pmt)) && pmt)
{
CMediaType mt(*pmt);
SetMediaType(&mt);
DeleteMediaType(pmt);
}
BYTE* pDataIn = NULL;
if(FAILED(hr = pSample->GetPointer(&pDataIn))) return hr;
long len = pSample->GetActualDataLength();
StripPacket(pDataIn, len);
if(len <= 0) return S_FALSE;
if(m_mt.subtype == MEDIASUBTYPE_SVCD_SUBPICTURE)
{
pDataIn += 4;
len -= 4;
}
if(len <= 0) return S_FALSE;
CAutoLock cAutoLock(&m_csReceive);
REFERENCE_TIME rtStart = 0, rtStop = 0;
hr = pSample->GetTime(&rtStart, &rtStop);
bool fRefresh = false;
if(FAILED(hr))
{
if(!m_sps.IsEmpty())
{
spu* sp = m_sps.GetTail();
sp->m_pData.SetSize(sp->m_pData.GetSize() + len);
memcpy(sp->m_pData.GetData() + sp->m_pData.GetSize() - len, pDataIn, len);
}
}
else
{
POSITION pos = m_sps.GetTailPosition();
while(pos)
{
POSITION cur = pos;
spu* sp = m_sps.GetPrev(pos);
if(sp->m_rtStop == _I64_MAX)
{
sp->m_rtStop = rtStart;
break;
}
}
CAutoPtr<spu> p;
if(m_mt.subtype == MEDIASUBTYPE_DVD_SUBPICTURE) p.Attach(new dvdspu());
else if(m_mt.subtype == MEDIASUBTYPE_CVD_SUBPICTURE) p.Attach(new cvdspu());
else if(m_mt.subtype == MEDIASUBTYPE_SVCD_SUBPICTURE) p.Attach(new svcdspu());
else return E_FAIL;
p->m_rtStart = rtStart;
p->m_rtStop = _I64_MAX;
p->m_pData.SetSize(len);
memcpy(p->m_pData.GetData(), pDataIn, len);
if(m_sphli && p->m_rtStart == PTS2RT(m_sphli->StartPTM))
{
p->m_psphli = m_sphli;
fRefresh = true;
}
m_sps.AddTail(p);
}
if(!m_sps.IsEmpty())
{
m_sps.GetTail()->Parse();
}
if(fRefresh)
{
// ((CMpeg2DecFilter*)m_pFilter)->Deliver(true);
}
return S_FALSE;
}
STDMETHODIMP CSubpicInputPin::EndFlush()
{
CAutoLock cAutoLock(&m_csReceive);
m_sps.RemoveAll();
return S_OK;
}
// IKsPropertySet
STDMETHODIMP CSubpicInputPin::Set(REFGUID PropSet, ULONG Id, LPVOID pInstanceData, ULONG InstanceLength, LPVOID pPropertyData, ULONG DataLength)
{
if(PropSet != AM_KSPROPSETID_DvdSubPic)
return __super::Set(PropSet, Id, pInstanceData, InstanceLength, pPropertyData, DataLength);
bool fRefresh = false;
switch(Id)
{
case AM_PROPERTY_DVDSUBPIC_PALETTE:
{
CAutoLock cAutoLock(&m_csReceive);
AM_PROPERTY_SPPAL* pSPPAL = (AM_PROPERTY_SPPAL*)pPropertyData;
memcpy(m_sppal, pSPPAL->sppal, sizeof(AM_PROPERTY_SPPAL));
m_fsppal = true;
DbgLog((LOG_TRACE, 0, _T("new palette")));
}
break;
case AM_PROPERTY_DVDSUBPIC_HLI:
{
CAutoLock cAutoLock(&m_csReceive);
AM_PROPERTY_SPHLI* pSPHLI = (AM_PROPERTY_SPHLI*)pPropertyData;
m_sphli.Free();
if(pSPHLI->HLISS)
{
POSITION pos = m_sps.GetHeadPosition();
while(pos)
{
spu* sp = m_sps.GetNext(pos);
if(sp->m_rtStart <= PTS2RT(pSPHLI->StartPTM) && PTS2RT(pSPHLI->StartPTM) < sp->m_rtStop)
{
fRefresh = true;
sp->m_psphli.Free();
sp->m_psphli.Attach(new AM_PROPERTY_SPHLI());
memcpy((AM_PROPERTY_SPHLI*)sp->m_psphli, pSPHLI, sizeof(AM_PROPERTY_SPHLI));
}
}
if(!fRefresh) // save it for later, a subpic might be late for this hli
{
m_sphli.Attach(new AM_PROPERTY_SPHLI());
memcpy((AM_PROPERTY_SPHLI*)m_sphli, pSPHLI, sizeof(AM_PROPERTY_SPHLI));
}
}
else
{
POSITION pos = m_sps.GetHeadPosition();
while(pos)
{
spu* sp = m_sps.GetNext(pos);
fRefresh |= !!sp->m_psphli;
sp->m_psphli.Free();
}
}
if(pSPHLI->HLISS)
DbgLog((LOG_TRACE, 0, _T("hli: %I64d - %I64d, (%d,%d) - (%d,%d)"),
PTS2RT(pSPHLI->StartPTM)/10000, PTS2RT(pSPHLI->EndPTM)/10000,
pSPHLI->StartX, pSPHLI->StartY, pSPHLI->StopX, pSPHLI->StopY));
}
break;
case AM_PROPERTY_DVDSUBPIC_COMPOSIT_ON:
{
CAutoLock cAutoLock(&m_csReceive);
AM_PROPERTY_COMPOSIT_ON* pCompositOn = (AM_PROPERTY_COMPOSIT_ON*)pPropertyData;
m_spon = *pCompositOn;
}
break;
default:
return E_PROP_ID_UNSUPPORTED;
}
if(fRefresh)
{
((CMpeg2DecFilter*)m_pFilter)->Deliver(true);
}
return S_OK;
}
STDMETHODIMP CSubpicInputPin::QuerySupported(REFGUID PropSet, ULONG Id, ULONG* pTypeSupport)
{
if(PropSet != AM_KSPROPSETID_DvdSubPic)
return __super::QuerySupported(PropSet, Id, pTypeSupport);
switch(Id)
{
case AM_PROPERTY_DVDSUBPIC_PALETTE:
*pTypeSupport = KSPROPERTY_SUPPORT_SET;
break;
case AM_PROPERTY_DVDSUBPIC_HLI:
*pTypeSupport = KSPROPERTY_SUPPORT_SET;
break;
case AM_PROPERTY_DVDSUBPIC_COMPOSIT_ON:
*pTypeSupport = KSPROPERTY_SUPPORT_SET;
break;
default:
return E_PROP_ID_UNSUPPORTED;
}
return S_OK;
}
// CSubpicInputPin::spu
static __inline BYTE GetNibble(BYTE* p, DWORD* offset, int& nField, int& fAligned)
{
BYTE ret = (p[offset[nField]] >> (fAligned << 2)) & 0x0f;
offset[nField] += 1-fAligned;
fAligned = !fAligned;
return ret;
}
static __inline BYTE GetHalfNibble(BYTE* p, DWORD* offset, int& nField, int& n)
{
BYTE ret = (p[offset[nField]] >> (n << 1)) & 0x03;
if(!n) offset[nField]++;
n = (n-1+4)&3;
return ret;
}
static __inline void DrawPixel(BYTE** yuv, CPoint pt, int pitch, AM_DVD_YUV& c)
{
if(c.Reserved == 0) return;
BYTE* p = &yuv[0][pt.y*pitch + pt.x];
// *p = (*p*(15-contrast) + sppal[color].Y*contrast)>>4;
*p -= (*p - c.Y) * c.Reserved >> 4;
if(pt.y&1) return; // since U/V is half res there is no need to overwrite the same line again
pt.x = (pt.x + 1) / 2;
pt.y = (pt.y /*+ 1*/) / 2; // only paint the upper field always, don't round it
pitch /= 2;
// U/V is exchanged? wierd but looks true when comparing the outputted colors from other decoders
p = &yuv[1][pt.y*pitch + pt.x];
// *p = (BYTE)(((((int)*p-0x80)*(15-contrast) + ((int)sppal[color].V-0x80)*contrast) >> 4) + 0x80);
*p -= (*p - c.V) * c.Reserved >> 4;
p = &yuv[2][pt.y*pitch + pt.x];
// *p = (BYTE)(((((int)*p-0x80)*(15-contrast) + ((int)sppal[color].U-0x80)*contrast) >> 4) + 0x80);
*p -= (*p - c.U) * c.Reserved >> 4;
// Neighter of the blending formulas are accurate (">>4" should be "/15").
// Even though the second one is a bit worse, since we are scaling the difference only,
// the error is still not noticable.
}
static __inline void DrawPixels(BYTE** yuv, int pitch, CPoint pt, int len, AM_DVD_YUV& c, CRect& rc)
{
if(pt.y < rc.top || pt.y >= rc.bottom) return;
if(pt.x < rc.left) {len -= rc.left - pt.x; pt.x = rc.left;}
if(pt.x + len > rc.right) len = rc.right - pt.x;
if(len <= 0 || pt.x >= rc.right) return;
if(c.Reserved == 0)
{
if(rc.IsRectEmpty())
return;
if(pt.y < rc.top || pt.y >= rc.bottom
|| pt.x+len < rc.left || pt.x >= rc.right)
return;
}
while(len-- > 0)
{
DrawPixel(yuv, pt, pitch, c);
pt.x++;
}
}
// CSubpicInputPin::dvdspu
bool CSubpicInputPin::dvdspu::Parse()
{
m_offsets.RemoveAll();
BYTE* p = m_pData.GetData();
WORD packetsize = (p[0]<<8)|p[1];
WORD datasize = (p[2]<<8)|p[3];
if(packetsize > m_pData.GetSize() || datasize > packetsize)
return(false);
int i, next = datasize;
#define GetWORD (p[i]<<8)|p[i+1]; i += 2
do
{
i = next;
int pts = GetWORD;
next = GetWORD;
if(next > packetsize || next < datasize)
return(false);
REFERENCE_TIME rt = m_rtStart + 1024*PTS2RT(pts);
for(bool fBreak = false; !fBreak; )
{
int len = 0;
switch(p[i])
{
case 0x00: len = 0; break;
case 0x01: len = 0; break;
case 0x02: len = 0; break;
case 0x03: len = 2; break;
case 0x04: len = 2; break;
case 0x05: len = 6; break;
case 0x06: len = 4; break;
default: len = 0; break;
}
if(i+len >= packetsize)
{
TRACE(_T("Warning: Wrong subpicture parameter block ending\n"));
break;
}
switch(p[i++])
{
case 0x00: // forced start displaying
m_fForced = true;
break;
case 0x01: // normal start displaying
m_fForced = false;
break;
case 0x02: // stop displaying
m_rtStop = rt;
break;
case 0x03:
m_sphli.ColCon.emph2col = p[i]>>4;
m_sphli.ColCon.emph1col = p[i]&0xf;
m_sphli.ColCon.patcol = p[i+1]>>4;
m_sphli.ColCon.backcol = p[i+1]&0xf;
i += 2;
break;
case 0x04:
m_sphli.ColCon.emph2con = p[i]>>4;
m_sphli.ColCon.emph1con = p[i]&0xf;
m_sphli.ColCon.patcon = p[i+1]>>4;
m_sphli.ColCon.backcon = p[i+1]&0xf;
i += 2;
break;
case 0x05:
m_sphli.StartX = (p[i]<<4) + (p[i+1]>>4);
m_sphli.StopX = ((p[i+1]&0x0f)<<8) + p[i+2]+1;
m_sphli.StartY = (p[i+3]<<4) + (p[i+4]>>4);
m_sphli.StopY = ((p[i+4]&0x0f)<<8) + p[i+5]+1;
i += 6;
break;
case 0x06:
m_offset[0] = GetWORD;
m_offset[1] = GetWORD;
break;
case 0xff: // end of ctrlblk
fBreak = true;
continue;
default: // skip this ctrlblk
fBreak = true;
break;
}
}
offset_t o = {rt, m_sphli};
m_offsets.AddTail(o); // is it always going to be sorted?
}
while(i <= next && i < packetsize);
return(true);
}
void CSubpicInputPin::dvdspu::Render(REFERENCE_TIME rt, BYTE** yuv, int w, int h, AM_DVD_YUV* sppal, bool fsppal)
{
BYTE* p = m_pData.GetData();
DWORD offset[2] = {m_offset[0], m_offset[1]};
AM_PROPERTY_SPHLI sphli = m_sphli;
CPoint pt(sphli.StartX, sphli.StartY);
CRect rc(pt, CPoint(sphli.StopX, sphli.StopY));
CRect rcclip(0, 0, w, h);
rcclip &= rc;
if(m_psphli)
{
rcclip &= CRect(m_psphli->StartX, m_psphli->StartY, m_psphli->StopX, m_psphli->StopY);
sphli = *m_psphli;
}
else if(m_offsets.GetCount() > 1)
{
POSITION pos = m_offsets.GetTailPosition();
while(pos)
{
const offset_t& o = m_offsets.GetPrev(pos);
if(rt >= o.rt) {sphli = o.sphli; break;}
}
}
AM_DVD_YUV pal[4];
pal[0] = sppal[fsppal ? sphli.ColCon.backcol : 0];
pal[0].Reserved = sphli.ColCon.backcon;
pal[1] = sppal[fsppal ? sphli.ColCon.patcol : 1];
pal[1].Reserved = sphli.ColCon.patcon;
pal[2] = sppal[fsppal ? sphli.ColCon.emph1col : 2];
pal[2].Reserved = sphli.ColCon.emph1con;
pal[3] = sppal[fsppal ? sphli.ColCon.emph2col : 3];
pal[3].Reserved = sphli.ColCon.emph2con;
int nField = 0;
int fAligned = 1;
DWORD end[2] = {offset[1], (p[2]<<8)|p[3]};
while((nField == 0 && offset[0] < end[0]) || (nField == 1 && offset[1] < end[1]))
{
DWORD code;
if((code = GetNibble(p, offset, nField, fAligned)) >= 0x4
|| (code = (code << 4) | GetNibble(p, offset, nField, fAligned)) >= 0x10
|| (code = (code << 4) | GetNibble(p, offset, nField, fAligned)) >= 0x40
|| (code = (code << 4) | GetNibble(p, offset, nField, fAligned)) >= 0x100)
{
DrawPixels(yuv, w, pt, code >> 2, pal[code&3], rcclip);
if((pt.x += code >> 2) < rc.right) continue;
}
DrawPixels(yuv, w, pt, rc.right - pt.x, pal[code&3], rcclip);
if(!fAligned) GetNibble(p, offset, nField, fAligned); // align to byte
pt.x = rc.left;
pt.y++;
nField = 1 - nField;
}
}
// CSubpicInputPin::cvdspu
bool CSubpicInputPin::cvdspu::Parse()
{
BYTE* p = m_pData.GetData();
WORD packetsize = (p[0]<<8)|p[1];
WORD datasize = (p[2]<<8)|p[3];
if(packetsize > m_pData.GetSize() || datasize > packetsize)
return(false);
p = m_pData.GetData() + datasize;
for(int i = datasize, j = packetsize-4; i <= j; i+=4, p+=4)
{
switch(p[0])
{
case 0x0c:
break;
case 0x04:
m_rtStop = m_rtStart + 10000i64*((p[1]<<16)|(p[2]<<8)|p[3])/90;
break;
case 0x17:
m_sphli.StartX = ((p[1]&0x0f)<<6) + (p[2]>>2);
m_sphli.StartY = ((p[2]&0x03)<<8) + p[3];
break;
case 0x1f:
m_sphli.StopX = ((p[1]&0x0f)<<6) + (p[2]>>2);
m_sphli.StopY = ((p[2]&0x03)<<8) + p[3];
break;
case 0x24: case 0x25: case 0x26: case 0x27:
m_sppal[0][p[0]-0x24].Y = p[1];
m_sppal[0][p[0]-0x24].U = p[2];
m_sppal[0][p[0]-0x24].V = p[3];
break;
case 0x2c: case 0x2d: case 0x2e: case 0x2f:
m_sppal[1][p[0]-0x2c].Y = p[1];
m_sppal[1][p[0]-0x2c].U = p[2];
m_sppal[1][p[0]-0x2c].V = p[3];
break;
case 0x37:
m_sppal[0][3].Reserved = p[2]>>4;
m_sppal[0][2].Reserved = p[2]&0xf;
m_sppal[0][1].Reserved = p[3]>>4;
m_sppal[0][0].Reserved = p[3]&0xf;
break;
case 0x3f:
m_sppal[1][3].Reserved = p[2]>>4;
m_sppal[1][2].Reserved = p[2]&0xf;
m_sppal[1][1].Reserved = p[3]>>4;
m_sppal[1][0].Reserved = p[3]&0xf;
break;
case 0x47:
m_offset[0] = (p[2]<<8)|p[3];
break;
case 0x4f:
m_offset[1] = (p[2]<<8)|p[3];
break;
default:
break;
}
}
return(true);
}
void CSubpicInputPin::cvdspu::Render(REFERENCE_TIME rt, BYTE** yuv, int w, int h, AM_DVD_YUV* sppal, bool fsppal)
{
BYTE* p = m_pData.GetData();
DWORD offset[2] = {m_offset[0], m_offset[1]};
CRect rcclip(0, 0, w, h);
/* FIXME: startx/y looks to be wrong in the sample I tested
CPoint pt(m_sphli.StartX, m_sphli.StartY);
CRect rc(pt, CPoint(m_sphli.StopX, m_sphli.StopY));
*/
CSize size(m_sphli.StopX - m_sphli.StartX, m_sphli.StopY - m_sphli.StartY);
CPoint pt((rcclip.Width() - size.cx) / 2, (rcclip.Height()*3 - size.cy*1) / 4);
CRect rc(pt, size);
int nField = 0;
int fAligned = 1;
DWORD end[2] = {offset[1], (p[2]<<8)|p[3]};
while((nField == 0 && offset[0] < end[0]) || (nField == 1 && offset[1] < end[1]))
{
BYTE code;
if((code = GetNibble(p, offset, nField, fAligned)) >= 0x4)
{
DrawPixels(yuv, w, pt, code >> 2, m_sppal[0][code&3], rcclip);
pt.x += code >> 2;
continue;
}
code = GetNibble(p, offset, nField, fAligned);
DrawPixels(yuv, w, pt, rc.right - pt.x, m_sppal[0][code&3], rcclip);
if(!fAligned) GetNibble(p, offset, nField, fAligned); // align to byte
pt.x = rc.left;
pt.y++;
nField = 1 - nField;
}
}
// CSubpicInputPin::svcdspu
bool CSubpicInputPin::svcdspu::Parse()
{
BYTE* p = m_pData.GetData();
BYTE* p0 = p;
if(m_pData.GetSize() < 2)
return(false);
WORD packetsize = (p[0]<<8)|p[1]; p += 2;
if(packetsize > m_pData.GetSize())
return(false);
bool duration = !!(*p++&0x04);
*p++; // unknown
if(duration)
{
m_rtStop = m_rtStart + 10000i64*((p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3])/90;
p += 4;
}
m_sphli.StartX = m_sphli.StopX = (p[0]<<8)|p[1]; p += 2;
m_sphli.StartY = m_sphli.StopY = (p[0]<<8)|p[1]; p += 2;
m_sphli.StopX += (p[0]<<8)|p[1]; p += 2;
m_sphli.StopY += (p[0]<<8)|p[1]; p += 2;
for(int i = 0; i < 4; i++)
{
m_sppal[i].Y = *p++;
m_sppal[i].U = *p++;
m_sppal[i].V = *p++;
m_sppal[i].Reserved = *p++ >> 4;
}
if(*p++&0xc0)
p += 4; // duration of the shift operation should be here, but it is untested
m_offset[1] = (p[0]<<8)|p[1]; p += 2;
m_offset[0] = p - p0;
m_offset[1] += m_offset[0];
return(true);
}
void CSubpicInputPin::svcdspu::Render(REFERENCE_TIME rt, BYTE** yuv, int w, int h, AM_DVD_YUV* sppal, bool fsppal)
{
BYTE* p = m_pData.GetData();
DWORD offset[2] = {m_offset[0], m_offset[1]};
CRect rcclip(0, 0, w, h);
/* FIXME: startx/y looks to be wrong in the sample I tested (yes, this one too!)
CPoint pt(m_sphli.StartX, m_sphli.StartY);
CRect rc(pt, CPoint(m_sphli.StopX, m_sphli.StopY));
*/
CSize size(m_sphli.StopX - m_sphli.StartX, m_sphli.StopY - m_sphli.StartY);
CPoint pt((rcclip.Width() - size.cx) / 2, (rcclip.Height()*3 - size.cy*1) / 4);
CRect rc(pt, size);
int nField = 0;
int n = 3;
DWORD end[2] = {offset[1], (p[2]<<8)|p[3]};
while((nField == 0 && offset[0] < end[0]) || (nField == 1 && offset[1] < end[1]))
{
BYTE code = GetHalfNibble(p, offset, nField, n);
BYTE repeat = 1 + (code == 0 ? GetHalfNibble(p, offset, nField, n) : 0);
DrawPixels(yuv, w, pt, repeat, m_sppal[code&3], rcclip);
if((pt.x += repeat) < rc.right) continue;
while(n != 3)
GetHalfNibble(p, offset, nField, n); // align to byte
pt.x = rc.left;
pt.y++;
nField = 1 - nField;
}
}
//
// CClosedCaptionOutputPin
//
CClosedCaptionOutputPin::CClosedCaptionOutputPin(CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
: CBaseOutputPin(NAME("CClosedCaptionOutputPin"), pFilter, pLock, phr, L"~CC")
{
}
HRESULT CClosedCaptionOutputPin::CheckMediaType(const CMediaType* mtOut)
{
return mtOut->majortype == MEDIATYPE_AUXLine21Data && mtOut->subtype == MEDIASUBTYPE_Line21_GOPPacket
? S_OK
: VFW_E_TYPE_NOT_ACCEPTED;
}
HRESULT CClosedCaptionOutputPin::GetMediaType(int iPosition, CMediaType* pmt)
{
if(iPosition < 0) return E_INVALIDARG;
if(iPosition > 0) return VFW_S_NO_MORE_ITEMS;
pmt->InitMediaType();
pmt->majortype = MEDIATYPE_AUXLine21Data;
pmt->subtype = MEDIASUBTYPE_Line21_GOPPacket;
pmt->formattype = FORMAT_None;
return S_OK;
}
HRESULT CClosedCaptionOutputPin::DecideBufferSize(IMemAllocator* pAllocator, ALLOCATOR_PROPERTIES* pProperties)
{
pProperties->cBuffers = 1;
pProperties->cbBuffer = 2048;
pProperties->cbAlign = 1;
pProperties->cbPrefix = 0;
HRESULT hr;
ALLOCATOR_PROPERTIES Actual;
if(FAILED(hr = pAllocator->SetProperties(pProperties, &Actual)))
return hr;
return pProperties->cBuffers > Actual.cBuffers || pProperties->cbBuffer > Actual.cbBuffer
? E_FAIL
: NOERROR;
}
HRESULT CClosedCaptionOutputPin::Deliver(const void* ptr, int len)
{
HRESULT hr = S_FALSE;
if(len > 4 && ptr && *(DWORD*)ptr == 0xf8014343 && IsConnected())
{
CComPtr<IMediaSample> pSample;
GetDeliveryBuffer(&pSample, NULL, NULL, 0);
BYTE* pData = NULL;
pSample->GetPointer(&pData);
*(DWORD*)pData = 0xb2010000;
memcpy(pData + 4, ptr, len);
pSample->SetActualDataLength(len + 4);
hr = __super::Deliver(pSample);
}
return hr;
}