2007-04-24 14:28:14 +02:00
/*
* MPEG Splitter Filter
*
* Copyright 2003 Robert Shearman
* Copyright 2004 - 2005 Christian Costa
* Copyright 2007 Chris Robinson
2008-04-09 21:07:14 +02:00
* Copyright 2008 Maarten Lankhorst
2007-04-24 14:28:14 +02:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 , USA
*/
# include <assert.h>
# include <math.h>
# include "quartz_private.h"
# include "control_private.h"
# include "pin.h"
# include "uuids.h"
# include "mmreg.h"
# include "mmsystem.h"
# include "winternl.h"
# include "wine/unicode.h"
# include "wine/debug.h"
# include "parser.h"
WINE_DEFAULT_DEBUG_CHANNEL ( quartz ) ;
# define SEQUENCE_HEADER_CODE 0xB3
# define PACK_START_CODE 0xBA
# define SYSTEM_START_CODE 0xBB
# define AUDIO_ELEMENTARY_STREAM 0xC0
# define VIDEO_ELEMENTARY_STREAM 0xE0
# define MPEG_SYSTEM_HEADER 3
# define MPEG_VIDEO_HEADER 2
# define MPEG_AUDIO_HEADER 1
# define MPEG_NO_HEADER 0
2008-04-19 07:21:59 +02:00
# define SEEK_INTERVAL (ULONGLONG)(10 * 10000000) /* Add an entry every 10 seconds */
2008-04-08 21:28:09 +02:00
struct seek_entry {
ULONGLONG bytepos ;
ULONGLONG timepos ;
} ;
2007-04-24 14:28:14 +02:00
typedef struct MPEGSplitterImpl
{
ParserImpl Parser ;
LONGLONG EndOfFile ;
2008-03-21 06:33:47 +01:00
LONGLONG duration ;
LONGLONG position ;
2008-04-22 22:57:11 +02:00
DWORD begin_offset ;
BYTE header [ 4 ] ;
/* Whether we just seeked (or started playing) */
2008-04-01 23:42:32 +02:00
BOOL seek ;
2008-04-22 22:57:11 +02:00
/* Seeking cache */
2008-04-08 21:28:09 +02:00
ULONG seek_entries ;
struct seek_entry * seektable ;
2007-04-24 14:28:14 +02:00
} MPEGSplitterImpl ;
static int MPEGSplitter_head_check ( const BYTE * header )
{
/* If this is a possible start code, check for a system or video header */
if ( header [ 0 ] = = 0 & & header [ 1 ] = = 0 & & header [ 2 ] = = 1 )
{
/* Check if we got a system or elementary stream start code */
if ( header [ 3 ] = = PACK_START_CODE | |
header [ 3 ] = = VIDEO_ELEMENTARY_STREAM | |
header [ 3 ] = = AUDIO_ELEMENTARY_STREAM )
return MPEG_SYSTEM_HEADER ;
/* Check for a MPEG video sequence start code */
if ( header [ 3 ] = = SEQUENCE_HEADER_CODE )
return MPEG_VIDEO_HEADER ;
}
/* This should give a good guess if we have an MPEG audio header */
if ( header [ 0 ] = = 0xff & & ( ( header [ 1 ] > > 5 ) & 0x7 ) = = 0x7 & &
( ( header [ 1 ] > > 1 ) & 0x3 ) ! = 0 & & ( ( header [ 2 ] > > 4 ) & 0xf ) ! = 0xf & &
( ( header [ 2 ] > > 2 ) & 0x3 ) ! = 0x3 )
return MPEG_AUDIO_HEADER ;
/* Nothing yet.. */
return MPEG_NO_HEADER ;
}
2008-03-21 06:33:47 +01:00
static const WCHAR wszAudioStream [ ] = { ' A ' , ' u ' , ' d ' , ' i ' , ' o ' , 0 } ;
static const WCHAR wszVideoStream [ ] = { ' V ' , ' i ' , ' d ' , ' e ' , ' o ' , 0 } ;
static const DWORD freqs [ 10 ] = { 44100 , 48000 , 32000 , 22050 , 24000 , 16000 , 11025 , 12000 , 8000 , 0 } ;
static const DWORD tabsel_123 [ 2 ] [ 3 ] [ 16 ] = {
{ { 0 , 32 , 64 , 96 , 128 , 160 , 192 , 224 , 256 , 288 , 320 , 352 , 384 , 416 , 448 , } ,
{ 0 , 32 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 160 , 192 , 224 , 256 , 320 , 384 , } ,
{ 0 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 160 , 192 , 224 , 256 , 320 , } } ,
{ { 0 , 32 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , 176 , 192 , 224 , 256 , } ,
{ 0 , 8 , 16 , 24 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , } ,
{ 0 , 8 , 16 , 24 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , } }
} ;
static HRESULT parse_header ( BYTE * header , LONGLONG * plen , LONGLONG * pduration )
{
2008-04-22 22:57:11 +02:00
LONGLONG duration ;
2008-03-21 06:33:47 +01:00
2008-04-23 23:13:04 +02:00
int bitrate_index , freq_index , lsf = 1 , mpeg1 , layer , padding , bitrate , length ;
2008-03-21 06:33:47 +01:00
if ( ! ( header [ 0 ] = = 0xff & & ( ( header [ 1 ] > > 5 ) & 0x7 ) = = 0x7 & &
( ( header [ 1 ] > > 1 ) & 0x3 ) ! = 0 & & ( ( header [ 2 ] > > 4 ) & 0xf ) ! = 0xf & &
( ( header [ 2 ] > > 2 ) & 0x3 ) ! = 0x3 ) )
{
2008-04-01 23:42:32 +02:00
FIXME ( " Not a valid header: %02x:%02x \n " , header [ 0 ] , header [ 1 ] ) ;
2008-03-21 06:33:47 +01:00
return E_INVALIDARG ;
}
mpeg1 = ( header [ 1 ] > > 4 ) & 0x1 ;
if ( mpeg1 )
lsf = ( ( header [ 1 ] > > 3 ) & 0x1 ) ^ 1 ;
layer = 4 - ( ( header [ 1 ] > > 1 ) & 0x3 ) ;
bitrate_index = ( ( header [ 2 ] > > 4 ) & 0xf ) ;
freq_index = ( ( header [ 2 ] > > 2 ) & 0x3 ) + ( mpeg1 ? ( lsf * 3 ) : 6 ) ;
padding = ( ( header [ 2 ] > > 1 ) & 0x1 ) ;
bitrate = tabsel_123 [ lsf ] [ layer - 1 ] [ bitrate_index ] * 1000 ;
2008-04-01 23:42:32 +02:00
if ( ! bitrate | | layer ! = 3 )
{
2008-04-08 01:11:36 +02:00
FIXME ( " Not a valid header: %02x:%02x:%02x:%02x \n " , header [ 0 ] , header [ 1 ] , header [ 2 ] , header [ 3 ] ) ;
2008-04-01 23:42:32 +02:00
return E_INVALIDARG ;
}
2008-03-21 06:33:47 +01:00
if ( layer = = 3 | | layer = = 2 )
length = 144 * bitrate / freqs [ freq_index ] + padding ;
else
length = 4 * ( 12 * bitrate / freqs [ freq_index ] + padding ) ;
duration = ( ULONGLONG ) 10000000 * ( ULONGLONG ) ( length ) / ( ULONGLONG ) ( bitrate / 8 ) ;
* plen = length ;
2008-04-22 22:57:11 +02:00
if ( pduration )
* pduration + = duration ;
2008-03-21 06:33:47 +01:00
return S_OK ;
}
2008-04-22 22:57:11 +02:00
static HRESULT FillBuffer ( MPEGSplitterImpl * This , IMediaSample * pCurrentSample )
2008-03-21 06:33:47 +01:00
{
Parser_OutputPin * pOutputPin = ( Parser_OutputPin * ) This - > Parser . ppPins [ 1 ] ;
LONGLONG length = 0 ;
2008-04-22 22:57:11 +02:00
LONGLONG pos = BYTES_FROM_MEDIATIME ( This - > Parser . pInputPin - > rtNext ) ;
LONGLONG time = This - > position ;
HRESULT hr ;
BYTE * fbuf = NULL ;
DWORD len = IMediaSample_GetActualDataLength ( pCurrentSample ) ;
2008-03-21 06:33:47 +01:00
2008-04-22 22:57:11 +02:00
TRACE ( " Source length: %u \n " , len ) ;
IMediaSample_GetPointer ( pCurrentSample , & fbuf ) ;
2008-03-21 06:33:47 +01:00
2008-04-22 22:57:11 +02:00
/* Find the next valid header.. it <SHOULD> be right here */
assert ( parse_header ( fbuf , & length , & This - > position ) = = S_OK ) ;
assert ( length = = len | | length + 4 = = len ) ;
IMediaSample_SetActualDataLength ( pCurrentSample , length ) ;
2008-03-21 06:33:47 +01:00
2008-04-30 23:34:07 +02:00
/* Queue the next sample */
2008-04-22 22:57:11 +02:00
if ( length + 4 = = len )
2008-03-21 06:33:47 +01:00
{
2008-04-22 22:57:11 +02:00
PullPin * pin = This - > Parser . pInputPin ;
LONGLONG stop = BYTES_FROM_MEDIATIME ( pin - > rtStop ) ;
2008-03-21 06:33:47 +01:00
2008-04-22 22:57:11 +02:00
hr = S_OK ;
memcpy ( This - > header , fbuf + length , 4 ) ;
while ( FAILED ( hr = parse_header ( This - > header , & length , NULL ) ) )
2008-03-21 06:33:47 +01:00
{
2008-04-22 22:57:11 +02:00
memmove ( This - > header , This - > header + 1 , 3 ) ;
if ( pos + 4 > = stop )
break ;
IAsyncReader_SyncRead ( pin - > pReader , + + pos , 1 , This - > header + 3 ) ;
2008-03-21 06:33:47 +01:00
}
2008-04-22 22:57:11 +02:00
pin - > rtNext = MEDIATIME_FROM_BYTES ( pos ) ;
2008-03-21 06:33:47 +01:00
if ( SUCCEEDED ( hr ) )
{
2008-04-22 22:57:11 +02:00
/* Remove 4 for the last header, which should hopefully work */
IMediaSample * sample = NULL ;
LONGLONG rtSampleStart = pin - > rtNext - MEDIATIME_FROM_BYTES ( 4 ) ;
LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES ( length + 4 ) ;
2008-03-21 06:33:47 +01:00
2008-04-22 22:57:11 +02:00
hr = IMemAllocator_GetBuffer ( pin - > pAlloc , & sample , NULL , NULL , 0 ) ;
2008-03-21 06:33:47 +01:00
2008-04-22 22:57:11 +02:00
if ( rtSampleStop > pin - > rtStop )
rtSampleStop = MEDIATIME_FROM_BYTES ( ALIGNUP ( BYTES_FROM_MEDIATIME ( pin - > rtStop ) , pin - > cbAlign ) ) ;
2008-03-21 06:33:47 +01:00
2008-04-22 22:57:11 +02:00
IMediaSample_SetTime ( sample , & rtSampleStart , & rtSampleStop ) ;
IMediaSample_SetPreroll ( sample , 0 ) ;
IMediaSample_SetDiscontinuity ( sample , 0 ) ;
IMediaSample_SetSyncPoint ( sample , 1 ) ;
pin - > rtCurrent = rtSampleStart ;
pin - > rtNext = rtSampleStop ;
2008-03-21 06:33:47 +01:00
2008-04-22 22:57:11 +02:00
if ( SUCCEEDED ( hr ) )
hr = IAsyncReader_Request ( pin - > pReader , sample , 0 ) ;
if ( FAILED ( hr ) )
FIXME ( " o_Ox%08x \n " , hr ) ;
}
2008-03-21 06:33:47 +01:00
}
2008-04-22 22:57:11 +02:00
/* If not, we're presumably at the end of file */
2008-03-21 06:33:47 +01:00
2008-04-22 22:57:11 +02:00
TRACE ( " Media time : %u.%03u \n " , ( DWORD ) ( This - > position / 10000000 ) , ( DWORD ) ( ( This - > position / 10000 ) % 1000 ) ) ;
2008-03-21 06:33:47 +01:00
2008-04-01 23:42:32 +02:00
IMediaSample_SetTime ( pCurrentSample , & time , & This - > position ) ;
2008-03-21 06:33:47 +01:00
2008-04-01 23:42:32 +02:00
hr = OutputPin_SendSample ( & pOutputPin - > pin , pCurrentSample ) ;
2008-04-08 01:11:36 +02:00
if ( hr ! = S_OK )
2008-04-01 23:42:32 +02:00
{
2008-04-08 01:11:36 +02:00
if ( hr ! = S_FALSE )
TRACE ( " Error sending sample (%x) \n " , hr ) ;
else
2008-04-22 22:57:11 +02:00
TRACE ( " S_FALSE (%d), holding \n " , IMediaSample_GetActualDataLength ( pCurrentSample ) ) ;
2008-04-01 23:42:32 +02:00
}
2008-04-08 01:11:36 +02:00
2008-03-21 06:33:47 +01:00
return hr ;
}
2007-04-24 14:28:14 +02:00
2008-04-19 04:30:25 +02:00
static HRESULT MPEGSplitter_process_sample ( LPVOID iface , IMediaSample * pSample , DWORD_PTR cookie )
2007-04-24 14:28:14 +02:00
{
MPEGSplitterImpl * This = ( MPEGSplitterImpl * ) iface ;
2008-03-21 06:33:47 +01:00
BYTE * pbSrcStream ;
2007-04-24 14:28:14 +02:00
DWORD cbSrcStream = 0 ;
2008-05-10 01:38:29 +02:00
REFERENCE_TIME tStart , tStop , tAviStart = This - > position ;
2007-04-24 14:28:14 +02:00
Parser_OutputPin * pOutputPin ;
2008-03-21 06:33:47 +01:00
HRESULT hr ;
2007-04-24 14:28:14 +02:00
pOutputPin = ( Parser_OutputPin * ) This - > Parser . ppPins [ 1 ] ;
hr = IMediaSample_GetTime ( pSample , & tStart , & tStop ) ;
if ( SUCCEEDED ( hr ) )
{
cbSrcStream = IMediaSample_GetActualDataLength ( pSample ) ;
hr = IMediaSample_GetPointer ( pSample , & pbSrcStream ) ;
}
2008-05-06 20:01:59 +02:00
/* Flush occurring */
2008-04-22 22:57:11 +02:00
if ( cbSrcStream = = 0 )
{
FIXME ( " .. Why do I need you? \n " ) ;
return S_OK ;
}
2007-04-24 14:28:14 +02:00
/* trace removed for performance reasons */
/* TRACE("(%p), %llu -> %llu\n", pSample, tStart, tStop); */
2008-03-21 06:33:47 +01:00
/* Now, try to find a new header */
2008-04-22 22:57:11 +02:00
hr = FillBuffer ( This , pSample ) ;
if ( hr ! = S_OK )
2007-04-24 14:28:14 +02:00
{
2008-04-22 22:57:11 +02:00
WARN ( " Failed with hres: %08x! \n " , hr ) ;
2008-04-09 21:07:14 +02:00
2008-04-30 23:34:07 +02:00
/* Unset progression if denied! */
if ( hr = = VFW_E_WRONG_STATE | | hr = = S_FALSE )
2008-03-28 21:10:20 +01:00
{
2008-04-30 23:34:07 +02:00
memcpy ( This - > header , pbSrcStream , 4 ) ;
This - > Parser . pInputPin - > rtCurrent = tStart ;
2008-05-10 01:38:29 +02:00
This - > position = tAviStart ;
2008-03-28 21:10:20 +01:00
}
2007-04-24 14:28:14 +02:00
}
2008-04-09 21:07:14 +02:00
if ( BYTES_FROM_MEDIATIME ( tStop ) > = This - > EndOfFile | | This - > position > = This - > Parser . mediaSeeking . llStop )
2007-04-24 14:28:14 +02:00
{
int i ;
2008-03-21 06:33:47 +01:00
TRACE ( " End of file reached \n " ) ;
2007-04-24 14:28:14 +02:00
for ( i = 0 ; i < This - > Parser . cStreams ; i + + )
{
IPin * ppin ;
hr = IPin_ConnectedTo ( This - > Parser . ppPins [ i + 1 ] , & ppin ) ;
if ( SUCCEEDED ( hr ) )
{
hr = IPin_EndOfStream ( ppin ) ;
IPin_Release ( ppin ) ;
}
if ( FAILED ( hr ) )
WARN ( " Error sending EndOfStream to pin %d (%x) \n " , i , hr ) ;
}
/* Force the pullpin thread to stop */
hr = S_FALSE ;
}
return hr ;
}
static HRESULT MPEGSplitter_query_accept ( LPVOID iface , const AM_MEDIA_TYPE * pmt )
{
if ( ! IsEqualIID ( & pmt - > majortype , & MEDIATYPE_Stream ) )
return S_FALSE ;
if ( IsEqualIID ( & pmt - > subtype , & MEDIASUBTYPE_MPEG1Audio ) )
return S_OK ;
if ( IsEqualIID ( & pmt - > subtype , & MEDIASUBTYPE_MPEG1Video ) )
FIXME ( " MPEG-1 video streams not yet supported. \n " ) ;
else if ( IsEqualIID ( & pmt - > subtype , & MEDIASUBTYPE_MPEG1System ) )
FIXME ( " MPEG-1 system streams not yet supported. \n " ) ;
else if ( IsEqualIID ( & pmt - > subtype , & MEDIASUBTYPE_MPEG1VideoCD ) )
FIXME ( " MPEG-1 VideoCD streams not yet supported. \n " ) ;
return S_FALSE ;
}
static HRESULT MPEGSplitter_init_audio ( MPEGSplitterImpl * This , const BYTE * header , PIN_INFO * ppiOutput , AM_MEDIA_TYPE * pamt )
{
WAVEFORMATEX * format ;
int bitrate_index ;
int freq_index ;
int mode_ext ;
int emphasis ;
int lsf = 1 ;
int mpeg1 ;
int layer ;
int mode ;
ZeroMemory ( pamt , sizeof ( * pamt ) ) ;
ppiOutput - > dir = PINDIR_OUTPUT ;
ppiOutput - > pFilter = ( IBaseFilter * ) This ;
wsprintfW ( ppiOutput - > achName , wszAudioStream ) ;
2008-03-12 21:32:07 +01:00
pamt - > formattype = FORMAT_WaveFormatEx ;
pamt - > majortype = MEDIATYPE_Audio ;
pamt - > subtype = MEDIASUBTYPE_MPEG1AudioPayload ;
2007-04-24 14:28:14 +02:00
pamt - > lSampleSize = 0 ;
2008-03-21 06:33:47 +01:00
pamt - > bFixedSizeSamples = FALSE ;
2007-04-24 14:28:14 +02:00
pamt - > bTemporalCompression = 0 ;
mpeg1 = ( header [ 1 ] > > 4 ) & 0x1 ;
if ( mpeg1 )
lsf = ( ( header [ 1 ] > > 3 ) & 0x1 ) ^ 1 ;
layer = 4 - ( ( header [ 1 ] > > 1 ) & 0x3 ) ;
bitrate_index = ( ( header [ 2 ] > > 4 ) & 0xf ) ;
freq_index = ( ( header [ 2 ] > > 2 ) & 0x3 ) + ( mpeg1 ? ( lsf * 3 ) : 6 ) ;
mode = ( ( header [ 3 ] > > 6 ) & 0x3 ) ;
mode_ext = ( ( header [ 3 ] > > 4 ) & 0x3 ) ;
emphasis = ( ( header [ 3 ] > > 0 ) & 0x3 ) ;
2008-03-21 06:33:47 +01:00
if ( ! bitrate_index )
{
/* Set to highest bitrate so samples will fit in for sure */
FIXME ( " Variable-bitrate audio not fully supported. \n " ) ;
bitrate_index = 15 ;
}
2007-04-24 14:28:14 +02:00
pamt - > cbFormat = ( ( layer = = 3 ) ? sizeof ( MPEGLAYER3WAVEFORMAT ) :
sizeof ( MPEG1WAVEFORMAT ) ) ;
pamt - > pbFormat = CoTaskMemAlloc ( pamt - > cbFormat ) ;
if ( ! pamt - > pbFormat )
return E_OUTOFMEMORY ;
ZeroMemory ( pamt - > pbFormat , pamt - > cbFormat ) ;
format = ( WAVEFORMATEX * ) pamt - > pbFormat ;
format - > wFormatTag = ( ( layer = = 3 ) ? WAVE_FORMAT_MPEGLAYER3 :
WAVE_FORMAT_MPEG ) ;
format - > nChannels = ( ( mode = = 3 ) ? 1 : 2 ) ;
format - > nSamplesPerSec = freqs [ freq_index ] ;
format - > nAvgBytesPerSec = tabsel_123 [ lsf ] [ layer - 1 ] [ bitrate_index ] * 1000 / 8 ;
if ( layer = = 3 )
format - > nBlockAlign = format - > nAvgBytesPerSec * 8 * 144 /
2008-03-21 06:33:47 +01:00
( format - > nSamplesPerSec < < lsf ) + 1 ;
2007-04-24 14:28:14 +02:00
else if ( layer = = 2 )
format - > nBlockAlign = format - > nAvgBytesPerSec * 8 * 144 /
2008-03-21 06:33:47 +01:00
format - > nSamplesPerSec + 1 ;
2007-04-24 14:28:14 +02:00
else
2008-03-21 06:33:47 +01:00
format - > nBlockAlign = 4 * ( format - > nAvgBytesPerSec * 8 * 12 / format - > nSamplesPerSec + 1 ) ;
2007-04-24 14:28:14 +02:00
format - > wBitsPerSample = 0 ;
if ( layer = = 3 )
{
MPEGLAYER3WAVEFORMAT * mp3format = ( MPEGLAYER3WAVEFORMAT * ) format ;
format - > cbSize = MPEGLAYER3_WFX_EXTRA_BYTES ;
mp3format - > wID = MPEGLAYER3_ID_MPEG ;
2008-03-21 06:33:47 +01:00
mp3format - > fdwFlags = MPEGLAYER3_FLAG_PADDING_ON ;
2007-04-24 14:28:14 +02:00
mp3format - > nBlockSize = format - > nBlockAlign ;
mp3format - > nFramesPerBlock = 1 ;
/* Beware the evil magic numbers. This struct is apparently horribly
* under - documented , and the only references I could find had it being
* set to this with no real explanation . It works fine though , so I ' m
* not complaining ( yet ) .
*/
mp3format - > nCodecDelay = 1393 ;
}
else
{
MPEG1WAVEFORMAT * mpgformat = ( MPEG1WAVEFORMAT * ) format ;
format - > cbSize = 22 ;
mpgformat - > fwHeadLayer = ( ( layer = = 1 ) ? ACM_MPEG_LAYER1 :
( ( layer = = 2 ) ? ACM_MPEG_LAYER2 :
ACM_MPEG_LAYER3 ) ) ;
mpgformat - > dwHeadBitrate = format - > nAvgBytesPerSec * 8 ;
mpgformat - > fwHeadMode = ( ( mode = = 3 ) ? ACM_MPEG_SINGLECHANNEL :
( ( mode = = 2 ) ? ACM_MPEG_DUALCHANNEL :
( ( mode = = 1 ) ? ACM_MPEG_JOINTSTEREO :
ACM_MPEG_STEREO ) ) ) ;
mpgformat - > fwHeadModeExt = ( ( mode = = 1 ) ? 0x0F : ( 1 < < mode_ext ) ) ;
mpgformat - > wHeadEmphasis = emphasis + 1 ;
mpgformat - > fwHeadFlags = ACM_MPEG_ID_MPEG1 ;
}
pamt - > subtype . Data1 = format - > wFormatTag ;
TRACE ( " MPEG audio stream detected: \n "
" \t Layer %d (%#x) \n "
" \t Frequency: %d \n "
" \t Channels: %d (%d) \n "
" \t BytesPerSec: %d \n " ,
layer , format - > wFormatTag , format - > nSamplesPerSec ,
format - > nChannels , mode , format - > nAvgBytesPerSec ) ;
dump_AM_MEDIA_TYPE ( pamt ) ;
return S_OK ;
}
2008-04-22 22:57:11 +02:00
static HRESULT MPEGSplitter_pre_connect ( IPin * iface , IPin * pConnectPin , ALLOCATOR_PROPERTIES * props )
2007-04-24 14:28:14 +02:00
{
PullPin * pPin = ( PullPin * ) iface ;
MPEGSplitterImpl * This = ( MPEGSplitterImpl * ) pPin - > pin . pinInfo . pFilter ;
HRESULT hr ;
LONGLONG pos = 0 ; /* in bytes */
2008-03-14 04:24:58 +01:00
BYTE header [ 10 ] ;
2007-04-24 14:28:14 +02:00
int streamtype = 0 ;
LONGLONG total , avail ;
AM_MEDIA_TYPE amt ;
PIN_INFO piOutput ;
IAsyncReader_Length ( pPin - > pReader , & total , & avail ) ;
This - > EndOfFile = total ;
2008-03-14 04:24:58 +01:00
hr = IAsyncReader_SyncRead ( pPin - > pReader , pos , 4 , header ) ;
2007-04-24 14:28:14 +02:00
if ( SUCCEEDED ( hr ) )
pos + = 4 ;
2008-03-14 04:24:58 +01:00
/* Skip ID3 v2 tag, if any */
if ( SUCCEEDED ( hr ) & & ! strncmp ( " ID3 " , ( char * ) header , 3 ) )
do {
UINT length ;
hr = IAsyncReader_SyncRead ( pPin - > pReader , pos , 6 , header + 4 ) ;
if ( FAILED ( hr ) )
break ;
pos + = 6 ;
TRACE ( " Found ID3 v2.%d.%d \n " , header [ 3 ] , header [ 4 ] ) ;
length = ( header [ 6 ] & 0x7F ) < < 21 ;
length + = ( header [ 7 ] & 0x7F ) < < 14 ;
length + = ( header [ 8 ] & 0x7F ) < < 7 ;
length + = ( header [ 9 ] & 0x7F ) ;
TRACE ( " Length: %u \n " , length ) ;
pos + = length ;
/* Read the real header for the mpeg splitter */
hr = IAsyncReader_SyncRead ( pPin - > pReader , pos , 4 , header ) ;
if ( SUCCEEDED ( hr ) )
pos + = 4 ;
TRACE ( " %x:%x:%x:%x \n " , header [ 0 ] , header [ 1 ] , header [ 2 ] , header [ 3 ] ) ;
} while ( 0 ) ;
2007-04-24 14:28:14 +02:00
while ( SUCCEEDED ( hr ) & & ! ( streamtype = MPEGSplitter_head_check ( header ) ) )
{
2008-03-14 04:24:58 +01:00
TRACE ( " %x:%x:%x:%x \n " , header [ 0 ] , header [ 1 ] , header [ 2 ] , header [ 3 ] ) ;
2007-04-24 14:28:14 +02:00
/* No valid header yet; shift by a byte and check again */
2008-03-21 06:33:47 +01:00
memmove ( header , header + 1 , 3 ) ;
2008-03-14 04:24:58 +01:00
hr = IAsyncReader_SyncRead ( pPin - > pReader , pos + + , 1 , header + 3 ) ;
2007-04-24 14:28:14 +02:00
}
if ( FAILED ( hr ) )
return hr ;
2008-03-21 06:33:47 +01:00
pos - = 4 ;
2008-04-22 22:57:11 +02:00
This - > begin_offset = pos ;
memcpy ( This - > header , header , 4 ) ;
2007-04-24 14:28:14 +02:00
2008-04-08 21:28:09 +02:00
This - > seektable [ 0 ] . bytepos = pos ;
This - > seektable [ 0 ] . timepos = 0 ;
2007-04-24 14:28:14 +02:00
switch ( streamtype )
{
case MPEG_AUDIO_HEADER :
2008-03-21 06:33:47 +01:00
{
LONGLONG duration = 0 ;
2008-04-08 21:28:09 +02:00
DWORD last_entry = 0 ;
2008-03-21 06:33:47 +01:00
DWORD ticks = GetTickCount ( ) ;
2007-04-24 14:28:14 +02:00
hr = MPEGSplitter_init_audio ( This , header , & piOutput , & amt ) ;
if ( SUCCEEDED ( hr ) )
{
WAVEFORMATEX * format = ( WAVEFORMATEX * ) amt . pbFormat ;
2008-04-22 22:57:11 +02:00
props - > cbAlign = 1 ;
props - > cbPrefix = 0 ;
2007-04-24 14:28:14 +02:00
/* Make the output buffer a multiple of the frame size */
2008-04-22 22:57:11 +02:00
props - > cbBuffer = 0x4000 / format - > nBlockAlign *
2007-04-24 14:28:14 +02:00
format - > nBlockAlign ;
2008-04-30 23:34:07 +02:00
props - > cBuffers = 3 ;
2008-04-22 22:57:11 +02:00
hr = Parser_AddPin ( & ( This - > Parser ) , & piOutput , props , & amt ) ;
2007-04-24 14:28:14 +02:00
}
if ( FAILED ( hr ) )
{
if ( amt . pbFormat )
CoTaskMemFree ( amt . pbFormat ) ;
ERR ( " Could not create pin for MPEG audio stream (%x) \n " , hr ) ;
2008-03-21 06:33:47 +01:00
break ;
}
/* Check for idv1 tag, and remove it from stream if found */
hr = IAsyncReader_SyncRead ( pPin - > pReader , This - > EndOfFile - 128 , 3 , header + 4 ) ;
if ( FAILED ( hr ) )
break ;
if ( ! strncmp ( ( char * ) header + 4 , " TAG " , 3 ) )
This - > EndOfFile - = 128 ;
2008-04-08 01:11:36 +02:00
This - > Parser . pInputPin - > rtStop = MEDIATIME_FROM_BYTES ( This - > EndOfFile ) ;
2008-04-22 22:57:11 +02:00
This - > Parser . pInputPin - > rtStart = This - > Parser . pInputPin - > rtCurrent = MEDIATIME_FROM_BYTES ( This - > begin_offset ) ;
2008-03-21 06:33:47 +01:00
2008-04-11 04:17:14 +02:00
/* http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm has a whole read up on audio headers */
2008-03-31 06:18:49 +02:00
while ( pos + 3 < This - > EndOfFile )
2008-03-21 06:33:47 +01:00
{
LONGLONG length = 0 ;
2008-03-28 21:10:20 +01:00
hr = IAsyncReader_SyncRead ( pPin - > pReader , pos , 4 , header ) ;
2008-03-31 06:18:49 +02:00
if ( hr ! = S_OK )
break ;
2008-03-21 06:33:47 +01:00
while ( parse_header ( header , & length , & duration ) )
{
/* No valid header yet; shift by a byte and check again */
memmove ( header , header + 1 , 3 ) ;
hr = IAsyncReader_SyncRead ( pPin - > pReader , pos + + , 1 , header + 3 ) ;
2008-03-31 06:18:49 +02:00
if ( hr ! = S_OK | | This - > EndOfFile - pos < 4 )
2008-03-21 06:33:47 +01:00
break ;
}
2008-03-28 21:10:20 +01:00
pos + = length ;
2008-04-08 21:28:09 +02:00
if ( This - > seektable & & ( duration / SEEK_INTERVAL ) > last_entry )
{
if ( last_entry + 1 > duration / SEEK_INTERVAL )
{
ERR ( " Somehow skipped %d interval lengths instead of 1 \n " , ( DWORD ) ( duration / SEEK_INTERVAL ) - ( last_entry + 1 ) ) ;
}
+ + last_entry ;
TRACE ( " Entry: %u \n " , last_entry ) ;
if ( last_entry > = This - > seek_entries )
{
This - > seek_entries + = 64 ;
This - > seektable = CoTaskMemRealloc ( This - > seektable , ( This - > seek_entries ) * sizeof ( struct seek_entry ) ) ;
}
This - > seektable [ last_entry ] . bytepos = pos ;
This - > seektable [ last_entry ] . timepos = duration ;
}
2008-03-28 21:10:20 +01:00
TRACE ( " Pos: %x%08x/%x%08x \n " , ( DWORD ) ( pos > > 32 ) , ( DWORD ) pos , ( DWORD ) ( This - > EndOfFile > > 32 ) , ( DWORD ) This - > EndOfFile ) ;
2007-04-24 14:28:14 +02:00
}
2008-03-21 06:33:47 +01:00
hr = S_OK ;
2008-03-26 18:01:09 +01:00
TRACE ( " Duration: %d seconds \n " , ( DWORD ) ( duration / 10000000 ) ) ;
2008-03-21 06:33:47 +01:00
TRACE ( " Parsing took %u ms \n " , GetTickCount ( ) - ticks ) ;
2008-04-01 23:42:32 +02:00
This - > duration = duration ;
2008-04-09 21:07:14 +02:00
This - > Parser . mediaSeeking . llCurrent = 0 ;
This - > Parser . mediaSeeking . llDuration = duration ;
This - > Parser . mediaSeeking . llStop = duration ;
2007-04-24 14:28:14 +02:00
break ;
2008-03-21 06:33:47 +01:00
}
2007-04-24 14:28:14 +02:00
case MPEG_VIDEO_HEADER :
FIXME ( " MPEG video processing not yet supported! \n " ) ;
hr = E_FAIL ;
break ;
case MPEG_SYSTEM_HEADER :
FIXME ( " MPEG system streams not yet supported! \n " ) ;
hr = E_FAIL ;
break ;
default :
break ;
}
2008-03-21 06:33:47 +01:00
This - > position = 0 ;
2007-04-24 14:28:14 +02:00
return hr ;
}
static HRESULT MPEGSplitter_cleanup ( LPVOID iface )
{
MPEGSplitterImpl * This = ( MPEGSplitterImpl * ) iface ;
2008-04-30 23:34:07 +02:00
TRACE ( " (%p) \n " , This ) ;
2007-04-24 14:28:14 +02:00
return S_OK ;
}
2008-04-01 23:43:09 +02:00
static HRESULT MPEGSplitter_seek ( IBaseFilter * iface )
{
MPEGSplitterImpl * This = ( MPEGSplitterImpl * ) iface ;
PullPin * pPin = This - > Parser . pInputPin ;
LONGLONG newpos , timepos , bytepos ;
HRESULT hr = S_OK ;
BYTE header [ 4 ] ;
newpos = This - > Parser . mediaSeeking . llCurrent ;
if ( newpos > This - > duration )
{
2008-04-08 01:11:36 +02:00
WARN ( " Requesting position %x%08x beyond end of stream %x%08x \n " , ( DWORD ) ( newpos > > 32 ) , ( DWORD ) newpos , ( DWORD ) ( This - > duration > > 32 ) , ( DWORD ) This - > duration ) ;
2008-04-01 23:43:09 +02:00
return E_INVALIDARG ;
}
if ( This - > position / 1000000 = = newpos / 1000000 )
{
2008-04-08 01:11:36 +02:00
TRACE ( " Requesting position %x%08x same as current position %x%08x \n " , ( DWORD ) ( newpos > > 32 ) , ( DWORD ) newpos , ( DWORD ) ( This - > position > > 32 ) , ( DWORD ) This - > position ) ;
2008-04-01 23:43:09 +02:00
return S_OK ;
}
2008-04-08 21:28:09 +02:00
/* Position, cached */
bytepos = This - > seektable [ newpos / SEEK_INTERVAL ] . bytepos ;
timepos = This - > seektable [ newpos / SEEK_INTERVAL ] . timepos ;
2008-04-01 23:43:09 +02:00
hr = IAsyncReader_SyncRead ( pPin - > pReader , bytepos , 4 , header ) ;
2008-04-22 22:57:11 +02:00
while ( bytepos + 3 < This - > EndOfFile )
2008-04-01 23:43:09 +02:00
{
LONGLONG length = 0 ;
hr = IAsyncReader_SyncRead ( pPin - > pReader , bytepos , 4 , header ) ;
2008-04-22 22:57:11 +02:00
if ( hr ! = S_OK | | timepos > = newpos )
2008-04-08 21:28:09 +02:00
break ;
while ( parse_header ( header , & length , & timepos ) & & bytepos + 3 < This - > EndOfFile )
2008-04-01 23:43:09 +02:00
{
/* No valid header yet; shift by a byte and check again */
memmove ( header , header + 1 , 3 ) ;
hr = IAsyncReader_SyncRead ( pPin - > pReader , + + bytepos , 1 , header + 3 ) ;
2008-04-08 21:28:09 +02:00
if ( hr ! = S_OK )
2008-04-01 23:43:09 +02:00
break ;
}
bytepos + = length ;
TRACE ( " Pos: %x%08x/%x%08x \n " , ( DWORD ) ( bytepos > > 32 ) , ( DWORD ) bytepos , ( DWORD ) ( This - > EndOfFile > > 32 ) , ( DWORD ) This - > EndOfFile ) ;
}
2008-04-08 01:11:36 +02:00
2008-04-01 23:43:09 +02:00
if ( SUCCEEDED ( hr ) )
{
PullPin * pin = This - > Parser . pInputPin ;
2008-04-08 01:11:36 +02:00
IPin * victim = NULL ;
2008-04-01 23:43:09 +02:00
2008-04-08 01:11:36 +02:00
TRACE ( " Moving sound to %08u bytes! \n " , ( DWORD ) bytepos ) ;
2008-04-01 23:43:09 +02:00
2008-04-09 21:07:14 +02:00
EnterCriticalSection ( & pin - > thread_lock ) ;
2008-04-01 23:43:09 +02:00
IPin_BeginFlush ( ( IPin * ) pin ) ;
2008-04-08 01:11:36 +02:00
/* Make sure this is done while stopped, BeginFlush takes care of this */
2008-04-01 23:43:09 +02:00
EnterCriticalSection ( & This - > Parser . csFilter ) ;
2008-04-22 22:57:11 +02:00
memcpy ( This - > header , header , 4 ) ;
2008-04-08 01:11:36 +02:00
IPin_ConnectedTo ( This - > Parser . ppPins [ 1 ] , & victim ) ;
if ( victim )
{
IPin_NewSegment ( victim , newpos , This - > duration , pin - > dRate ) ;
IPin_Release ( victim ) ;
}
2008-04-01 23:43:09 +02:00
pin - > rtStart = pin - > rtCurrent = MEDIATIME_FROM_BYTES ( bytepos ) ;
pin - > rtStop = MEDIATIME_FROM_BYTES ( ( REFERENCE_TIME ) This - > EndOfFile ) ;
This - > seek = TRUE ;
This - > position = newpos ;
LeaveCriticalSection ( & This - > Parser . csFilter ) ;
2008-04-08 01:11:36 +02:00
TRACE ( " Done flushing \n " ) ;
IPin_EndFlush ( ( IPin * ) pin ) ;
2008-04-09 21:07:14 +02:00
LeaveCriticalSection ( & pin - > thread_lock ) ;
2008-04-01 23:43:09 +02:00
}
return hr ;
}
2008-04-01 23:42:32 +02:00
2008-04-22 22:57:11 +02:00
static HRESULT MPEGSplitter_disconnect ( LPVOID iface )
2008-04-16 05:39:36 +02:00
{
/* TODO: Find memory leaks etc */
return S_OK ;
}
2008-04-22 22:57:11 +02:00
static HRESULT MPEGSplitter_first_request ( LPVOID iface )
{
MPEGSplitterImpl * This = ( MPEGSplitterImpl * ) iface ;
PullPin * pin = This - > Parser . pInputPin ;
HRESULT hr ;
LONGLONG length ;
IMediaSample * sample ;
TRACE ( " Seeking? %d \n " , This - > seek ) ;
assert ( parse_header ( This - > header , & length , NULL ) = = S_OK ) ;
if ( pin - > rtCurrent > = pin - > rtStop )
{
/* Last sample has already been queued, request nothing more */
FIXME ( " Done! \n " ) ;
return S_OK ;
}
hr = IMemAllocator_GetBuffer ( pin - > pAlloc , & sample , NULL , NULL , 0 ) ;
pin - > rtNext = pin - > rtCurrent ;
if ( SUCCEEDED ( hr ) )
{
LONGLONG rtSampleStart = pin - > rtNext ;
/* Add 4 for the next header, which should hopefully work */
LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES ( length + 4 ) ;
if ( rtSampleStop > pin - > rtStop )
rtSampleStop = MEDIATIME_FROM_BYTES ( ALIGNUP ( BYTES_FROM_MEDIATIME ( pin - > rtStop ) , pin - > cbAlign ) ) ;
hr = IMediaSample_SetTime ( sample , & rtSampleStart , & rtSampleStop ) ;
pin - > rtCurrent = pin - > rtNext ;
pin - > rtNext = rtSampleStop ;
IMediaSample_SetPreroll ( sample , FALSE ) ;
IMediaSample_SetDiscontinuity ( sample , This - > seek ) ;
IMediaSample_SetSyncPoint ( sample , 1 ) ;
This - > seek = 0 ;
hr = IAsyncReader_Request ( pin - > pReader , sample , 0 ) ;
}
if ( FAILED ( hr ) )
ERR ( " Horsemen of the apocalypse came to bring error 0x%08x \n " , hr ) ;
return hr ;
}
2008-04-23 20:23:43 +02:00
static const IBaseFilterVtbl MPEGSplitter_Vtbl =
{
Parser_QueryInterface ,
Parser_AddRef ,
Parser_Release ,
Parser_GetClassID ,
Parser_Stop ,
Parser_Pause ,
Parser_Run ,
Parser_GetState ,
Parser_SetSyncSource ,
Parser_GetSyncSource ,
Parser_EnumPins ,
Parser_FindPin ,
Parser_QueryFilterInfo ,
Parser_JoinFilterGraph ,
Parser_QueryVendorInfo
} ;
2007-04-24 14:28:14 +02:00
HRESULT MPEGSplitter_create ( IUnknown * pUnkOuter , LPVOID * ppv )
{
MPEGSplitterImpl * This ;
HRESULT hr = E_FAIL ;
TRACE ( " (%p, %p) \n " , pUnkOuter , ppv ) ;
* ppv = NULL ;
if ( pUnkOuter )
return CLASS_E_NOAGGREGATION ;
This = CoTaskMemAlloc ( sizeof ( MPEGSplitterImpl ) ) ;
if ( ! This )
return E_OUTOFMEMORY ;
ZeroMemory ( This , sizeof ( MPEGSplitterImpl ) ) ;
2008-04-08 21:28:09 +02:00
This - > seektable = CoTaskMemAlloc ( sizeof ( struct seek_entry ) * 64 ) ;
if ( ! This - > seektable )
{
CoTaskMemFree ( This ) ;
return E_OUTOFMEMORY ;
}
This - > seek_entries = 64 ;
2008-04-25 23:59:05 +02:00
hr = Parser_Create ( & ( This - > Parser ) , & MPEGSplitter_Vtbl , & CLSID_MPEG1Splitter , MPEGSplitter_process_sample , MPEGSplitter_query_accept , MPEGSplitter_pre_connect , MPEGSplitter_cleanup , MPEGSplitter_disconnect , MPEGSplitter_first_request , NULL , NULL , MPEGSplitter_seek , NULL ) ;
2007-04-24 14:28:14 +02:00
if ( FAILED ( hr ) )
{
CoTaskMemFree ( This ) ;
return hr ;
}
2008-04-22 22:57:11 +02:00
This - > seek = 1 ;
2007-04-24 14:28:14 +02:00
/* Note: This memory is managed by the parser filter once created */
* ppv = ( LPVOID ) This ;
return hr ;
}