/*
* (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 "FLVSplitter.h"
#include "../../../DSUtil/DSUtil.h"
#ifdef STANDALONE_FILTER
#include
#endif
#include
#include "moreuuids.h"
#define FLV_AUDIODATA 8
#define FLV_VIDEODATA 9
#define FLV_SCRIPTDATA 18
#define FLV_AUDIO_PCM 0 // Linear PCM, platform endian
#define FLV_AUDIO_ADPCM 1 // ADPCM
#define FLV_AUDIO_MP3 2 // MP3
#define FLV_AUDIO_PCMLE 3 // Linear PCM, little endian
#define FLV_AUDIO_NELLY16 4 // Nellymoser 16 kHz mono
#define FLV_AUDIO_NELLY8 5 // Nellymoser 8 kHz mono
#define FLV_AUDIO_NELLY 6 // Nellymoser
// 7 = G.711 A-law logarithmic PCM (reserved)
// 8 = G.711 mu-law logarithmic PCM (reserved)
// 9 = reserved
#define FLV_AUDIO_AAC 10 // AAC
#define FLV_AUDIO_SPEEX 11 // Speex
// 14 = MP3 8 kHz (reserved)
// 15 = Device-specific sound (reserved)
//#define FLV_VIDEO_JPEG 1 // non-standard? need samples
#define FLV_VIDEO_H263 2 // Sorenson H.263
#define FLV_VIDEO_SCREEN 3 // Screen video
#define FLV_VIDEO_VP6 4 // On2 VP6
#define FLV_VIDEO_VP6A 5 // On2 VP6 with alpha channel
#define FLV_VIDEO_SCREEN2 6 // Screen video version 2
#define FLV_VIDEO_AVC 7 // AVC
#ifdef STANDALONE_FILTER
const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] = {
{&MEDIATYPE_Stream, &MEDIASUBTYPE_FLV},
{&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 sudPinTypesOut2[] = {
{&MEDIATYPE_Video, &MEDIASUBTYPE_NULL},
};
const AMOVIESETUP_FILTER sudFilter[] = {
{&__uuidof(CFLVSplitterFilter), FlvSplitterName, MERIT_NORMAL, _countof(sudpPins), sudpPins, CLSID_LegacyAmFilterCategory},
{&__uuidof(CFLVSourceFilter), FlvSourceName, MERIT_NORMAL, 0, nullptr, 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]},
};
int g_cTemplates = _countof(g_Templates);
STDAPI DllRegisterServer()
{
DeleteRegKey(_T("Media Type\\Extensions\\"), _T(".flv"));
RegisterSourceFilter(CLSID_AsyncReader, MEDIASUBTYPE_FLV, _T("0,4,,464C5601"), nullptr);
return AMovieDllRegisterServer2(TRUE);
}
STDAPI DllUnregisterServer()
{
UnRegisterSourceFilter(MEDIASUBTYPE_FLV);
return AMovieDllRegisterServer2(FALSE);
}
#include "../../FilterApp.h"
CFilterApp theApp;
#endif
//
// CFLVSplitterFilter
//
CFLVSplitterFilter::CFLVSplitterFilter(LPUNKNOWN pUnk, HRESULT* phr)
: CBaseSplitterFilter(NAME("CFLVSplitterFilter"), pUnk, phr, __uuidof(this))
{
}
STDMETHODIMP CFLVSplitterFilter::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, FlvSourceName);
}
pInfo->pGraph = m_pGraph;
if (m_pGraph) {
m_pGraph->AddRef();
}
return S_OK;
}
bool CFLVSplitterFilter::ReadTag(Tag& t)
{
if (m_pFile->GetRemaining() < 15) {
return false;
}
t.PreviousTagSize = (UINT32)m_pFile->BitRead(32);
t.TagType = (BYTE)m_pFile->BitRead(8);
t.DataSize = (UINT32)m_pFile->BitRead(24);
t.TimeStamp = (UINT32)m_pFile->BitRead(24);
t.TimeStamp |= (UINT32)m_pFile->BitRead(8) << 24;
t.StreamID = (UINT32)m_pFile->BitRead(24);
return m_pFile->GetRemaining() >= t.DataSize;
}
bool CFLVSplitterFilter::ReadTag(AudioTag& at)
{
if (!m_pFile->GetRemaining()) {
return false;
}
at.SoundFormat = (BYTE)m_pFile->BitRead(4);
at.SoundRate = (BYTE)m_pFile->BitRead(2);
at.SoundSize = (BYTE)m_pFile->BitRead(1);
at.SoundType = (BYTE)m_pFile->BitRead(1);
return true;
}
bool CFLVSplitterFilter::ReadTag(VideoTag& vt)
{
if (!m_pFile->GetRemaining()) {
return false;
}
vt.FrameType = (BYTE)m_pFile->BitRead(4);
vt.CodecID = (BYTE)m_pFile->BitRead(4);
return true;
}
#ifndef NOVIDEOTWEAK
bool CFLVSplitterFilter::ReadTag(VideoTweak& vt)
{
if (!m_pFile->GetRemaining()) {
return false;
}
vt.x = (BYTE)m_pFile->BitRead(4);
vt.y = (BYTE)m_pFile->BitRead(4);
return true;
}
#endif
bool CFLVSplitterFilter::Sync(__int64& pos)
{
m_pFile->Seek(pos);
while (m_pFile->GetRemaining() >= 15) {
__int64 limit = m_pFile->GetRemaining();
while (true) {
BYTE b = (BYTE)m_pFile->BitRead(8);
if (b == FLV_AUDIODATA || b == FLV_VIDEODATA) {
break;
}
if (--limit < 15) {
return false;
}
}
pos = m_pFile->GetPos() - 5;
m_pFile->Seek(pos);
Tag ct;
if (ReadTag(ct)) {
__int64 next = m_pFile->GetPos() + ct.DataSize;
if (next == m_pFile->GetLength() - 4) {
m_pFile->Seek(pos);
return true;
} else if (next <= m_pFile->GetLength() - 19) {
m_pFile->Seek(next);
Tag nt;
if (ReadTag(nt) && (nt.TagType == FLV_AUDIODATA || nt.TagType == FLV_VIDEODATA || nt.TagType == FLV_SCRIPTDATA)) {
if ((nt.PreviousTagSize == ct.DataSize + 11) ||
(m_IgnorePrevSizes &&
nt.TimeStamp >= ct.TimeStamp &&
nt.TimeStamp - ct.TimeStamp <= 1000)) {
m_pFile->Seek(pos);
return true;
}
}
}
}
m_pFile->Seek(pos + 5);
}
return false;
}
HRESULT CFLVSplitterFilter::CreateOutputs(IAsyncReader* pAsyncReader)
{
CheckPointer(pAsyncReader, E_POINTER);
HRESULT hr = E_FAIL;
m_pFile.Free();
m_pFile.Attach(DEBUG_NEW CBaseSplitterFileEx(pAsyncReader, hr, DEFAULT_CACHE_LENGTH, false));
if (!m_pFile) {
return E_OUTOFMEMORY;
}
if (FAILED(hr)) {
m_pFile.Free();
return hr;
}
m_rtNewStart = m_rtCurrent = 0;
m_rtNewStop = m_rtStop = m_rtDuration = 0;
if (m_pFile->BitRead(24) != 'FLV' || m_pFile->BitRead(8) != 1) {
return E_FAIL;
}
EXECUTE_ASSERT(m_pFile->BitRead(5) == 0); // TypeFlagsReserved
bool fTypeFlagsAudio = !!m_pFile->BitRead(1);
EXECUTE_ASSERT(m_pFile->BitRead(1) == 0); // TypeFlagsReserved
bool fTypeFlagsVideo = !!m_pFile->BitRead(1);
m_DataOffset = (UINT32)m_pFile->BitRead(32);
// doh, these flags aren't always telling the truth
fTypeFlagsAudio = fTypeFlagsVideo = true;
Tag t;
AudioTag at;
VideoTag vt;
UINT32 prevTagSize = 0;
m_IgnorePrevSizes = false;
m_pFile->Seek(m_DataOffset);
for (int i = 0; ReadTag(t) && (fTypeFlagsVideo || fTypeFlagsAudio); i++) {
if (!t.DataSize) {
continue; // skip empty Tag
}
UINT64 next = m_pFile->GetPos() + t.DataSize;
CStringW name;
CMediaType mt;
CMediaType ff_mtype;
mt.SetSampleSize(1);
mt.subtype = GUID_NULL;
if (i != 0 && t.PreviousTagSize != prevTagSize) {
m_IgnorePrevSizes = true;
}
prevTagSize = t.DataSize + 11;
if (t.TagType == FLV_AUDIODATA && t.DataSize != 0 && fTypeFlagsAudio) {
UNREFERENCED_PARAMETER(at);
AudioTag at;
name = L"Audio";
if (ReadTag(at)) {
int dataSize = t.DataSize - 1;
fTypeFlagsAudio = false;
mt.majortype = MEDIATYPE_Audio;
mt.formattype = FORMAT_WaveFormatEx;
WAVEFORMATEX* wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX));
memset(wfe, 0, sizeof(WAVEFORMATEX));
wfe->nSamplesPerSec = 44100 * (1 << at.SoundRate) / 8;
wfe->wBitsPerSample = 8 * (at.SoundSize + 1);
wfe->nChannels = at.SoundType + 1;
switch (at.SoundFormat) {
case FLV_AUDIO_PCM:
case FLV_AUDIO_PCMLE:
mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_PCM);
name += L" PCM";
wfe->nBlockAlign = wfe->wBitsPerSample * wfe->nChannels / 8;
wfe->nAvgBytesPerSec = wfe->nBlockAlign * wfe->nSamplesPerSec;
break;
case FLV_AUDIO_ADPCM:
mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_ADPCM_SWF);
name += L" ADPCM";
break;
case FLV_AUDIO_MP3:
mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_MPEGLAYER3);
name += L" MP3";
{
CBaseSplitterFileEx::mpahdr h;
CMediaType mt2;
if (m_pFile->Read(h, 4, false, &mt2)) {
mt = mt2;
}
}
break;
case FLV_AUDIO_NELLY16:
mt.subtype = FOURCCMap(MAKEFOURCC('N', 'E', 'L', 'L'));
wfe->nSamplesPerSec = 16000;
name += L" Nellimoser";
break;
case FLV_AUDIO_NELLY8:
mt.subtype = FOURCCMap(MAKEFOURCC('N', 'E', 'L', 'L'));
wfe->nSamplesPerSec = 8000;
name += L" Nellimoser";
break;
case FLV_AUDIO_NELLY:
mt.subtype = FOURCCMap(MAKEFOURCC('N', 'E', 'L', 'L'));
name += L" Nellimoser";
break;
case FLV_AUDIO_AAC: {
if (dataSize < 1 || m_pFile->BitRead(8) != 0) { // packet type 0 == aac header
fTypeFlagsAudio = true;
break;
}
mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_AAC);
name += L" AAC";
__int64 configOffset = m_pFile->GetPos();
UINT32 configSize = dataSize - 1;
if (configSize < 2) {
break;
}
// Might break depending on the AAC profile, see ff_mpeg4audio_get_config in ffmpeg's mpeg4audio.c
m_pFile->BitRead(5);
int iSampleRate = (int)m_pFile->BitRead(4);
int iChannels = (int)m_pFile->BitRead(4);
if (iSampleRate > 12 || iChannels > 7) {
break;
}
const int sampleRates[] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000,
22050, 16000, 12000, 11025, 8000, 7350
};
const int channels[] = {
0, 1, 2, 3, 4, 5, 6, 8
};
wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX) + configSize);
memset(wfe, 0, mt.FormatLength());
wfe->nSamplesPerSec = sampleRates[iSampleRate];
wfe->wBitsPerSample = 16;
wfe->nChannels = channels[iChannels];
wfe->cbSize = configSize;
m_pFile->Seek(configOffset);
m_pFile->ByteRead((BYTE*)(wfe + 1), configSize);
}
}
}
} else if (t.TagType == FLV_VIDEODATA && t.DataSize != 0 && fTypeFlagsVideo) {
UNREFERENCED_PARAMETER(vt);
VideoTag vt;
if (ReadTag(vt) && vt.FrameType == 1) {
int dataSize = t.DataSize - 1;
fTypeFlagsVideo = false;
name = L"Video";
mt.majortype = MEDIATYPE_Video;
mt.formattype = FORMAT_VideoInfo;
VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER));
memset(vih, 0, sizeof(VIDEOINFOHEADER));
BITMAPINFOHEADER* bih = &vih->bmiHeader;
int w, h, arx, ary;
switch (vt.CodecID) {
case FLV_VIDEO_H263: // H.263
if (m_pFile->BitRead(17) != 1) {
break;
}
m_pFile->BitRead(13); // Version (5), TemporalReference (8)
switch (BYTE PictureSize = (BYTE)m_pFile->BitRead(3)) { // w00t
case 0:
case 1:
vih->bmiHeader.biWidth = (WORD)m_pFile->BitRead(8 * (PictureSize + 1));
vih->bmiHeader.biHeight = (WORD)m_pFile->BitRead(8 * (PictureSize + 1));
break;
case 2:
case 3:
case 4:
vih->bmiHeader.biWidth = 704 / PictureSize;
vih->bmiHeader.biHeight = 576 / PictureSize;
break;
case 5:
case 6:
PictureSize -= 3;
vih->bmiHeader.biWidth = 640 / PictureSize;
vih->bmiHeader.biHeight = 480 / PictureSize;
break;
}
if (!vih->bmiHeader.biWidth || !vih->bmiHeader.biHeight) {
break;
}
mt.subtype = FOURCCMap(vih->bmiHeader.biCompression = '1VLF');
name += L" H.263";
break;
case FLV_VIDEO_SCREEN: {
m_pFile->BitRead(4);
vih->bmiHeader.biWidth = (LONG)m_pFile->BitRead(12);
m_pFile->BitRead(4);
vih->bmiHeader.biHeight = (LONG)m_pFile->BitRead(12);
if (!vih->bmiHeader.biWidth || !vih->bmiHeader.biHeight) {
break;
}
vih->bmiHeader.biSize = sizeof(vih->bmiHeader);
vih->bmiHeader.biPlanes = 1;
vih->bmiHeader.biBitCount = 24;
vih->bmiHeader.biSizeImage = vih->bmiHeader.biWidth * vih->bmiHeader.biHeight * 3;
mt.subtype = FOURCCMap(vih->bmiHeader.biCompression = '1VSF');
name += L" Screen";
break;
}
case FLV_VIDEO_VP6A: // VP6 with alpha
m_pFile->BitRead(24);
case FLV_VIDEO_VP6: { // VP6
#ifdef NOVIDEOTWEAK
m_pFile->BitRead(8);
#else
VideoTweak fudge;
ReadTag(fudge);
#endif
if (m_pFile->BitRead(1)) {
// Delta (inter) frame
fTypeFlagsVideo = true;
break;
}
m_pFile->BitRead(6);
bool fSeparatedCoeff = !!m_pFile->BitRead(1);
m_pFile->BitRead(5);
int filterHeader = (int)m_pFile->BitRead(2);
m_pFile->BitRead(1);
if (fSeparatedCoeff || !filterHeader) {
m_pFile->BitRead(16);
}
h = (int)m_pFile->BitRead(8) * 16;
w = (int)m_pFile->BitRead(8) * 16;
ary = (int)m_pFile->BitRead(8) * 16;
arx = (int)m_pFile->BitRead(8) * 16;
if (arx && arx != w || ary && ary != h) {
VIDEOINFOHEADER2* vih2 = (VIDEOINFOHEADER2*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER2));
memset(vih2, 0, sizeof(VIDEOINFOHEADER2));
vih2->dwPictAspectRatioX = arx;
vih2->dwPictAspectRatioY = ary;
bih = &vih2->bmiHeader;
mt.formattype = FORMAT_VideoInfo2;
vih = (VIDEOINFOHEADER*)vih2;
}
bih->biWidth = w;
bih->biHeight = h;
#ifndef NOVIDEOTWEAK
SetRect(&vih->rcSource, 0, 0, w - fudge.x, h - fudge.y);
SetRect(&vih->rcTarget, 0, 0, w - fudge.x, h - fudge.y);
#endif
mt.subtype = FOURCCMap(bih->biCompression = '4VLF');
name += L" VP6";
break;
}
case FLV_VIDEO_AVC: { // H.264
if (dataSize < 4 || m_pFile->BitRead(8) != 0) { // packet type 0 == avc header
fTypeFlagsVideo = true;
break;
}
m_pFile->BitRead(24); // composition time
__int64 headerOffset = m_pFile->GetPos();
UINT32 headerSize = dataSize - 4;
BYTE* headerData = DEBUG_NEW BYTE[headerSize];
m_pFile->ByteRead(headerData, headerSize);
m_pFile->Seek(headerOffset + 9);
mt.formattype = FORMAT_MPEG2Video;
MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + headerSize);
memset(vih, 0, mt.FormatLength());
vih->hdr.bmiHeader.biSize = sizeof(vih->hdr.bmiHeader);
vih->hdr.bmiHeader.biPlanes = 1;
vih->hdr.bmiHeader.biBitCount = 24;
vih->dwFlags = (headerData[4] & 0x03) + 1; // nal length size
vih->dwProfile = (BYTE)m_pFile->BitRead(8);
m_pFile->BitRead(8);
vih->dwLevel = (BYTE)m_pFile->BitRead(8);
m_pFile->UExpGolombRead(); // seq_parameter_set_id
UINT64 chroma_format_idc = 0;
if (vih->dwProfile >= 100) { // high profile
chroma_format_idc = m_pFile->UExpGolombRead();
if (chroma_format_idc == 3) { // chroma_format_idc
m_pFile->BitRead(1); // residue_transform_flag
}
m_pFile->UExpGolombRead(); // bit_depth_luma_minus8
m_pFile->UExpGolombRead(); // bit_depth_chroma_minus8
m_pFile->BitRead(1); // qpprime_y_zero_transform_bypass_flag
if (m_pFile->BitRead(1)) { // seq_scaling_matrix_present_flag
for (int k = 0; k < 8; k++) {
if (m_pFile->BitRead(1)) { // seq_scaling_list_present_flag
for (int j = 0, size = (k < 6) ? 16 : 64, next = 8; j < size && next != 0; ++j) {
next = (next + m_pFile->SExpGolombRead() + 256) & 255;
}
}
}
}
}
m_pFile->UExpGolombRead(); // log2_max_frame_num_minus4
UINT64 pic_order_cnt_type = m_pFile->UExpGolombRead();
if (pic_order_cnt_type == 0) {
m_pFile->UExpGolombRead(); // log2_max_pic_order_cnt_lsb_minus4
} else if (pic_order_cnt_type == 1) {
m_pFile->BitRead(1); // delta_pic_order_always_zero_flag
m_pFile->SExpGolombRead(); // offset_for_non_ref_pic
m_pFile->SExpGolombRead(); // offset_for_top_to_bottom_field
UINT64 num_ref_frames_in_pic_order_cnt_cycle = m_pFile->UExpGolombRead();
for (int k = 0; k < num_ref_frames_in_pic_order_cnt_cycle; k++) {
m_pFile->SExpGolombRead(); // offset_for_ref_frame[k]
}
}
m_pFile->UExpGolombRead(); // num_ref_frames
m_pFile->BitRead(1); // gaps_in_frame_num_value_allowed_flag
UINT64 pic_width_in_mbs_minus1 = m_pFile->UExpGolombRead();
UINT64 pic_height_in_map_units_minus1 = m_pFile->UExpGolombRead();
BYTE frame_mbs_only_flag = (BYTE)m_pFile->BitRead(1);
if (!frame_mbs_only_flag) {
m_pFile->BitRead(1); // mb_adaptive_frame_field_flag
}
m_pFile->BitRead(1); // direct_8x8_inference_flag
BYTE crop = (BYTE)m_pFile->BitRead(1); // frame_cropping_flag
unsigned int crop_left = 0;
unsigned int crop_right = 0;
unsigned int crop_top = 0;
unsigned int crop_bottom = 0;
if (crop) {
crop_left = (unsigned int)m_pFile->UExpGolombRead(); // frame_cropping_rect_left_offset
crop_right = (unsigned int)m_pFile->UExpGolombRead(); // frame_cropping_rect_right_offset
crop_top = (unsigned int)m_pFile->UExpGolombRead(); // frame_cropping_rect_top_offset
crop_bottom = (unsigned int)m_pFile->UExpGolombRead(); // frame_cropping_rect_bottom_offset
}
struct sar {
WORD num;
WORD den;
} sar;
if (m_pFile->BitRead(1)) { // vui_parameters_present_flag
if (m_pFile->BitRead(1)) { // aspect_ratio_info_present_flag
BYTE aspect_ratio_idc = (BYTE)m_pFile->BitRead(8); // aspect_ratio_idc
if (255 == aspect_ratio_idc) {
sar.num = (WORD)m_pFile->BitRead(16); // sar_width
sar.den = (WORD)m_pFile->BitRead(16); // sar_height
} else if (aspect_ratio_idc < 17) {
sar.num = pixel_aspect[aspect_ratio_idc][0];
sar.den = pixel_aspect[aspect_ratio_idc][1];
} else {
return E_FAIL;
}
} else {
sar.num = 1;
sar.den = 1;
}
}
unsigned int mb_Width = (unsigned int)pic_width_in_mbs_minus1 + 1;
unsigned int mb_Height = ((unsigned int)pic_height_in_map_units_minus1 + 1) * (2 - frame_mbs_only_flag);
BYTE CHROMA444 = (chroma_format_idc == 3);
unsigned int Width, Height;
Width = 16 * mb_Width - (2u >> CHROMA444) * min(crop_right, (8u << CHROMA444) - 1);
if (frame_mbs_only_flag) {
Height = 16 * mb_Height - (2u >> CHROMA444) * min(crop_bottom, (8u << CHROMA444) - 1);
} else {
Height = 16 * mb_Height - (4u >> CHROMA444) * min(crop_bottom, (8u << CHROMA444) - 1);
}
if (!sar.num) {
sar.num = 1;
}
if (!sar.den) {
sar.den = 1;
}
CSize aspect(Width * sar.num, Height * sar.den);
int gcd = GCD(aspect.cx, aspect.cy);
if (gcd > 1) {
aspect.cx /= gcd;
aspect.cy /= gcd;
}
vih->hdr.dwPictAspectRatioX = aspect.cx;
vih->hdr.dwPictAspectRatioY = aspect.cy;
vih->hdr.bmiHeader.biWidth = Width;
vih->hdr.bmiHeader.biHeight = Height;
BYTE* src = (BYTE*)headerData + 5;
BYTE* dst = (BYTE*)vih->dwSequenceHeader;
BYTE* src_end = (BYTE*)headerData + headerSize;
BYTE* dst_end = (BYTE*)vih->dwSequenceHeader + headerSize;
int spsCount = *(src++) & 0x1F;
int ppsCount = -1;
vih->cbSequenceHeader = 0;
while (src < src_end - 1) {
if (spsCount == 0 && ppsCount == -1) {
ppsCount = *(src++);
continue;
}
if (spsCount > 0) {
spsCount--;
} else if (ppsCount > 0) {
ppsCount--;
} else {
break;
}
int len = ((src[0] << 8) | src[1]) + 2;
if (src + len > src_end || dst + len > dst_end) {
ASSERT(0);
break;
}
memcpy(dst, src, len);
src += len;
dst += len;
vih->cbSequenceHeader += len;
}
delete [] headerData;
mt.subtype = FOURCCMap(vih->hdr.bmiHeader.biCompression = '1CVA');
name += L" H.264";
break;
}
default:
fTypeFlagsVideo = true;
}
}
}
if (mt.subtype != GUID_NULL) {
CAtlArray mts;
if (mt.subtype == FOURCCMap(MAKEFOURCC('A', 'S', 'W', 'F'))) {
mts.InsertAt(0, ff_mtype);
}
mts.Add(mt);
CAutoPtr pPinOut(DEBUG_NEW CBaseSplitterOutputPin(mts, name, this, this, &hr));
EXECUTE_ASSERT(SUCCEEDED(AddOutputPin(t.TagType, pPinOut)));
}
m_pFile->Seek(next);
}
if (m_pFile->IsRandomAccess()) {
__int64 pos = max(m_DataOffset, m_pFile->GetLength() - 256 * 1024);
if (Sync(pos)) {
Tag t;
AudioTag at;
VideoTag vt;
while (ReadTag(t)) {
UINT64 next = m_pFile->GetPos() + t.DataSize;
if (t.TagType == FLV_AUDIODATA && ReadTag(at) || t.TagType == FLV_VIDEODATA && ReadTag(vt)) {
m_rtDuration = max(m_rtDuration, 10000i64 * t.TimeStamp);
}
m_pFile->Seek(next);
}
}
}
m_rtNewStop = m_rtStop = m_rtDuration;
return !m_pOutputs.IsEmpty() ? S_OK : E_FAIL;
}
bool CFLVSplitterFilter::DemuxInit()
{
SetThreadName((DWORD) - 1, "CFLVSplitterFilter");
return true;
}
void CFLVSplitterFilter::DemuxSeek(REFERENCE_TIME rt)
{
if (!m_rtDuration || rt <= 0) {
m_pFile->Seek(m_DataOffset);
} else if (!m_IgnorePrevSizes) {
NormalSeek(rt);
} else {
AlternateSeek(rt);
}
}
void CFLVSplitterFilter::NormalSeek(REFERENCE_TIME rt)
{
bool fAudio = !!GetOutputPin(FLV_AUDIODATA);
bool fVideo = !!GetOutputPin(FLV_VIDEODATA);
__int64 pos = m_DataOffset + (__int64)(double(m_pFile->GetLength() - m_DataOffset) * rt / m_rtDuration);
if (!Sync(pos)) {
ASSERT(0);
m_pFile->Seek(m_DataOffset);
return;
}
Tag t;
AudioTag at;
VideoTag vt;
while (ReadTag(t)) {
if (10000i64 * t.TimeStamp >= rt) {
m_pFile->Seek(m_pFile->GetPos() - 15);
break;
}
m_pFile->Seek(m_pFile->GetPos() + t.DataSize);
}
while (m_pFile->GetPos() >= m_DataOffset && (fAudio || fVideo) && ReadTag(t)) {
UINT64 prev = m_pFile->GetPos() - 15 - t.PreviousTagSize - 4;
if (10000i64 * t.TimeStamp <= rt) {
if (t.TagType == FLV_AUDIODATA && ReadTag(at)) {
fAudio = false;
} else if (t.TagType == FLV_VIDEODATA && ReadTag(vt) && vt.FrameType == 1) {
fVideo = false;
}
}
m_pFile->Seek(prev);
}
if (fAudio || fVideo) {
ASSERT(0);
m_pFile->Seek(m_DataOffset);
}
}
void CFLVSplitterFilter::AlternateSeek(REFERENCE_TIME rt)
{
bool hasAudio = !!GetOutputPin(FLV_AUDIODATA);
bool hasVideo = !!GetOutputPin(FLV_VIDEODATA);
__int64 estimPos = m_DataOffset + (__int64)(double(m_pFile->GetLength() - m_DataOffset) * rt / m_rtDuration);
while (true) {
estimPos -= 256 * 1024;
if (estimPos < m_DataOffset) {
estimPos = m_DataOffset;
}
bool foundAudio = !hasAudio;
bool foundVideo = !hasVideo;
__int64 bestPos = estimPos;
if (Sync(bestPos)) {
Tag t;
AudioTag at;
VideoTag vt;
while (ReadTag(t) && t.TimeStamp * 10000i64 < rt) {
__int64 cur = m_pFile->GetPos() - 15;
__int64 next = cur + 15 + t.DataSize;
if (hasAudio && t.TagType == FLV_AUDIODATA && ReadTag(at)) {
foundAudio = true;
if (!hasVideo) {
bestPos = cur;
}
} else if (hasVideo && t.TagType == FLV_VIDEODATA && ReadTag(vt) && vt.FrameType == 1) {
foundVideo = true;
bestPos = cur;
}
m_pFile->Seek(next);
}
}
if (foundAudio && foundVideo) {
m_pFile->Seek(bestPos);
return;
} else if (estimPos == m_DataOffset) {
m_pFile->Seek(m_DataOffset);
return;
}
}
}
bool CFLVSplitterFilter::DemuxLoop()
{
HRESULT hr = S_OK;
CAutoPtr p;
Tag t;
AudioTag at = {};
VideoTag vt = {};
while (SUCCEEDED(hr) && !CheckRequest(nullptr) && m_pFile->GetRemaining()) {
if (!ReadTag(t)) {
break;
}
__int64 next = m_pFile->GetPos() + t.DataSize;
if ((t.DataSize > 0) && (t.TagType == FLV_AUDIODATA && ReadTag(at) || t.TagType == FLV_VIDEODATA && ReadTag(vt))) {
UINT32 tsOffset = 0;
if (t.TagType == FLV_VIDEODATA) {
if (vt.FrameType == 5) {
goto NextTag; // video info/command frame
}
if (vt.CodecID == FLV_VIDEO_VP6) {
m_pFile->BitRead(8);
} else if (vt.CodecID == FLV_VIDEO_VP6A) {
m_pFile->BitRead(32);
} else if (vt.CodecID == FLV_VIDEO_AVC) {
if (m_pFile->BitRead(8) != 1) {
goto NextTag;
}
// Tag timestamps specify decode time, this is the display time offset
tsOffset = (UINT32)m_pFile->BitRead(24);
tsOffset = (tsOffset + 0xff800000) ^ 0xff800000; // sign extension
}
}
if (t.TagType == FLV_AUDIODATA && at.SoundFormat == FLV_AUDIO_AAC) {
if (m_pFile->BitRead(8) != 1) {
goto NextTag;
}
}
__int64 dataSize = next - m_pFile->GetPos();
if (dataSize <= 0) {
goto NextTag;
}
p.Attach(DEBUG_NEW Packet());
p->TrackNumber = t.TagType;
p->rtStart = 10000i64 * (t.TimeStamp + tsOffset);
p->rtStop = p->rtStart + 1;
p->bSyncPoint = t.TagType == FLV_VIDEODATA ? vt.FrameType == 1 : true;
p->SetCount((size_t)dataSize);
m_pFile->ByteRead(p->GetData(), p->GetCount());
hr = DeliverPacket(p);
}
NextTag:
m_pFile->Seek(next);
}
return true;
}
//
// CFLVSourceFilter
//
CFLVSourceFilter::CFLVSourceFilter(LPUNKNOWN pUnk, HRESULT* phr)
: CFLVSplitterFilter(pUnk, phr)
{
m_clsid = __uuidof(this);
m_pInput.Free();
}