/*
* (C) 2003-2006 Gabest
* (C) 2006-2013 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
#include
#include
#include
#include "../../../DSUtil/DSUtil.h"
#include "../../../DSUtil/MediaTypes.h"
#include "RealMediaSplitter.h"
#include "../../../Subtitles/SubtitleInputPin.h"
#ifdef STANDALONE_FILTER
#include
#endif
#include "moreuuids.h"
#define MAXPACKETS_RV 10000
template
static void bswap(T& var)
{
BYTE* s = (BYTE*)&var;
for (BYTE* d = s + sizeof(var) - 1; s < d; s++, d--) {
*s ^= *d, *d ^= *s, *s ^= *d;
}
}
void rvinfo::bswap()
{
::bswap(dwSize);
::bswap(w);
::bswap(h);
::bswap(bpp);
::bswap(unk1);
::bswap(fps);
::bswap(type1);
::bswap(type2);
}
void rainfo3::bswap()
{
::bswap(fourcc);
::bswap(version);
::bswap(header_size);
::bswap(data_size);
}
void rainfo::bswap()
{
::bswap(version1);
::bswap(version2);
::bswap(header_size);
::bswap(flavor);
::bswap(coded_frame_size);
::bswap(sub_packet_h);
::bswap(frame_size);
::bswap(sub_packet_size);
}
void rainfo4::bswap()
{
__super::bswap();
::bswap(sample_rate);
::bswap(sample_size);
::bswap(channels);
}
void rainfo5::bswap()
{
__super::bswap();
::bswap(sample_rate);
::bswap(sample_size);
::bswap(channels);
}
using namespace RMFF;
#ifdef STANDALONE_FILTER
const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] = {
{&MEDIATYPE_Stream, &MEDIASUBTYPE_NULL},
};
const AMOVIESETUP_PIN sudpPins[] = {
{L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, nullptr, _countof(sudPinTypesIn), sudPinTypesIn},
{L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, nullptr, 0, nullptr}
};
const AMOVIESETUP_MEDIATYPE sudPinTypesIn2[] = {
{&MEDIATYPE_Video, &MEDIASUBTYPE_RV20},
{&MEDIATYPE_Video, &MEDIASUBTYPE_RV30},
{&MEDIATYPE_Video, &MEDIASUBTYPE_RV40},
{&MEDIATYPE_Video, &MEDIASUBTYPE_RV41},
};
const AMOVIESETUP_MEDIATYPE sudPinTypesOut2[] = {
{&MEDIATYPE_Video, &MEDIASUBTYPE_NULL},
};
const AMOVIESETUP_PIN sudpPins2[] = {
{L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, nullptr, _countof(sudPinTypesIn2), sudPinTypesIn2},
{L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, nullptr, _countof(sudPinTypesOut2), sudPinTypesOut2}
};
const AMOVIESETUP_MEDIATYPE sudPinTypesIn3[] = {
{&MEDIATYPE_Audio, &MEDIASUBTYPE_14_4},
{&MEDIATYPE_Audio, &MEDIASUBTYPE_28_8},
{&MEDIATYPE_Audio, &MEDIASUBTYPE_ATRC},
{&MEDIATYPE_Audio, &MEDIASUBTYPE_COOK},
{&MEDIATYPE_Audio, &MEDIASUBTYPE_DNET},
{&MEDIATYPE_Audio, &MEDIASUBTYPE_SIPR},
{&MEDIATYPE_Audio, &MEDIASUBTYPE_AAC},
{&MEDIATYPE_Audio, &MEDIASUBTYPE_RAAC},
{&MEDIATYPE_Audio, &MEDIASUBTYPE_RACP},
};
const AMOVIESETUP_MEDIATYPE sudPinTypesOut3[] = {
{&MEDIATYPE_Audio, &MEDIASUBTYPE_PCM},
};
const AMOVIESETUP_PIN sudpPins3[] = {
{L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, nullptr, _countof(sudPinTypesIn3), sudPinTypesIn3},
{L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, nullptr, _countof(sudPinTypesOut3), sudPinTypesOut3}
};
const AMOVIESETUP_FILTER sudFilter[] = {
{&__uuidof(CRealMediaSplitterFilter), RMSplitterName, MERIT_NORMAL, _countof(sudpPins), sudpPins, CLSID_LegacyAmFilterCategory},
{&__uuidof(CRealMediaSourceFilter), RMSourceName, MERIT_NORMAL, 0, nullptr, CLSID_LegacyAmFilterCategory},
{&__uuidof(CRealVideoDecoder), L"MPC RealVideo Decoder", MERIT_NORMAL, _countof(sudpPins2), sudpPins2, CLSID_LegacyAmFilterCategory},
{&__uuidof(CRealAudioDecoder), L"MPC RealAudio Decoder", MERIT_NORMAL, _countof(sudpPins3), sudpPins3, CLSID_LegacyAmFilterCategory},
};
CFactoryTemplate g_Templates[] = {
{sudFilter[0].strName, sudFilter[0].clsID, CreateInstance, nullptr, &sudFilter[0]},
{sudFilter[1].strName, sudFilter[1].clsID, CreateInstance, nullptr, &sudFilter[1]},
{sudFilter[2].strName, sudFilter[2].clsID, CreateInstance, nullptr, &sudFilter[2]},
{sudFilter[3].strName, sudFilter[3].clsID, CreateInstance, nullptr, &sudFilter[3]},
};
int g_cTemplates = _countof(g_Templates);
STDAPI DllRegisterServer()
{
RegisterSourceFilter(CLSID_AsyncReader, MEDIASUBTYPE_RealMedia, _T("0,4,,2E524D46"), _T(".rm"), _T(".rmvb"), _T(".ram"), nullptr);
return AMovieDllRegisterServer2(TRUE);
}
STDAPI DllUnregisterServer()
{
UnRegisterSourceFilter(MEDIASUBTYPE_RealMedia);
return AMovieDllRegisterServer2(FALSE);
}
#include "../../FilterApp.h"
CFilterApp theApp;
#endif
//
// CRealMediaSplitterFilter
//
CRealMediaSplitterFilter::CRealMediaSplitterFilter(LPUNKNOWN pUnk, HRESULT* phr)
: CBaseSplitterFilter(NAME("CRealMediaSplitterFilter"), pUnk, phr, __uuidof(this), MAXPACKETS_RV)
{
}
CRealMediaSplitterFilter::~CRealMediaSplitterFilter()
{
}
STDMETHODIMP CRealMediaSplitterFilter::QueryFilterInfo(FILTER_INFO* pInfo)
{
CheckPointer(pInfo, E_POINTER);
ValidateReadWritePtr(pInfo, sizeof(FILTER_INFO));
if (m_pName && m_pName[0] == L'M' && m_pName[1] == L'P' && m_pName[2] == L'C') {
(void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);
} else {
wcscpy_s(pInfo->achName, RMSourceName);
}
pInfo->pGraph = m_pGraph;
if (m_pGraph) {
m_pGraph->AddRef();
}
return S_OK;
}
HRESULT CRealMediaSplitterFilter::CreateOutputs(IAsyncReader* pAsyncReader)
{
CheckPointer(pAsyncReader, E_POINTER);
{
DWORD dw;
if (FAILED(pAsyncReader->SyncRead(0, 4, (BYTE*)&dw)) || dw != 'FMR.') {
return E_FAIL;
}
}
HRESULT hr = E_FAIL;
m_pFile.Free();
m_pFile.Attach(DEBUG_NEW CRMFile(pAsyncReader, hr));
if (!m_pFile) {
return E_OUTOFMEMORY;
}
if (FAILED(hr)) {
m_pFile.Free();
return hr;
}
m_rtNewStart = m_rtCurrent = 0;
m_rtNewStop = m_rtStop = 0;
m_rtStop = 10000i64 * m_pFile->m_p.tDuration;
POSITION pos = m_pFile->m_mps.GetHeadPosition();
while (pos) {
MediaProperies* pmp = m_pFile->m_mps.GetNext(pos);
CStringW name;
name.Format(L"Output %02u", pmp->stream);
if (!pmp->name.IsEmpty()) {
name += L" (" + CStringW(pmp->name) + L")";
}
CAtlArray mts;
CMediaType mt;
mt.SetSampleSize(max(pmp->maxPacketSize * 16/**/, 1));
if (pmp->mime == "video/x-pn-realvideo") {
mt.majortype = MEDIATYPE_Video;
mt.formattype = FORMAT_VideoInfo;
VIDEOINFOHEADER* pvih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER) + (ULONG)pmp->typeSpecData.GetCount());
memset(mt.Format(), 0, mt.FormatLength());
memcpy(pvih + 1, pmp->typeSpecData.GetData(), pmp->typeSpecData.GetCount());
rvinfo rvi = *(rvinfo*)pmp->typeSpecData.GetData();
rvi.bswap();
ASSERT(rvi.dwSize >= FIELD_OFFSET(rvinfo, morewh));
ASSERT(rvi.fcc1 == 'ODIV');
mt.subtype = FOURCCMap(rvi.fcc2);
if (rvi.fps > 0x10000) {
pvih->AvgTimePerFrame = REFERENCE_TIME(10000000i64 / ((float)rvi.fps / 0x10000));
}
pvih->dwBitRate = pmp->avgBitRate;
pvih->bmiHeader.biSize = sizeof(pvih->bmiHeader);
pvih->bmiHeader.biWidth = rvi.w;
pvih->bmiHeader.biHeight = rvi.h;
pvih->bmiHeader.biPlanes = 3;
pvih->bmiHeader.biBitCount = rvi.bpp;
pvih->bmiHeader.biCompression = rvi.fcc2;
pvih->bmiHeader.biSizeImage = rvi.w * rvi.h * 3 / 2;
mts.Add(mt);
BYTE* extra = pmp->typeSpecData.GetData();
DWORD extralen = (DWORD)pmp->typeSpecData.GetCount();
if (extralen > 26) {
extra += 26;
extralen -= 26;
VIDEOINFOHEADER* pvih2 = (VIDEOINFOHEADER*)mt.ReallocFormatBuffer(sizeof(VIDEOINFOHEADER) + extralen);
memcpy(pvih2 + 1, extra, extralen);
mts.InsertAt(0, mt);
}
if (pmp->width > 0 && pmp->height > 0) {
BITMAPINFOHEADER bmi = pvih->bmiHeader;
mt.formattype = FORMAT_VideoInfo2;
VIDEOINFOHEADER2* pvih2 = (VIDEOINFOHEADER2*)mt.ReallocFormatBuffer(sizeof(VIDEOINFOHEADER2) + (ULONG)pmp->typeSpecData.GetCount());
memset(mt.Format() + FIELD_OFFSET(VIDEOINFOHEADER2, dwInterlaceFlags), 0, mt.FormatLength() - FIELD_OFFSET(VIDEOINFOHEADER2, dwInterlaceFlags));
memcpy(pvih2 + 1, pmp->typeSpecData.GetData(), pmp->typeSpecData.GetCount());
pvih2->bmiHeader = bmi;
pvih2->bmiHeader.biWidth = (DWORD)pmp->width;
pvih2->bmiHeader.biHeight = (DWORD)pmp->height;
pvih2->dwPictAspectRatioX = rvi.w;
pvih2->dwPictAspectRatioY = rvi.h;
mts.InsertAt(0, mt);
}
} else if (pmp->mime == "audio/x-pn-realaudio") {
mt.majortype = MEDIATYPE_Audio;
mt.formattype = FORMAT_WaveFormatEx;
mt.bTemporalCompression = 1;
WAVEFORMATEX* pwfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX) + (ULONG)pmp->typeSpecData.GetCount());
memset(mt.Format(), 0, mt.FormatLength());
memcpy(pwfe + 1, pmp->typeSpecData.GetData(), pmp->typeSpecData.GetCount());
union {
DWORD fcc;
char fccstr[5];
};
fcc = 0;
fccstr[4] = 0;
BYTE* fmt = pmp->typeSpecData.GetData();
for (size_t i = 0; i < pmp->typeSpecData.GetCount() - 4; i++, fmt++) {
if (*(DWORD*)fmt == MAKEFOURCC('.' , 'r' , 'a' , 0xfd)) {
break;
}
}
BYTE* extra = nullptr;
WORD ver = fmt[4] << 8 | fmt[5];
if (ver == 3) {
rainfo3 rai3 = *(rainfo3*)fmt;
rai3.bswap();
fcc = '4_41';
pwfe->nChannels = 1;
pwfe->nSamplesPerSec = 8000;
pwfe->nAvgBytesPerSec = 1000;
pwfe->nBlockAlign = 2;
pwfe->wBitsPerSample = 16;
} else {
rainfo rai = *(rainfo*)fmt;
rai.bswap();
if (rai.version2 == 4) {
rainfo4 rai4 = *(rainfo4*)fmt;
rai4.bswap();
pwfe->nChannels = rai4.channels;
pwfe->wBitsPerSample = rai4.sample_size;
pwfe->nSamplesPerSec = rai4.sample_rate;
pwfe->nBlockAlign = rai4.frame_size;
BYTE* p = (BYTE*)((rainfo4*)fmt + 1);
int len = *p++;
p += len;
len = *p++;
ASSERT(len == 4);
if (len == 4) {
fcc = MAKEFOURCC(p[0], p[1], p[2], p[3]);
}
extra = p + len + 3;
} else if (rai.version2 == 5) {
rainfo5 rai5 = *(rainfo5*)fmt;
rai5.bswap();
pwfe->nChannels = rai5.channels;
pwfe->wBitsPerSample = rai5.sample_size;
pwfe->nSamplesPerSec = rai5.sample_rate;
pwfe->nBlockAlign = rai5.frame_size;
fcc = rai5.fourcc3;
extra = fmt + sizeof(rainfo5) + 4;
} else {
continue;
}
}
_strupr_s(fccstr);
mt.subtype = FOURCCMap(fcc);
bswap(fcc);
switch (fcc) {
case '14_4':
pwfe->wFormatTag = WAVE_FORMAT_14_4;
break;
case '28_8':
pwfe->wFormatTag = WAVE_FORMAT_28_8;
break;
case 'ATRC':
pwfe->wFormatTag = WAVE_FORMAT_ATRC;
break;
case 'COOK':
pwfe->wFormatTag = WAVE_FORMAT_COOK;
break;
case 'DNET':
pwfe->wFormatTag = WAVE_FORMAT_DNET;
break;
case 'SIPR':
pwfe->wFormatTag = WAVE_FORMAT_SIPR;
break;
case 'RAAC':
pwfe->wFormatTag = WAVE_FORMAT_RAAC;
break;
case 'RACP':
pwfe->wFormatTag = WAVE_FORMAT_RACP;
break;
}
if (pwfe->wFormatTag) {
mts.Add(mt);
if (fcc == 'DNET') {
mt.subtype = FOURCCMap(pwfe->wFormatTag = WAVE_FORMAT_DOLBY_AC3);
mts.InsertAt(0, mt);
} else if (fcc == 'RAAC' || fcc == 'RACP') {
mt.subtype = FOURCCMap(pwfe->wFormatTag = WAVE_FORMAT_AAC);
DWORD extralen = *(DWORD*)extra;
extra += 4;
::bswap(extralen);
ASSERT(*extra == 2); // always 2? why? what does it mean?
if (*extra == 2) {
extra++;
extralen--;
WAVEFORMATEX* pwfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + extralen);
pwfe->cbSize = extralen;
memcpy(pwfe + 1, extra, extralen);
} else {
WAVEFORMATEX* pwfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + 5);
pwfe->cbSize = MakeAACInitData((BYTE*)(pwfe + 1), 0, pwfe->nSamplesPerSec, pwfe->nChannels);
}
mts.InsertAt(0, mt);
}
}
} else if (pmp->mime == "logical-fileinfo") {
CAtlMap> lfi;
CStringA key, value;
BYTE* p = pmp->typeSpecData.GetData();
BYTE* end = p + pmp->typeSpecData.GetCount();
p += 8;
DWORD cnt = p <= end - 4 ? *(DWORD*)p : 0;
bswap(cnt);
p += 4;
if (cnt > 0xffff) { // different format?
p += 2;
cnt = p <= end - 4 ? *(DWORD*)p : 0;
bswap(cnt);
p += 4;
}
while (p < end - 4 && cnt-- > 0) {
BYTE* base = p;
DWORD len = *(DWORD*)p;
bswap(len);
p += 4;
if (base + len > end) {
break;
}
p++;
WORD keylen = *(WORD*)p;
bswap(keylen);
p += 2;
memcpy(key.GetBufferSetLength(keylen), p, keylen);
p += keylen;
p += 4;
WORD valuelen = *(WORD*)p;
bswap(valuelen);
p += 2;
memcpy(value.GetBufferSetLength(valuelen), p, valuelen);
p += valuelen;
ASSERT(p == base + len);
p = base + len;
lfi[key] = value;
}
POSITION pos = lfi.GetStartPosition();
while (pos) {
lfi.GetNextAssoc(pos, key, value);
int n = 0;
if (key.Find("CHAPTER") == 0 && key.Find("TIME") == key.GetLength() - 4
&& (n = strtol(key.Mid(7), nullptr, 10)) > 0) {
int h, m, s, ms;
char c;
if (7 != sscanf_s(value, "%d%c%d%c%d%c%d", &h, &c, 1, &m, &c, 1, &s, &c, 1, &ms)) {
continue;
}
key.Format("CHAPTER%02dNAME", n);
if (!lfi.Lookup(key, value) || value.IsEmpty()) {
value.Format("Chapter %d", n);
}
ChapAppend(
((((REFERENCE_TIME)h * 60 + m) * 60 + s) * 1000 + ms) * 10000,
CStringW(CString(value)));
}
}
}
if (mts.IsEmpty()) {
TRACE(_T("Unsupported RealMedia stream (%d): %s\n"), pmp->stream, CString(pmp->mime));
continue;
}
HRESULT hr2;
CAutoPtr pPinOut(DEBUG_NEW CRealMediaSplitterOutputPin(mts, name, this, this, &hr2));
if (SUCCEEDED(AddOutputPin((DWORD)pmp->stream, pPinOut))) {
if (!m_rtStop) {
m_pFile->m_p.tDuration = max(m_pFile->m_p.tDuration, pmp->tDuration);
}
}
}
pos = m_pFile->m_subs.GetHeadPosition();
for (DWORD stream = 0; pos; stream++) {
CRMFile::subtitle& s = m_pFile->m_subs.GetNext(pos);
CStringW name;
name.Format(L"Subtitle %02u", stream);
if (!s.name.IsEmpty()) {
name += L" (" + CStringW(CString(s.name)) + L")";
}
CMediaType mt;
mt.SetSampleSize(1);
mt.majortype = MEDIATYPE_Text;
CAtlArray mts;
mts.Add(mt);
HRESULT hr2;
CAutoPtr pPinOut(DEBUG_NEW CRealMediaSplitterOutputPin(mts, name, this, this, &hr2));
AddOutputPin((DWORD)~stream, pPinOut);
}
m_rtDuration = m_rtNewStop = m_rtStop = 10000i64 * m_pFile->m_p.tDuration;
SetProperty(L"TITL", CStringW(m_pFile->m_cd.title));
SetProperty(L"AUTH", CStringW(m_pFile->m_cd.author));
SetProperty(L"CPYR", CStringW(m_pFile->m_cd.copyright));
SetProperty(L"DESC", CStringW(m_pFile->m_cd.comment));
return !m_pOutputs.IsEmpty() ? S_OK : E_FAIL;
}
bool CRealMediaSplitterFilter::DemuxInit()
{
SetThreadName((DWORD) - 1, "CRealMediaSplitterFilter");
if (!m_pFile) {
return false;
}
// reindex if needed
if (m_pFile->m_irs.IsEmpty()) {
m_nOpenProgress = 0;
m_rtDuration = 0;
int stream = m_pFile->GetMasterStream();
UINT32 tLastStart = (UINT32) - 1;
UINT32 nPacket = 0;
POSITION pos = m_pFile->m_dcs.GetHeadPosition();
while (pos && !m_fAbort) {
DataChunk* pdc = m_pFile->m_dcs.GetNext(pos);
m_pFile->Seek(pdc->pos);
for (UINT32 i = 0; i < pdc->nPackets && !m_fAbort; i++, nPacket++) {
UINT64 filepos = m_pFile->GetPos();
HRESULT hr;
MediaPacketHeader mph;
if (S_OK != (hr = m_pFile->Read(mph, false))) {
break;
}
m_rtDuration = max((__int64)(10000i64 * mph.tStart), m_rtDuration);
if (mph.stream == stream && (mph.flags & MediaPacketHeader::PN_KEYFRAME_FLAG) && tLastStart != mph.tStart) {
CAutoPtr pir(DEBUG_NEW IndexRecord);
pir->tStart = mph.tStart;
pir->ptrFilePos = (UINT32)filepos;
pir->packet = nPacket;
m_pFile->m_irs.AddTail(pir);
tLastStart = mph.tStart;
}
m_nOpenProgress = m_pFile->GetPos() * 100 / m_pFile->GetLength();
DWORD cmd;
if (CheckRequest(&cmd)) {
if (cmd == CMD_EXIT) {
m_fAbort = true;
} else {
Reply(S_OK);
}
}
}
}
m_nOpenProgress = 100;
if (m_fAbort) {
m_pFile->m_irs.RemoveAll();
}
m_fAbort = false;
}
m_seekpos = nullptr;
m_seekpacket = 0;
m_seekfilepos = 0;
return true;
}
void CRealMediaSplitterFilter::DemuxSeek(REFERENCE_TIME rt)
{
if (rt <= 0) {
m_seekpos = m_pFile->m_dcs.GetHeadPosition();
m_seekpacket = 0;
m_seekfilepos = m_pFile->m_dcs.GetHead()->pos;
} else {
m_seekpos = nullptr;
POSITION pos = m_pFile->m_irs.GetTailPosition();
while (pos && !m_seekpos) {
IndexRecord* pir = m_pFile->m_irs.GetPrev(pos);
if (pir->tStart <= rt / 10000) {
m_seekpacket = pir->packet;
pos = m_pFile->m_dcs.GetTailPosition();
while (pos && !m_seekpos) {
POSITION tmp = pos;
DataChunk* pdc = m_pFile->m_dcs.GetPrev(pos);
if (pdc->pos <= pir->ptrFilePos) {
m_seekpos = tmp;
m_seekfilepos = pir->ptrFilePos;
POSITION pos = m_pFile->m_dcs.GetHeadPosition();
while (pos != m_seekpos) {
m_seekpacket -= m_pFile->m_dcs.GetNext(pos)->nPackets;
}
}
}
// search the closest keyframe to the seek time (commented out 'cause rm seems to index all of its keyframes...)
/*
if (m_seekpos)
{
DataChunk* pdc = m_pFile->m_dcs.GetAt(m_seekpos);
m_pFile->Seek(m_seekfilepos);
REFERENCE_TIME seektime = -1;
UINT32 seekstream = -1;
for (UINT32 i = m_seekpacket; i < pdc->nPackets; i++)
{
UINT64 filepos = m_pFile->GetPos();
MediaPacketHeader mph;
if (S_OK != m_pFile->Read(mph, false))
break;
if (seekstream == -1) seekstream = mph.stream;
if (seekstream != mph.stream) continue;
if (seektime == 10000i64*mph.tStart) continue;
if (rt < 10000i64*mph.tStart) break;
if ((mph.flags&MediaPacketHeader::PN_KEYFRAME_FLAG))
{
m_seekpacket = i;
m_seekfilepos = filepos;
seektime = 10000i64*mph.tStart;
}
}
}
*/
}
}
if (!m_seekpos) {
m_seekpos = m_pFile->m_dcs.GetHeadPosition();
m_seekpacket = 0;
m_seekfilepos = m_pFile->m_dcs.GetAt(m_seekpos)->pos;
}
}
}
bool CRealMediaSplitterFilter::DemuxLoop()
{
HRESULT hr = S_OK;
POSITION pos;
pos = m_pFile->m_subs.GetHeadPosition();
for (DWORD stream = 0; pos && SUCCEEDED(hr) && !CheckRequest(nullptr); stream++) {
CRMFile::subtitle& s = m_pFile->m_subs.GetNext(pos);
CAutoPtr p(DEBUG_NEW Packet);
p->TrackNumber = ~stream;
p->bSyncPoint = TRUE;
p->rtStart = 0;
p->rtStop = 1;
size_t count = (4 + 1) + (2 + 4 + (s.name.GetLength() + 1) * 2) + (2 + 4 + s.data.GetLength());
p->SetCount(count);
BYTE* ptr = p->GetData();
strcpy_s((char*)ptr, count, "GAB2");
ptr += 4 + 1;
count -= 4 + 1;
*(WORD*)ptr = 2;
ptr += 2;
count -= 2;
*(DWORD*)ptr = (s.name.GetLength() + 1) * 2;
ptr += 4;
count -= 4;
wcscpy_s((WCHAR*)ptr, count / 2, CStringW(s.name));
ptr += (s.name.GetLength() + 1) * 2;
*(WORD*)ptr = 4;
ptr += 2;
*(DWORD*)ptr = s.data.GetLength();
ptr += 4;
memcpy((char*)ptr, s.data, s.data.GetLength());
ptr += s.name.GetLength();
hr = DeliverPacket(p);
}
pos = m_seekpos;
while (pos && SUCCEEDED(hr) && !CheckRequest(nullptr)) {
DataChunk* pdc = m_pFile->m_dcs.GetNext(pos);
m_pFile->Seek(m_seekfilepos > 0 ? m_seekfilepos : pdc->pos);
for (UINT32 i = m_seekpacket; i < pdc->nPackets && SUCCEEDED(hr) && !CheckRequest(nullptr); i++) {
MediaPacketHeader mph;
if (S_OK != (hr = m_pFile->Read(mph))) {
break;
}
CAutoPtr p(DEBUG_NEW Packet);
p->TrackNumber = mph.stream;
p->bSyncPoint = !!(mph.flags & MediaPacketHeader::PN_KEYFRAME_FLAG);
p->rtStart = 10000i64 * (mph.tStart);
p->rtStop = p->rtStart + 1;
p->Copy(mph.pData);
hr = DeliverPacket(p);
}
m_seekpacket = 0;
m_seekfilepos = 0;
}
return true;
}
// IKeyFrameInfo
STDMETHODIMP CRealMediaSplitterFilter::GetKeyFrameCount(UINT& nKFs)
{
if (!m_pFile) {
return E_UNEXPECTED;
}
nKFs = m_pFile->m_irs.GetCount();
return S_OK;
}
STDMETHODIMP CRealMediaSplitterFilter::GetKeyFrames(const GUID* pFormat, REFERENCE_TIME* pKFs, UINT& nKFs)
{
CheckPointer(pFormat, E_POINTER);
CheckPointer(pKFs, E_POINTER);
if (!m_pFile) {
return E_UNEXPECTED;
}
if (*pFormat != TIME_FORMAT_MEDIA_TIME) {
return E_INVALIDARG;
}
UINT nKFsTmp = 0;
POSITION pos = m_pFile->m_irs.GetHeadPosition();
for (int i = 0; pos && nKFsTmp < nKFs; i++) {
pKFs[nKFsTmp++] = 10000i64 * m_pFile->m_irs.GetNext(pos)->tStart;
}
nKFs = nKFsTmp;
return S_OK;
}
//
// CRealMediaSplitterOutputPin
//
CRealMediaSplitterOutputPin::CRealMediaSplitterOutputPin(CAtlArray& mts, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
: CBaseSplitterOutputPin(mts, pName, pFilter, pLock, phr, 0, MAXPACKETS_RV)
{
}
CRealMediaSplitterOutputPin::~CRealMediaSplitterOutputPin()
{
}
HRESULT CRealMediaSplitterOutputPin::DeliverEndFlush()
{
{
CAutoLock cAutoLock(&m_csQueue);
m_segments.Clear();
}
return __super::DeliverEndFlush();
}
HRESULT CRealMediaSplitterOutputPin::DeliverSegments()
{
HRESULT hr;
if (m_segments.IsEmpty()) {
m_segments.Clear();
return S_OK;
}
CAutoPtr p(DEBUG_NEW Packet());
p->TrackNumber = (DWORD) - 1;
p->bDiscontinuity = m_segments.fDiscontinuity;
p->bSyncPoint = m_segments.fSyncPoint;
p->rtStart = m_segments.rtStart;
p->rtStop = m_segments.rtStart + 1;
DWORD len = 0, total = 0;
POSITION pos = m_segments.GetHeadPosition();
while (pos) {
segment* s = m_segments.GetNext(pos);
len = max(len, s->offset + s->data.GetCount());
total += s->data.GetCount();
}
ASSERT(len == total);
len += 1 + 2 * 4 * (!m_segments.fMerged ? m_segments.GetCount() : 1);
p->SetCount(len);
BYTE* pData = p->GetData();
*pData++ = m_segments.fMerged ? 0 : m_segments.GetCount() - 1;
if (m_segments.fMerged) {
*((DWORD*)pData) = 1;
pData += 4;
*((DWORD*)pData) = 0;
pData += 4;
} else {
pos = m_segments.GetHeadPosition();
while (pos) {
*((DWORD*)pData) = 1;
pData += 4;
*((DWORD*)pData) = m_segments.GetNext(pos)->offset;
pData += 4;
}
}
pos = m_segments.GetHeadPosition();
while (pos) {
segment* s = m_segments.GetNext(pos);
memcpy(pData + s->offset, s->data.GetData(), s->data.GetCount());
}
hr = __super::DeliverPacket(p);
m_segments.Clear();
return hr;
}
HRESULT CRealMediaSplitterOutputPin::DeliverPacket(CAutoPtr p)
{
HRESULT hr = S_OK;
ASSERT(p->rtStart < p->rtStop);
if (m_mt.subtype == MEDIASUBTYPE_WAVE_DOLBY_AC3) {
WORD* s = (WORD*)p->GetData();
WORD* e = s + p->GetCount() / 2;
while (s < e) {
bswap(*s++);
}
}
if (m_mt.subtype == MEDIASUBTYPE_RV10 || m_mt.subtype == MEDIASUBTYPE_RV20
|| m_mt.subtype == MEDIASUBTYPE_RV30 || m_mt.subtype == MEDIASUBTYPE_RV40
|| m_mt.subtype == MEDIASUBTYPE_RV41) {
CAutoLock cAutoLock(&m_csQueue);
int len = (int)p->GetCount();
BYTE* pIn = p->GetData();
BYTE* pInOrg = pIn;
if (m_segments.rtStart != p->rtStart) {
if (S_OK != (hr = DeliverSegments())) {
return hr;
}
}
if (!m_segments.fDiscontinuity && p->bDiscontinuity) {
m_segments.fDiscontinuity = true;
}
m_segments.fSyncPoint = !!p->bSyncPoint;
m_segments.rtStart = p->rtStart;
while (pIn - pInOrg < len) {
BYTE hdr = *pIn++;
DWORD packetlen = 0, packetoffset = 0;
if ((hdr & 0xc0) == 0x40) {
pIn++;
packetlen = len - (pIn - pInOrg);
} else {
if ((hdr & 0x40) == 0) {
pIn++; //BYTE subseq = (*pIn++)&0x7f;
}
#define GetWORD(var) \
var = (var << 8) | (*pIn++); \
var = (var << 8) | (*pIn++);
GetWORD(packetlen);
if (packetlen & 0x8000) {
m_segments.fMerged = true;
}
if ((packetlen & 0x4000) == 0) {
GetWORD(packetlen);
packetlen &= 0x3fffffff;
} else {
packetlen &= 0x3fff;
}
GetWORD(packetoffset);
if ((packetoffset & 0x4000) == 0) {
GetWORD(packetoffset);
packetoffset &= 0x3fffffff;
} else {
packetoffset &= 0x3fff;
}
#undef GetWORD
if ((hdr & 0xc0) == 0xc0) {
m_segments.rtStart = 10000i64 * packetoffset - m_rtStart, packetoffset = 0;
} else if ((hdr & 0xc0) == 0x80) {
packetoffset = packetlen - packetoffset;
}
pIn++; //BYTE seqnum = *pIn++;
}
int len2 = min(len - (pIn - pInOrg), int(packetlen - packetoffset));
CAutoPtr s(DEBUG_NEW segment);
s->offset = packetoffset;
s->data.SetCount(len2);
memcpy(s->data.GetData(), pIn, len2);
m_segments.AddTail(s);
pIn += len2;
if ((hdr & 0x80) || packetoffset + len2 >= packetlen) {
if (S_OK != (hr = DeliverSegments())) {
return hr;
}
}
}
} else if (m_mt.subtype == MEDIASUBTYPE_RAAC || m_mt.subtype == MEDIASUBTYPE_RACP
|| m_mt.subtype == MEDIASUBTYPE_AAC) {
BYTE* ptr = p->GetData() + 2;
CAtlList sizes;
int total = 0;
int remaining = (int)p->GetCount() - 2;
int expected = *(ptr - 1) >> 4;
while (total < remaining) {
int size = (ptr[0] << 8) | (ptr[1]);
sizes.AddTail(size);
total += size;
ptr += 2;
remaining -= 2;
expected--;
}
ASSERT(total == remaining);
ASSERT(expected == 0);
WAVEFORMATEX* wfe = (WAVEFORMATEX*)m_mt.pbFormat;
REFERENCE_TIME rtDur = 10240000000i64 / wfe->nSamplesPerSec * (wfe->cbSize > 2 ? 2 : 1);
REFERENCE_TIME rtStart = p->rtStart;
BOOL bDiscontinuity = p->bDiscontinuity;
POSITION pos = sizes.GetHeadPosition();
while (pos) {
WORD size = sizes.GetNext(pos);
CAutoPtr p(DEBUG_NEW Packet);
p->bDiscontinuity = bDiscontinuity;
p->bSyncPoint = true;
p->rtStart = rtStart;
p->rtStop = rtStart += rtDur;
p->SetData(ptr, size);
ptr += size;
bDiscontinuity = false;
if (S_OK != (hr = __super::DeliverPacket(p))) {
break;
}
}
} else {
hr = __super::DeliverPacket(p);
}
return hr;
}
//
// CRealMediaSourceFilter
//
CRealMediaSourceFilter::CRealMediaSourceFilter(LPUNKNOWN pUnk, HRESULT* phr)
: CRealMediaSplitterFilter(pUnk, phr)
{
m_clsid = __uuidof(this);
m_pInput.Free();
}
//
// CRMFile
//
CRMFile::CRMFile(IAsyncReader* pAsyncReader, HRESULT& hr)
: CBaseSplitterFile(pAsyncReader, hr)
{
if (FAILED(hr)) {
return;
}
hr = Init();
}
template
HRESULT CRMFile::Read(T& var)
{
HRESULT hr = ByteRead((BYTE*)&var, sizeof(var));
bswap(var);
return hr;
}
HRESULT CRMFile::Read(ChunkHdr& hdr)
{
memset(&hdr, 0, sizeof(hdr));
HRESULT hr;
if (S_OK != (hr = Read(hdr.object_id))
|| S_OK != (hr = Read(hdr.size))
|| S_OK != (hr = Read(hdr.object_version))) {
return hr;
}
return S_OK;
}
HRESULT CRMFile::Read(MediaPacketHeader& mph, bool fFull)
{
memset(&mph, 0, FIELD_OFFSET(MediaPacketHeader, pData));
mph.stream = (UINT16) - 1;
HRESULT hr;
UINT16 object_version;
if (S_OK != (hr = Read(object_version))) {
return hr;
}
if (object_version != 0 && object_version != 1) {
return S_OK;
}
UINT8 flags;
if (S_OK != (hr = Read(mph.len))
|| S_OK != (hr = Read(mph.stream))
|| S_OK != (hr = Read(mph.tStart))
|| S_OK != (hr = Read(mph.reserved))
|| S_OK != (hr = Read(flags))) {
return hr;
}
mph.flags = (MediaPacketHeader::flag_t)flags;
LONG len = mph.len;
len -= sizeof(object_version);
len -= FIELD_OFFSET(MediaPacketHeader, flags);
len -= sizeof(flags);
ASSERT(len >= 0);
len = max(len, 0);
if (fFull) {
mph.pData.SetCount(len);
if (mph.len > 0 && S_OK != (hr = ByteRead(mph.pData.GetData(), len))) {
return hr;
}
} else {
Seek(GetPos() + len);
}
return S_OK;
}
HRESULT CRMFile::Init()
{
Seek(0);
bool fFirstChunk = true;
HRESULT hr;
ChunkHdr hdr;
while (GetRemaining() && S_OK == (hr = Read(hdr))) {
__int64 pos = GetPos() - sizeof(hdr);
if (fFirstChunk && hdr.object_id != '.RMF') {
return E_FAIL;
}
fFirstChunk = false;
if (pos + hdr.size > GetLength() && hdr.object_id != 'DATA') { // truncated?
break;
}
if (hdr.object_id == 0x2E7261FD) { // '.ra+0xFD'
return E_FAIL;
}
if (hdr.object_version == 0) {
switch (hdr.object_id) {
case '.RMF':
if (S_OK != (hr = Read(m_fh.version))) {
return hr;
}
if (hdr.size == 0x10) {
WORD w = 0;
if (S_OK != (hr = Read(w))) {
return hr;
}
m_fh.nHeaders = w;
} else if (S_OK != (hr = Read(m_fh.nHeaders))) {
return hr;
}
break;
case 'CONT':
UINT16 slen;
if (S_OK != (hr = Read(slen))) {
return hr;
}
if (slen > 0 && S_OK != (hr = ByteRead((BYTE*)m_cd.title.GetBufferSetLength(slen), slen))) {
return hr;
}
if (S_OK != (hr = Read(slen))) {
return hr;
}
if (slen > 0 && S_OK != (hr = ByteRead((BYTE*)m_cd.author.GetBufferSetLength(slen), slen))) {
return hr;
}
if (S_OK != (hr = Read(slen))) {
return hr;
}
if (slen > 0 && S_OK != (hr = ByteRead((BYTE*)m_cd.copyright.GetBufferSetLength(slen), slen))) {
return hr;
}
if (S_OK != (hr = Read(slen))) {
return hr;
}
if (slen > 0 && S_OK != (hr = ByteRead((BYTE*)m_cd.comment.GetBufferSetLength(slen), slen))) {
return hr;
}
break;
case 'PROP':
if (S_OK != (hr = Read(m_p.maxBitRate))) {
return hr;
}
if (S_OK != (hr = Read(m_p.avgBitRate))) {
return hr;
}
if (S_OK != (hr = Read(m_p.maxPacketSize))) {
return hr;
}
if (S_OK != (hr = Read(m_p.avgPacketSize))) {
return hr;
}
if (S_OK != (hr = Read(m_p.nPackets))) {
return hr;
}
if (S_OK != (hr = Read(m_p.tDuration))) {
return hr;
}
if (S_OK != (hr = Read(m_p.tPreroll))) {
return hr;
}
if (S_OK != (hr = Read(m_p.ptrIndex))) {
return hr;
}
if (S_OK != (hr = Read(m_p.ptrData))) {
return hr;
}
if (S_OK != (hr = Read(m_p.nStreams))) {
return hr;
}
UINT16 flags;
if (S_OK != (hr = Read(flags))) {
return hr;
}
m_p.flags = (Properies::flags_t)flags;
break;
case 'MDPR': {
CAutoPtr mp(DEBUG_NEW MediaProperies);
if (S_OK != (hr = Read(mp->stream))) {
return hr;
}
if (S_OK != (hr = Read(mp->maxBitRate))) {
return hr;
}
if (S_OK != (hr = Read(mp->avgBitRate))) {
return hr;
}
if (S_OK != (hr = Read(mp->maxPacketSize))) {
return hr;
}
if (S_OK != (hr = Read(mp->avgPacketSize))) {
return hr;
}
if (S_OK != (hr = Read(mp->tStart))) {
return hr;
}
if (S_OK != (hr = Read(mp->tPreroll))) {
return hr;
}
if (S_OK != (hr = Read(mp->tDuration))) {
return hr;
}
UINT8 len;
if (S_OK != (hr = Read(len))) {
return hr;
}
if (len > 0 && S_OK != (hr = ByteRead((BYTE*)mp->name.GetBufferSetLength(len), len))) {
return hr;
}
if (S_OK != (hr = Read(len))) {
return hr;
}
if (len > 0 && S_OK != (hr = ByteRead((BYTE*)mp->mime.GetBufferSetLength(len), len))) {
return hr;
}
UINT32 tsdlen;
if (S_OK != (hr = Read(tsdlen))) {
return hr;
}
mp->typeSpecData.SetCount(tsdlen);
if (tsdlen > 0 && S_OK != (hr = ByteRead(mp->typeSpecData.GetData(), tsdlen))) {
return hr;
}
mp->width = mp->height = 0;
mp->interlaced = mp->top_field_first = false;
m_mps.AddTail(mp);
break;
}
case 'DATA': {
CAutoPtr dc(DEBUG_NEW DataChunk);
if (S_OK != (hr = Read(dc->nPackets))) {
return hr;
}
if (S_OK != (hr = Read(dc->ptrNext))) {
return hr;
}
dc->pos = GetPos();
m_dcs.AddTail(dc);
GetDimensions();
break;
}
case 'INDX': {
IndexChunkHeader ich;
if (S_OK != (hr = Read(ich.nIndices))) {
return hr;
}
if (S_OK != (hr = Read(ich.stream))) {
return hr;
}
if (S_OK != (hr = Read(ich.ptrNext))) {
return hr;
}
int stream = GetMasterStream();
while (ich.nIndices-- > 0) {
UINT16 object_version;
if (S_OK != (hr = Read(object_version))) {
return hr;
}
if (object_version == 0) {
CAutoPtr ir(DEBUG_NEW IndexRecord);
if (S_OK != (hr = Read(ir->tStart))) {
return hr;
}
if (S_OK != (hr = Read(ir->ptrFilePos))) {
return hr;
}
if (S_OK != (hr = Read(ir->packet))) {
return hr;
}
if (ich.stream == stream) {
m_irs.AddTail(ir);
}
}
}
break;
}
case '.SUB':
if (hdr.size > sizeof(hdr)) {
int size = hdr.size - sizeof(hdr);
CAutoVectorPtr buff;
if (!buff.Allocate(size)) {
return E_OUTOFMEMORY;
}
char* p = buff;
if (S_OK != (hr = ByteRead((BYTE*)p, size))) {
return hr;
}
for (char* end = p + size; p < end;) {
subtitle s;
s.name = p;
p += s.name.GetLength() + 1;
CStringA len(p);
p += len.GetLength() + 1;
s.data = CStringA(p, strtol(len, nullptr, 10));
p += s.data.GetLength();
m_subs.AddTail(s);
}
}
break;
}
}
if (hdr.object_id == 'CONT' && BitRead(32, true) == 'DATA') {
hdr.size = GetPos() - pos;
}
ASSERT(hdr.object_id == 'DATA'
|| GetPos() == pos + hdr.size
|| GetPos() == pos + sizeof(hdr));
pos += hdr.size;
if (pos > GetPos()) {
Seek(pos);
}
}
return S_OK;
}
#define GetBits(n) GetBits2(n, p, bit_offset, bit_buffer)
unsigned int GetBits2(int n, unsigned char*& p, unsigned int& bit_offset, unsigned int& bit_buffer)
{
unsigned int ret = ((unsigned int)bit_buffer >> (32 - (n)));
bit_offset += n;
bit_buffer <<= n;
if (bit_offset > (32 - 16)) {
p += bit_offset >> 3;
bit_offset &= 7;
bit_buffer = (unsigned int)p[0] << 24;
bit_buffer |= (unsigned int)p[1] << 16;
bit_buffer |= (unsigned int)p[2] << 8;
bit_buffer |= (unsigned int)p[3];
bit_buffer <<= bit_offset;
}
return ret;
}
void GetDimensions(unsigned char* p, unsigned int* wi, unsigned int* hi)
{
unsigned int w, h, c;
const unsigned int cw[8] = {160, 176, 240, 320, 352, 640, 704, 0};
const unsigned int ch1[8] = {120, 132, 144, 240, 288, 480, 0, 0};
const unsigned int ch2[4] = {180, 360, 576, 0};
unsigned int bit_offset = 0;
unsigned int bit_buffer = *(unsigned int*)p;
bswap(bit_buffer);
GetBits(13);
GetBits(13);
w = cw[GetBits(3)];
if (w == 0) {
do {
c = GetBits(8);
w += (c << 2);
} while (c == 255);
}
c = GetBits(3);
h = ch1[c];
if (h == 0) {
c = ((c << 1) | GetBits(1)) & 3;
h = ch2[c];
if (h == 0) {
do {
c = GetBits(8);
h += (c << 2);
} while (c == 255);
}
}
*wi = w;
*hi = h;
}
void GetDimensions_X10(unsigned char* p, unsigned int* wi, unsigned int* hi,
bool* interlaced, bool* top_field_first, bool* repeat_field)
{
unsigned int w, h, c;
const unsigned int cw[8] = {160, 176, 240, 320, 352, 640, 704, 0};
const unsigned int ch1[8] = {120, 132, 144, 240, 288, 480, 0, 0};
const unsigned int ch2[4] = {180, 360, 576, 0};
unsigned int bit_offset = 0;
unsigned int bit_buffer = *(unsigned int*)p;
bswap(bit_buffer);
GetBits(9);
*interlaced = false;
*top_field_first = false;
*repeat_field = false;
c = GetBits(1);
if (c) {
c = GetBits(1);
if (c) {
*interlaced = true;
}
c = GetBits(1);
if (c) {
*top_field_first = true;
}
c = GetBits(1);
if (c) {
*repeat_field = true;
}
c = GetBits(1);
c = GetBits(1);
if (c) {
GetBits(2);
}
}
GetBits(16);
w = cw[GetBits(3)];
if (w == 0) {
do {
c = GetBits(8);
w += (c << 2);
} while (c == 255);
}
c = GetBits(3);
h = ch1[c];
if (h == 0) {
c = ((c << 1) | GetBits(1)) & 3;
h = ch2[c];
if (h == 0) {
do {
c = GetBits(8);
h += (c << 2);
} while (c == 255);
}
}
*wi = w;
*hi = h;
}
void CRMFile::GetDimensions()
{
POSITION pos = m_mps.GetHeadPosition();
while (pos) {
UINT64 filepos = GetPos();
MediaProperies* pmp = m_mps.GetNext(pos);
if (pmp->mime == "video/x-pn-realvideo") {
pmp->width = pmp->height = 0;
rvinfo rvi = *(rvinfo*)pmp->typeSpecData.GetData();
rvi.bswap();
if (rvi.fcc2 != '04VR' && rvi.fcc2 != '14VR') {
continue;
}
MediaPacketHeader mph;
while (S_OK == Read(mph)) {
if (mph.stream != pmp->stream || mph.len == 0
|| !(mph.flags & MediaPacketHeader::PN_KEYFRAME_FLAG)) {
continue;
}
BYTE* p = mph.pData.GetData();
BYTE* p0 = p;
int len = (int)mph.pData.GetCount();
BYTE hdr = *p++;
DWORD packetlen = 0, packetoffset = 0;
if ((hdr & 0xc0) == 0x40) {
packetlen = len - (++p - p0);
} else {
if ((hdr & 0x40) == 0) {
p++;
}
#define GetWORD(var) \
var = (var << 8) | (*p++); \
var = (var << 8) | (*p++);
GetWORD(packetlen);
if ((packetlen & 0x4000) == 0) {
GetWORD(packetlen);
packetlen &= 0x3fffffff;
} else {
packetlen &= 0x3fff;
}
GetWORD(packetoffset);
if ((packetoffset & 0x4000) == 0) {
GetWORD(packetoffset);
packetoffset &= 0x3fffffff;
} else {
packetoffset &= 0x3fff;
}
#undef GetWORD
if ((hdr & 0xc0) == 0xc0) {
packetoffset = 0;
} else if ((hdr & 0xc0) == 0x80) {
packetoffset = packetlen - packetoffset;
}
p++;
}
len = min(len - (p - p0), int(packetlen - packetoffset));
if (len > 0) {
bool repeat_field;
if (rvi.fcc2 == '14VR') {
::GetDimensions_X10(p, &pmp->width, &pmp->height, &pmp->interlaced, &pmp->top_field_first, &repeat_field);
} else {
::GetDimensions(p, &pmp->width, &pmp->height);
}
if (rvi.w == pmp->width && rvi.h == pmp->height) {
pmp->width = pmp->height = 0;
}
break;
}
}
}
Seek(filepos);
}
}
int CRMFile::GetMasterStream()
{
int s1 = -1, s2 = -1;
POSITION pos = m_mps.GetHeadPosition();
while (pos) {
MediaProperies* pmp = m_mps.GetNext(pos);
if (pmp->mime == "video/x-pn-realvideo") {
s1 = pmp->stream;
break;
} else if (pmp->mime == "audio/x-pn-realaudio" && s2 == -1) {
s2 = pmp->stream;
}
}
if (s1 == -1) {
s1 = s2;
}
return s1;
}
////////////////////////////
//
// CRealVideoDecoder
//
CRealVideoDecoder::CRealVideoDecoder(LPUNKNOWN lpunk, HRESULT* phr)
: CBaseVideoFilter(NAME("CRealVideoDecoder"), lpunk, phr, __uuidof(this))
, m_hDrvDll(nullptr)
, m_dwCookie(0)
, m_lastBuffSizeDim(0)
{
}
CRealVideoDecoder::~CRealVideoDecoder()
{
if (m_hDrvDll) {
FreeLibrary(m_hDrvDll);
}
}
HRESULT CRealVideoDecoder::InitRV(const CMediaType* pmt)
{
FreeRV();
HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
rvinfo rvi = *(rvinfo*)(pmt->Format() + (pmt->formattype == FORMAT_VideoInfo ? sizeof(VIDEOINFOHEADER) : sizeof(VIDEOINFOHEADER2)));
rvi.bswap();
#pragma pack(push, 1)
struct {
WORD unk1, w, h, unk3;
DWORD unk2, subformat, unk5, format;
} i =
{11, rvi.w, rvi.h, 0, 0, rvi.type1, 1, rvi.type2};
#pragma pack(pop)
if (FAILED(hr = RVInit(&i, &m_dwCookie))) {
return hr;
}
if (rvi.fcc2 <= '03VR' && rvi.type2 >= 0x20200002) {
int nWidthHeight = (1 + ((rvi.type1 >> 16) & 7));
UINT32* pWH = DEBUG_NEW UINT32[nWidthHeight * 2];
pWH[0] = rvi.w;
pWH[1] = rvi.h;
for (int j = 2; j < nWidthHeight * 2; j++) {
pWH[j] = rvi.morewh[j - 2] * 4;
}
#pragma pack(push, 1)
struct {
UINT32 data1;
UINT32 data2;
UINT32* dimensions;
} cmsg_data =
{0x24, nWidthHeight, pWH};
#pragma pack(pop)
hr = RVCustomMessage(&cmsg_data, m_dwCookie);
delete [] pWH;
}
return hr;
}
void CRealVideoDecoder::FreeRV()
{
if (m_dwCookie) {
RVFree(m_dwCookie);
m_dwCookie = 0;
}
}
HRESULT CRealVideoDecoder::Transform(IMediaSample* pIn)
{
CAutoLock cAutoLock(&m_csReceive);
HRESULT hr;
BYTE* pDataIn = nullptr;
if (FAILED(hr = pIn->GetPointer(&pDataIn))) {
return hr;
}
long len = pIn->GetActualDataLength();
if (len <= 0) {
return S_OK; // nothing to do
}
REFERENCE_TIME rtStart, rtStop;
pIn->GetTime(&rtStart, &rtStop);
rtStart += m_tStart;
int offset = 1 + ((*pDataIn) + 1) * 8;
#pragma pack(push, 1)
struct {
DWORD len, unk1, chunks;
DWORD* extra;
DWORD unk2, timestamp;
} transform_in =
{len - offset, 0, *pDataIn, (DWORD*)(pDataIn + 1), 0, (DWORD)(rtStart / 10000)};
struct {
DWORD unk1, unk2, timestamp, w, h;
} transform_out =
{0, 0, 0, 0, 0};
#pragma pack(pop)
pDataIn += offset;
if (m_fDropFrames && m_timestamp + 1 == transform_in.timestamp) {
m_timestamp = transform_in.timestamp;
return S_OK;
}
unsigned int tmp1, tmp2;
bool interlaced = false, tmp3, tmp4;
::GetDimensions_X10(pDataIn, &tmp1, &tmp2, &interlaced, &tmp3, &tmp4);
int size = tmp1 * tmp2;
if (m_lastBuffSizeDim < size) {
_aligned_free(m_pI420);
_aligned_free(m_pI420Tmp);
m_lastBuffSizeDim = size;
TRACE(_T("resize out put buff %d") , size);
m_pI420 = static_cast(_aligned_malloc(size * 3 / 2, 16));
if (m_pI420) {
TRACE(_T(" m_pI420.Allocated 1"));
memset(m_pI420, 0, size);
TRACE(_T(" m_pI420.Allocated 2"));
memset(m_pI420 + size, 0x80, size / 2);
TRACE(_T(" m_pI420.Allocated 3"));
} else {
TRACE(_T(" m_pI420.Allocate fail %d") , size * 3 / 2);
return S_OK;
}
m_pI420Tmp = static_cast(_aligned_malloc(size * 3 / 2, 16));
if (m_pI420Tmp) {
TRACE(_T(" m_pI420Tmp.Allocated 1"));
memset(m_pI420Tmp, 0, size);
TRACE(_T(" m_pI420Tmp.Allocated 2"));
memset(m_pI420Tmp + size, 0x80, size / 2);
TRACE(_T(" m_pI420Tmp.Allocated 3"));
} else {
TRACE(_T(" m_pI420Tmp.Allocate fail %d") , size * 3 / 2);
return S_OK;
}
}
hr = RVTransform(pDataIn, (BYTE*)m_pI420, &transform_in, &transform_out, m_dwCookie);
m_timestamp = transform_in.timestamp;
if (FAILED(hr)) {
TRACE(_T("RV returned an error code!!!\n"));
ASSERT(!(transform_out.unk1 & 1)); // error allowed when the "render" flag is not set
// return hr;
}
if (pIn->IsPreroll() == S_OK || rtStart < 0 || !(transform_out.unk1 & 1)) {
return S_OK;
}
CComPtr pOut;
BYTE* pDataOut = nullptr;
if (/*FAILED(hr = GetDeliveryBuffer(transform_out.w, transform_out.h, &pOut)) // TODO
&& */ FAILED(hr = GetDeliveryBuffer(m_w, m_h, &pOut))
|| FAILED(hr = pOut->GetPointer(&pDataOut))) {
return hr;
}
BYTE* pI420[3] = {m_pI420, m_pI420Tmp, nullptr};
if (interlaced) {
int iSize = m_w * m_h;
DeinterlaceBlend(pI420[1], pI420[0], m_w, m_h, m_w, m_w);
DeinterlaceBlend(pI420[1] + iSize, pI420[0] + iSize, m_w / 2, m_h / 2, m_w / 2, m_w / 2);
DeinterlaceBlend(pI420[1] + iSize * 5 / 4, pI420[0] + iSize * 5 / 4, m_w / 2, m_h / 2, m_w / 2, m_w / 2);
pI420[2] = pI420[1], pI420[1] = pI420[0], pI420[0] = pI420[2];
}
if (transform_out.w != m_w || transform_out.h != m_h) {
Resize(pI420[0], transform_out.w, transform_out.h, pI420[1], m_w, m_h);
// only one of these can be true, and when it happens the result image must be in the tmp buffer
if (transform_out.w == m_w || transform_out.h == m_h) {
pI420[2] = pI420[1], pI420[1] = pI420[0], pI420[0] = pI420[2];
}
}
rtStart = 10000i64 * transform_out.timestamp - m_tStart;
rtStop = rtStart + 1;
pOut->SetTime(&rtStart, /*nullptr*/&rtStop);
pOut->SetDiscontinuity(pIn->IsDiscontinuity() == S_OK);
CopyBuffer(pDataOut, pI420[0], m_w, m_h, m_w, MEDIASUBTYPE_I420);
DbgLog((LOG_TRACE, 0, _T("V: rtStart=%I64d, rtStop=%I64d, disc=%d, sync=%d"),
rtStart, rtStop, pOut->IsDiscontinuity() == S_OK, pOut->IsSyncPoint() == S_OK));
return m_pOutput->Deliver(pOut);
}
void CRealVideoDecoder::Resize(BYTE* pIn, DWORD wi, DWORD hi, BYTE* pOut, DWORD wo, DWORD ho)
{
int si = wi * hi, so = wo * ho;
ASSERT(((si * so) & 3) == 0);
if (wi < wo) {
ResizeWidth(pIn, wi, hi, pOut, wo, ho);
ResizeWidth(pIn + si, wi / 2, hi / 2, pOut + so, wo / 2, ho / 2);
ResizeWidth(pIn + si + si / 4, wi / 2, hi / 2, pOut + so + so / 4, wo / 2, ho / 2);
if (hi == ho) {
return;
}
ResizeHeight(pOut, wo, hi, pIn, wo, ho);
ResizeHeight(pOut + so, wo / 2, hi / 2, pIn + so, wo / 2, ho / 2);
ResizeHeight(pOut + so + so / 4, wo / 2, hi / 2, pIn + so + so / 4, wo / 2, ho / 2);
} else if (hi < ho) {
ResizeHeight(pIn, wi, hi, pOut, wo, ho);
ResizeHeight(pIn + si, wi / 2, hi / 2, pOut + so, wo / 2, ho / 2);
ResizeHeight(pIn + si + si / 4, wi / 2, hi / 2, pOut + so + so / 4, wo / 2, ho / 2);
if (wi == wo) {
return;
}
ASSERT(0); // this is uncreachable code, but anyway... looks nice being so symmetric
ResizeWidth(pOut, wi, ho, pIn, wo, ho);
ResizeWidth(pOut + so, wi / 2, ho / 2, pIn + so, wo / 2, ho / 2);
ResizeWidth(pOut + so + so / 4, wi / 2, ho / 2, pIn + so + so / 4, wo / 2, ho / 2);
}
}
void CRealVideoDecoder::ResizeWidth(BYTE* pIn, DWORD wi, DWORD hi, BYTE* pOut, DWORD wo, DWORD ho)
{
for (DWORD y = 0; y < hi; y++, pIn += wi, pOut += wo) {
if (wi == wo) {
memcpy(pOut, pIn, wo);
} else {
ResizeRow(pIn, wi, 1, pOut, wo, 1);
}
}
}
void CRealVideoDecoder::ResizeHeight(BYTE* pIn, DWORD wi, DWORD hi, BYTE* pOut, DWORD wo, DWORD ho)
{
if (hi == ho) {
memcpy(pOut, pIn, wo * ho);
} else {
for (DWORD x = 0; x < wo; x++, pIn++, pOut++) {
ResizeRow(pIn, hi, wo, pOut, ho, wo);
}
}
}
void CRealVideoDecoder::ResizeRow(BYTE* pIn, DWORD wi, DWORD dpi, BYTE* pOut, DWORD wo, DWORD dpo)
{
ASSERT(wi < wo);
if (dpo == 1) {
for (DWORD i = 0, j = 0, dj = (wi << 16) / wo; i < wo - 1; i++, pOut++, j += dj)
// pOut[i] = pIn[j>>16];
{
BYTE* p = &pIn[j >> 16];
DWORD jf = j & 0xffff;
*pOut = ((p[0] * (0xffff - jf) + p[1] * jf) + 0x7fff) >> 16;
}
*pOut = pIn[wi - 1];
} else {
for (DWORD i = 0, j = 0, dj = (wi << 16) / wo; i < wo - 1; i++, pOut += dpo, j += dj)
// *pOut = pIn[dpi*(j>>16)];
{
BYTE* p = &pIn[dpi * (j >> 16)];
DWORD jf = j & 0xffff;
*pOut = ((p[0] * (0xffff - jf) + p[dpi] * jf) + 0x7fff) >> 16;
}
*pOut = pIn[dpi * (wi - 1)];
}
}
HRESULT CRealVideoDecoder::CheckInputType(const CMediaType* mtIn)
{
if (mtIn->majortype != MEDIATYPE_Video
|| mtIn->subtype != MEDIASUBTYPE_RV20
&& mtIn->subtype != MEDIASUBTYPE_RV30
&& mtIn->subtype != MEDIASUBTYPE_RV40
&& mtIn->subtype != MEDIASUBTYPE_RV41) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
if (mtIn->formattype == FORMAT_VideoInfo2) {
VIDEOINFOHEADER2* vih2 = (VIDEOINFOHEADER2*)mtIn->Format();
if (vih2->dwPictAspectRatioX < (DWORD)vih2->bmiHeader.biWidth
|| vih2->dwPictAspectRatioY < (DWORD)vih2->bmiHeader.biHeight) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
}
if (!m_pInput->IsConnected()) {
if (m_hDrvDll) {
FreeLibrary(m_hDrvDll);
m_hDrvDll = nullptr;
}
CAtlList paths;
CString olddll, newdll, oldpath, newpath;
olddll.Format(_T("drv%c3260.dll"), (TCHAR)((mtIn->subtype.Data1 >> 16) & 0xff));
newdll =
mtIn->subtype == FOURCCMap('14VR') ? _T("drvi.dll") :
mtIn->subtype == FOURCCMap('02VR') ? _T("drv2.dll") :
_T("drvc.dll");
CRegKey key;
TCHAR buff[MAX_PATH];
ULONG len = sizeof(buff);
if (ERROR_SUCCESS == key.Open(HKEY_CLASSES_ROOT, _T("Software\\RealNetworks\\Preferences\\DT_Codecs"), KEY_READ)
&& ERROR_SUCCESS == key.QueryStringValue(nullptr, buff, &len) && _tcslen(buff) > 0) {
oldpath = buff;
TCHAR c = oldpath[oldpath.GetLength() - 1];
if (c != '\\' && c != '/') {
oldpath += '\\';
}
key.Close();
}
len = sizeof(buff);
if (ERROR_SUCCESS == key.Open(HKEY_CLASSES_ROOT, _T("Helix\\HelixSDK\\10.0\\Preferences\\DT_Codecs"), KEY_READ)
&& ERROR_SUCCESS == key.QueryStringValue(nullptr, buff, &len) && _tcslen(buff) > 0) {
newpath = buff;
TCHAR c = newpath[newpath.GetLength() - 1];
if (c != '\\' && c != '/') {
newpath += '\\';
}
key.Close();
}
if (!newpath.IsEmpty()) {
paths.AddTail(newpath + newdll);
}
if (!oldpath.IsEmpty()) {
paths.AddTail(oldpath + newdll);
}
paths.AddTail(newdll); // default dll paths
if (!newpath.IsEmpty()) {
paths.AddTail(newpath + olddll);
}
if (!oldpath.IsEmpty()) {
paths.AddTail(oldpath + olddll);
}
paths.AddTail(olddll); // default dll paths
POSITION pos = paths.GetHeadPosition();
do {
if (pos) {
m_hDrvDll = LoadLibrary(paths.GetNext(pos));
}
} while (pos && !m_hDrvDll);
if (m_hDrvDll) {
RVCustomMessage = (PRVCustomMessage)GetProcAddress(m_hDrvDll, "RV20toYUV420CustomMessage");
RVFree = (PRVFree)GetProcAddress(m_hDrvDll, "RV20toYUV420Free");
RVHiveMessage = (PRVHiveMessage)GetProcAddress(m_hDrvDll, "RV20toYUV420HiveMessage");
RVInit = (PRVInit)GetProcAddress(m_hDrvDll, "RV20toYUV420Init");
RVTransform = (PRVTransform)GetProcAddress(m_hDrvDll, "RV20toYUV420Transform");
if (!RVCustomMessage) {
RVCustomMessage = (PRVCustomMessage)GetProcAddress(m_hDrvDll, "RV40toYUV420CustomMessage");
}
if (!RVFree) {
RVFree = (PRVFree)GetProcAddress(m_hDrvDll, "RV40toYUV420Free");
}
if (!RVHiveMessage) {
RVHiveMessage = (PRVHiveMessage)GetProcAddress(m_hDrvDll, "RV40toYUV420HiveMessage");
}
if (!RVInit) {
RVInit = (PRVInit)GetProcAddress(m_hDrvDll, "RV40toYUV420Init");
}
if (!RVTransform) {
RVTransform = (PRVTransform)GetProcAddress(m_hDrvDll, "RV40toYUV420Transform");
}
}
if (!m_hDrvDll || !RVCustomMessage
|| !RVFree || !RVHiveMessage
|| !RVInit || !RVTransform) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
if (FAILED(InitRV(mtIn))) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
}
return S_OK;
}
HRESULT CRealVideoDecoder::CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut)
{
if (m_pOutput && m_pOutput->IsConnected()) {
BITMAPINFOHEADER bih1, bih2;
if (ExtractBIH(mtOut, &bih1) && ExtractBIH(&m_pOutput->CurrentMediaType(), &bih2)
&& abs(bih1.biHeight) != abs(bih2.biHeight)) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
}
return __super::CheckTransform(mtIn, mtOut);
}
HRESULT CRealVideoDecoder::StartStreaming()
{
const CMediaType& mt = m_pInput->CurrentMediaType();
if (FAILED(InitRV(&mt))) {
return E_FAIL;
}
int size = m_w * m_h;
m_lastBuffSizeDim = size;
m_pI420 = static_cast(_aligned_malloc(size * 3 / 2, 16));
memset(m_pI420, 0, size);
memset(m_pI420 + size, 0x80, size / 2);
m_pI420Tmp = static_cast(_aligned_malloc(size * 3 / 2, 16));
memset(m_pI420Tmp, 0, size);
memset(m_pI420Tmp + size, 0x80, size / 2);
return __super::StartStreaming();
}
HRESULT CRealVideoDecoder::StopStreaming()
{
_aligned_free(m_pI420);
_aligned_free(m_pI420Tmp);
FreeRV();
return __super::StopStreaming();
}
HRESULT CRealVideoDecoder::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
CAutoLock cAutoLock(&m_csReceive);
m_timestamp = (DWORD)~0;
m_fDropFrames = false;
DWORD tmp[2] = {20, 0};
RVHiveMessage(tmp, m_dwCookie);
m_tStart = tStart;
return __super::NewSegment(tStart, tStop, dRate);
}
HRESULT CRealVideoDecoder::AlterQuality(Quality q)
{
if (q.Late > 500 * 10000i64) {
m_fDropFrames = true;
}
if (q.Late <= 0) {
m_fDropFrames = false;
}
// TRACE(_T("CRealVideoDecoder::AlterQuality: Type=%d, Proportion=%d, Late=%I64d, TimeStamp=%I64d\n"), q.Type, q.Proportion, q.Late, q.TimeStamp);
return E_NOTIMPL;
}
/////////////////////////
//
// CRealAudioDecoder
//
CRealAudioDecoder::CRealAudioDecoder(LPUNKNOWN lpunk, HRESULT* phr)
: CTransformFilter(NAME("CRealAudioDecoder"), lpunk, __uuidof(this))
, m_hDrvDll(nullptr)
, m_dwCookie(0)
{
if (phr) {
*phr = S_OK;
}
}
CRealAudioDecoder::~CRealAudioDecoder()
{
// FreeRA();
if (m_hDrvDll) {
FreeLibrary(m_hDrvDll);
}
}
HRESULT CRealAudioDecoder::InitRA(const CMediaType* pmt)
{
FreeRA();
HRESULT hr;
if (RAOpenCodec2 && FAILED(hr = RAOpenCodec2(&m_dwCookie, m_dllpath))
|| RAOpenCodec && FAILED(hr = RAOpenCodec(&m_dwCookie))) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
WAVEFORMATEX* pwfe = (WAVEFORMATEX*)pmt->Format();
// someone might be doing cbSize = sizeof(WAVEFORMATEX), chances of
// cbSize being really sizeof(WAVEFORMATEX) is less than this,
// especially with our rm splitter ;)
DWORD cbSize = pwfe->cbSize;
if (cbSize == sizeof(WAVEFORMATEX)) {
ASSERT(0);
cbSize = 0;
}
WORD wBitsPerSample = pwfe->wBitsPerSample;
if (!wBitsPerSample) {
wBitsPerSample = 16;
}
#pragma pack(push, 1)
struct {
DWORD freq;
WORD bpsample, channels, quality;
DWORD bpframe, packetsize, extralen;
void* extra;
} initdata = {
pwfe->nSamplesPerSec, wBitsPerSample, pwfe->nChannels, 100,
0, 0, 0, nullptr
};
#pragma pack(pop)
CAutoVectorPtr pBuff;
if (pmt->subtype == MEDIASUBTYPE_AAC) {
pBuff.Allocate(cbSize + 1);
pBuff[0] = 0x02;
memcpy(pBuff + 1, pwfe + 1, cbSize);
initdata.extralen = cbSize + 1;
initdata.extra = pBuff;
} else {
if (pmt->FormatLength() <= sizeof(WAVEFORMATEX) + cbSize) { // must have type_specific_data appended
return VFW_E_TYPE_NOT_ACCEPTED;
}
BYTE* fmt = pmt->Format() + sizeof(WAVEFORMATEX) + cbSize;
for (int i = 0, len = pmt->FormatLength() - (sizeof(WAVEFORMATEX) + cbSize); i < len - 4; i++, fmt++) {
if (fmt[0] == '.' || fmt[1] == 'r' || fmt[2] == 'a') {
break;
}
}
m_rai = *(rainfo*)fmt;
m_rai.bswap();
BYTE* p;
if (m_rai.version2 == 4) {
p = (BYTE*)((rainfo4*)fmt + 1);
int len = *p++;
p += len;
len = *p++;
p += len;
ASSERT(len == 4);
} else if (m_rai.version2 == 5) {
p = (BYTE*)((rainfo5*)fmt + 1);
} else {
return VFW_E_TYPE_NOT_ACCEPTED;
}
p += 3;
if (m_rai.version2 == 5) {
p++;
}
initdata.bpframe = m_rai.sub_packet_size;
initdata.packetsize = m_rai.coded_frame_size;
initdata.extralen = min((pmt->Format() + pmt->FormatLength()) - (p + 4), (LONG) * (DWORD*)p);
initdata.extra = p + 4;
}
if (FAILED(hr = RAInitDecoder(m_dwCookie, &initdata))) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
if (RASetPwd) {
RASetPwd(m_dwCookie, "Ardubancel Quazanga");
}
if (RASetFlavor && FAILED(hr = RASetFlavor(m_dwCookie, m_rai.flavor))) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
return S_OK;
}
void CRealAudioDecoder::FreeRA()
{
if (m_dwCookie) {
RAFreeDecoder(m_dwCookie);
RACloseCodec(m_dwCookie);
m_dwCookie = 0;
}
}
HRESULT CRealAudioDecoder::Receive(IMediaSample* pIn)
{
CAutoLock cAutoLock(&m_csReceive);
HRESULT hr;
AM_SAMPLE2_PROPERTIES* const pProps = m_pInput->SampleProps();
if (pProps->dwStreamId != AM_STREAM_MEDIA) {
return m_pOutput->Deliver(pIn);
}
BYTE* pDataIn = nullptr;
if (FAILED(hr = pIn->GetPointer(&pDataIn))) {
return hr;
}
BYTE* pDataInOrg = pDataIn;
UNREFERENCED_PARAMETER(pDataInOrg);
long len = pIn->GetActualDataLength();
if (len <= 0) {
return S_OK;
}
REFERENCE_TIME rtStart, rtStop;
pIn->GetTime(&rtStart, &rtStop);
/*
if (pIn->IsPreroll() == S_OK || rtStart < 0)
return S_OK;
*/
if (S_OK == pIn->IsSyncPoint()) {
m_bufflen = 0;
m_rtBuffStart = rtStart;
m_fBuffDiscontinuity = pIn->IsDiscontinuity() == S_OK;
}
BYTE* src = nullptr;
BYTE* dst = nullptr;
int w = m_rai.coded_frame_size;
int h = m_rai.sub_packet_h;
int sps = m_rai.sub_packet_size;
if (m_pInput->CurrentMediaType().subtype == MEDIASUBTYPE_RAAC
|| m_pInput->CurrentMediaType().subtype == MEDIASUBTYPE_RACP
|| m_pInput->CurrentMediaType().subtype == MEDIASUBTYPE_AAC) {
src = pDataIn;
dst = pDataIn + len;
w = len;
} else {
memcpy(&m_buff[m_bufflen], pDataIn, len);
m_bufflen += len;
len = w * h;
if (m_bufflen >= len) {
ASSERT(m_bufflen == len);
src = m_buff;
dst = m_buff + len;
if (sps > 0
&& (m_pInput->CurrentMediaType().subtype == MEDIASUBTYPE_COOK
|| m_pInput->CurrentMediaType().subtype == MEDIASUBTYPE_ATRC)) {
for (int y = 0; y < h; y++) {
for (int x = 0, w2 = w / sps; x < w2; x++) {
// TRACE(_T("--- %d, %d\n"), (h*x+((h+1)/2)*(y&1)+(y>>1)), sps*(h*x+((h+1)/2)*(y&1)+(y>>1)));
memcpy(dst + sps * (h * x + ((h + 1) / 2) * (y & 1) + (y >> 1)), src, sps);
src += sps;
}
}
src = m_buff + len;
dst = m_buff + len * 2;
} else if (m_pInput->CurrentMediaType().subtype == MEDIASUBTYPE_SIPR) {
// http://mplayerhq.hu/pipermail/mplayer-dev-eng/2002-August/010569.html
static BYTE sipr_swaps[38][2] = {
{0, 63}, {1, 22}, {2, 44}, {3, 90}, {5, 81}, {7, 31}, {8, 86}, {9, 58}, {10, 36}, {12, 68},
{13, 39}, {14, 73}, {15, 53}, {16, 69}, {17, 57}, {19, 88}, {20, 34}, {21, 71}, {24, 46},
{25, 94}, {26, 54}, {28, 75}, {29, 50}, {32, 70}, {33, 92}, {35, 74}, {38, 85}, {40, 56},
{42, 87}, {43, 65}, {45, 59}, {48, 79}, {49, 93}, {51, 89}, {55, 95}, {61, 76}, {67, 83},
{77, 80}
};
int bs = h * w * 2 / 96; // nibbles per subpacket
for (int n = 0; n < 38; n++) {
int i = bs * sipr_swaps[n][0];
int o = bs * sipr_swaps[n][1];
// swap nibbles of block 'i' with 'o' TODO: optimize
for (int j = 0; j < bs; j++) {
int x = (i & 1) ? (src[(i >> 1)] >> 4) : (src[(i >> 1)] & 15);
int y = (o & 1) ? (src[(o >> 1)] >> 4) : (src[(o >> 1)] & 15);
if (o & 1) {
src[(o >> 1)] = (src[(o >> 1)] & 0x0F) | (x << 4);
} else {
src[(o >> 1)] = (src[(o >> 1)] & 0xF0) | x;
}
if (i & 1) {
src[(i >> 1)] = (src[(i >> 1)] & 0x0F) | (y << 4);
} else {
src[(i >> 1)] = (src[(i >> 1)] & 0xF0) | y;
}
++i;
++o;
}
}
}
m_bufflen = 0;
}
}
rtStart = m_rtBuffStart;
for (; src < dst; src += w) {
CComPtr pOut;
BYTE* pDataOut = nullptr;
if (FAILED(hr = m_pOutput->GetDeliveryBuffer(&pOut, nullptr, nullptr, 0))
|| FAILED(hr = pOut->GetPointer(&pDataOut))) {
return hr;
}
AM_MEDIA_TYPE* pmt;
if (SUCCEEDED(pOut->GetMediaType(&pmt)) && pmt) {
CMediaType mt(*pmt);
m_pOutput->SetMediaType(&mt);
DeleteMediaType(pmt);
}
hr = RADecode(m_dwCookie, src, w, pDataOut, &len, -1);
if (FAILED(hr)) {
TRACE(_T("RA returned an error code!!!\n"));
continue;
// return hr;
}
WAVEFORMATEX* pwfe = (WAVEFORMATEX*)m_pOutput->CurrentMediaType().Format();
rtStop = rtStart + 1000i64 * len / pwfe->nAvgBytesPerSec * 10000;
pOut->SetTime(&rtStart, &rtStop);
pOut->SetMediaTime(nullptr, nullptr);
pOut->SetDiscontinuity(m_fBuffDiscontinuity);
m_fBuffDiscontinuity = false;
pOut->SetSyncPoint(TRUE);
pOut->SetActualDataLength(len);
DbgLog((LOG_TRACE, 0, _T("A: rtStart=%I64d, rtStop=%I64d, disc=%d, sync=%d"),
rtStart, rtStop, pOut->IsDiscontinuity() == S_OK, pOut->IsSyncPoint() == S_OK));
if (rtStart >= 0 && S_OK != (hr = m_pOutput->Deliver(pOut))) {
return hr;
}
rtStart = rtStop;
}
m_rtBuffStart = rtStart;
return S_OK;
}
HRESULT CRealAudioDecoder::CheckInputType(const CMediaType* mtIn)
{
if (mtIn->majortype != MEDIATYPE_Audio
|| mtIn->subtype != MEDIASUBTYPE_14_4
&& mtIn->subtype != MEDIASUBTYPE_28_8
&& mtIn->subtype != MEDIASUBTYPE_ATRC
&& mtIn->subtype != MEDIASUBTYPE_COOK
&& mtIn->subtype != MEDIASUBTYPE_DNET
&& mtIn->subtype != MEDIASUBTYPE_SIPR
&& mtIn->subtype != MEDIASUBTYPE_RAAC
&& mtIn->subtype != MEDIASUBTYPE_RACP
&& mtIn->subtype != MEDIASUBTYPE_AAC) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
if (!m_pInput->IsConnected()) {
if (m_hDrvDll) {
FreeLibrary(m_hDrvDll);
m_hDrvDll = nullptr;
}
CAtlList paths;
CString olddll, newdll, oldpath, newpath;
TCHAR fourcc[5] = {
(TCHAR)((mtIn->subtype.Data1 >> 0) & 0xff),
(TCHAR)((mtIn->subtype.Data1 >> 8) & 0xff),
(TCHAR)((mtIn->subtype.Data1 >> 16) & 0xff),
(TCHAR)((mtIn->subtype.Data1 >> 24) & 0xff),
0
};
if (!_tcscmp(_T("RACP"), fourcc) || !_tcscmp(_T("\xff"), fourcc)) {
_tcscpy_s(fourcc, _T("RAAC"));
}
olddll.Format(_T("%s3260.dll"), fourcc);
newdll.Format(_T("%s.dll"), fourcc);
CRegKey key;
TCHAR buff[MAX_PATH];
ULONG len = _countof(buff);
if (ERROR_SUCCESS == key.Open(HKEY_CLASSES_ROOT, _T("Software\\RealNetworks\\Preferences\\DT_Codecs"), KEY_READ)
&& ERROR_SUCCESS == key.QueryStringValue(nullptr, buff, &len) && _tcslen(buff) > 0) {
oldpath = buff;
TCHAR c = oldpath[oldpath.GetLength() - 1];
if (c != '\\' && c != '/') {
oldpath += '\\';
}
key.Close();
}
len = _countof(buff);
if (ERROR_SUCCESS == key.Open(HKEY_CLASSES_ROOT, _T("Helix\\HelixSDK\\10.0\\Preferences\\DT_Codecs"), KEY_READ)
&& ERROR_SUCCESS == key.QueryStringValue(nullptr, buff, &len) && _tcslen(buff) > 0) {
newpath = buff;
TCHAR c = newpath[newpath.GetLength() - 1];
if (c != '\\' && c != '/') {
newpath += '\\';
}
key.Close();
}
if (!newpath.IsEmpty()) {
paths.AddTail(newpath + newdll);
}
if (!oldpath.IsEmpty()) {
paths.AddTail(oldpath + newdll);
}
paths.AddTail(newdll); // default dll paths
if (!newpath.IsEmpty()) {
paths.AddTail(newpath + olddll);
}
if (!oldpath.IsEmpty()) {
paths.AddTail(oldpath + olddll);
}
paths.AddTail(olddll); // default dll paths
POSITION pos = paths.GetHeadPosition();
do {
if (pos) {
m_hDrvDll = LoadLibrary(paths.GetNext(pos));
}
} while (pos && !m_hDrvDll);
if (m_hDrvDll) {
RACloseCodec = (PCloseCodec)GetProcAddress(m_hDrvDll, "RACloseCodec");
RADecode = (PDecode)GetProcAddress(m_hDrvDll, "RADecode");
RAFlush = (PFlush)GetProcAddress(m_hDrvDll, "RAFlush");
RAFreeDecoder = (PFreeDecoder)GetProcAddress(m_hDrvDll, "RAFreeDecoder");
RAGetFlavorProperty = (PGetFlavorProperty)GetProcAddress(m_hDrvDll, "RAGetFlavorProperty");
RAInitDecoder = (PInitDecoder)GetProcAddress(m_hDrvDll, "RAInitDecoder");
RAOpenCodec = (POpenCodec)GetProcAddress(m_hDrvDll, "RAOpenCodec");
RAOpenCodec2 = (POpenCodec2)GetProcAddress(m_hDrvDll, "RAOpenCodec2");
RASetFlavor = (PSetFlavor)GetProcAddress(m_hDrvDll, "RASetFlavor");
RASetDLLAccessPath = (PSetDLLAccessPath)GetProcAddress(m_hDrvDll, "RASetDLLAccessPath");
RASetPwd = (PSetPwd)GetProcAddress(m_hDrvDll, "RASetPwd");
}
if (!m_hDrvDll || !RACloseCodec || !RADecode /*|| !RAFlush*/
|| !RAFreeDecoder || !RAGetFlavorProperty || !RAInitDecoder
|| !(RAOpenCodec || RAOpenCodec2) /*|| !RASetFlavor*/) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
if (m_hDrvDll) {
char chbuff[MAX_PATH];
GetModuleFileNameA(m_hDrvDll, chbuff, MAX_PATH);
CPathA p(chbuff);
p.RemoveFileSpec();
p.AddBackslash();
m_dllpath = p.m_strPath;
if (RASetDLLAccessPath) {
RASetDLLAccessPath("DT_Codecs=" + m_dllpath);
}
}
if (FAILED(InitRA(mtIn))) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
}
return S_OK;
}
HRESULT CRealAudioDecoder::CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut)
{
return mtIn->majortype == MEDIATYPE_Audio && (mtIn->subtype == MEDIASUBTYPE_14_4
|| mtIn->subtype == MEDIASUBTYPE_28_8
|| mtIn->subtype == MEDIASUBTYPE_ATRC
|| mtIn->subtype == MEDIASUBTYPE_COOK
|| mtIn->subtype == MEDIASUBTYPE_DNET
|| mtIn->subtype == MEDIASUBTYPE_SIPR
|| mtIn->subtype == MEDIASUBTYPE_RAAC
|| mtIn->subtype == MEDIASUBTYPE_RACP
|| mtIn->subtype == MEDIASUBTYPE_AAC)
&& mtOut->majortype == MEDIATYPE_Audio && mtOut->subtype == MEDIASUBTYPE_PCM
? S_OK
: VFW_E_TYPE_NOT_ACCEPTED;
}
HRESULT CRealAudioDecoder::DecideBufferSize(IMemAllocator* pAllocator, ALLOCATOR_PROPERTIES* pProperties)
{
if (m_pInput->IsConnected() == FALSE) {
return E_UNEXPECTED;
}
CComPtr pAllocatorIn;
m_pInput->GetAllocator(&pAllocatorIn);
if (!pAllocatorIn) {
return E_UNEXPECTED;
}
WAVEFORMATEX* pwfe = (WAVEFORMATEX*)m_pOutput->CurrentMediaType().Format();
WORD wBitsPerSample = pwfe->wBitsPerSample;
if (!wBitsPerSample) {
wBitsPerSample = 16;
}
// ok, maybe this is too much...
pProperties->cBuffers = 8;
pProperties->cbBuffer = pwfe->nChannels * pwfe->nSamplesPerSec * wBitsPerSample >> 3; // nAvgBytesPerSec;
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 CRealAudioDecoder::GetMediaType(int iPosition, CMediaType* pmt)
{
if (m_pInput->IsConnected() == FALSE) {
return E_UNEXPECTED;
}
*pmt = m_pInput->CurrentMediaType();
pmt->subtype = MEDIASUBTYPE_PCM;
WAVEFORMATEX* pwfe = (WAVEFORMATEX*)pmt->ReallocFormatBuffer(sizeof(WAVEFORMATEX));
if (iPosition < 0) {
return E_INVALIDARG;
}
if (iPosition > (pwfe->nChannels > 2 && pwfe->nChannels <= 6 ? 1 : 0)) {
return VFW_S_NO_MORE_ITEMS;
}
if (!pwfe->wBitsPerSample) {
pwfe->wBitsPerSample = 16;
}
pwfe->cbSize = 0;
pwfe->wFormatTag = WAVE_FORMAT_PCM;
pwfe->nBlockAlign = pwfe->nChannels * pwfe->wBitsPerSample >> 3;
pwfe->nAvgBytesPerSec = pwfe->nSamplesPerSec * pwfe->nBlockAlign;
if (iPosition == 0 && pwfe->nChannels > 2 && pwfe->nChannels <= 6) {
static DWORD chmask[] = {
KSAUDIO_SPEAKER_DIRECTOUT,
KSAUDIO_SPEAKER_MONO,
KSAUDIO_SPEAKER_STEREO,
KSAUDIO_SPEAKER_STEREO | SPEAKER_FRONT_CENTER,
KSAUDIO_SPEAKER_QUAD,
KSAUDIO_SPEAKER_QUAD | SPEAKER_FRONT_CENTER,
KSAUDIO_SPEAKER_5POINT1
};
WAVEFORMATEXTENSIBLE* pwfex = (WAVEFORMATEXTENSIBLE*)pmt->ReallocFormatBuffer(sizeof(WAVEFORMATEXTENSIBLE));
pwfex->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
pwfex->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
pwfex->dwChannelMask = chmask[pwfex->Format.nChannels];
pwfex->Samples.wValidBitsPerSample = pwfex->Format.wBitsPerSample;
pwfex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
return S_OK;
}
HRESULT CRealAudioDecoder::StartStreaming()
{
int w = m_rai.coded_frame_size;
int h = m_rai.sub_packet_h;
int sps = m_rai.sub_packet_size;
UNREFERENCED_PARAMETER(sps);
int len = w * h;
m_buff.Allocate(len * 2);
m_bufflen = 0;
m_rtBuffStart = 0;
return __super::StartStreaming();
}
HRESULT CRealAudioDecoder::StopStreaming()
{
m_buff.Free();
m_bufflen = 0;
return __super::StopStreaming();
}
HRESULT CRealAudioDecoder::EndOfStream()
{
return __super::EndOfStream();
}
HRESULT CRealAudioDecoder::BeginFlush()
{
return __super::BeginFlush();
}
HRESULT CRealAudioDecoder::EndFlush()
{
return __super::EndFlush();
}
HRESULT CRealAudioDecoder::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
CAutoLock cAutoLock(&m_csReceive);
m_tStart = tStart;
m_bufflen = 0;
m_rtBuffStart = 0;
return __super::NewSegment(tStart, tStop, dRate);
}