quartz: Add a libavformat based MPEG demuxer.
Henri Verbeet
hverbeet at codeweavers.com
Mon Mar 23 03:42:27 CDT 2009
---
configure.ac | 21 +
dlls/quartz/mpegsplit.c | 1097 +++++++++++++++++++++--------------------------
2 files changed, 514 insertions(+), 604 deletions(-)
diff --git a/configure.ac b/configure.ac
index 3c2463c..a31f831 100644
--- a/configure.ac
+++ b/configure.ac
@@ -23,6 +23,8 @@ AC_ARG_WITH(alsa, AS_HELP_STRING([--without-alsa],[do not use the Alsa soun
[if test "x$withval" = "xno"; then ac_cv_header_sys_asoundlib_h=no; ac_cv_header_alsa_asoundlib_h=no; fi])
AC_ARG_WITH(audioio, AS_HELP_STRING([--without-audioio],[do not use the AudioIO sound support]),
[if test "x$withval" = "xno"; then ac_cv_header_libaudioio_h=no; fi])
+AC_ARG_WITH(avformat, AS_HELP_STRING([--without-avformat], [do not use avformat (MPEG demuxer support)]))
+AC_ARG_WITH(avutil, AS_HELP_STRING([--without-avutil], [do not use avutil (MPEG demuxer support)]))
AC_ARG_WITH(capi, AS_HELP_STRING([--without-capi],[do not use CAPI (ISDN support)]),
[if test "x$withval" = "xno"; then ac_cv_header_capi20_h=no; ac_cv_header_linux_capi_h=no; fi])
AC_ARG_WITH(cms, AS_HELP_STRING([--without-cms],[do not use CMS (color management support)]),
@@ -997,6 +999,25 @@ fi
WINE_NOTICE_WITH(gnutls,[test "x$ac_cv_lib_soname_gnutls" = "x"],
[libgnutls ${notice_platform}development files not found, no schannel support.])
+dnl **** Check for libavutil
+if test "x$with_avutil" != "xno"
+then
+ AC_CHECK_HEADER(libavutil/avutil.h,
+ [WINE_CHECK_SONAME(avutil,avutil_version)])
+fi
+WINE_NOTICE_WITH(avutil,[test "x$ac_cv_lib_soname_avutil" = "x"],
+ [libavutil ${notice_platform}development files not found, no MPEG demuxer support.])
+
+dnl **** Check for libavformat
+if test "x$with_avformat" != "xno"
+then
+ AC_CHECK_HEADER(libavformat/avformat.h,
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libavformat/avformat.h>]], [[typeof(av_read_frame) *pfunc;]])],
+ [WINE_CHECK_SONAME(avformat,avformat_version)])])
+fi
+WINE_NOTICE_WITH(avformat,[test "x$ac_cv_lib_soname_avformat" = "x"],
+ [libavformat ${notice_platform}development files not found, no MPEG demuxer support.])
+
dnl **** Check which curses lib to use ***
CURSESLIBS=""
if test "$ac_cv_header_ncurses_h" = "yes"
diff --git a/dlls/quartz/mpegsplit.c b/dlls/quartz/mpegsplit.c
index a97a8c9..1efdb1a 100644
--- a/dlls/quartz/mpegsplit.c
+++ b/dlls/quartz/mpegsplit.c
@@ -5,6 +5,7 @@
* Copyright 2004-2005 Christian Costa
* Copyright 2007 Chris Robinson
* Copyright 2008 Maarten Lankhorst
+ * Copyright 2009 Henri Verbeet for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -21,754 +22,576 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#include <assert.h>
-#include <math.h>
+#include "config.h"
+#include "wine/port.h"
+#if defined SONAME_LIBAVFORMAT && defined SONAME_LIBAVUTIL
+#include <libavformat/avformat.h>
+#endif
+
+#include "wine/debug.h"
+#include "wine/library.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
+#if defined SONAME_LIBAVFORMAT && defined SONAME_LIBAVUTIL
+
+static void *libavformat_handle;
+static void *libavutil_handle;
+#define MAKE_FUNCPTR(f) static typeof(f) * p##f
+MAKE_FUNCPTR(av_close_input_file);
+MAKE_FUNCPTR(av_find_stream_info);
+MAKE_FUNCPTR(av_free);
+MAKE_FUNCPTR(av_malloc);
+MAKE_FUNCPTR(av_open_input_file);
+MAKE_FUNCPTR(av_read_frame);
+MAKE_FUNCPTR(av_register_all);
+MAKE_FUNCPTR(av_seek_frame);
+MAKE_FUNCPTR(register_protocol);
+#undef MAKE_FUNCPTR
-#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
-
-#define SEEK_INTERVAL (ULONGLONG)(10 * 10000000) /* Add an entry every 10 seconds */
-
-struct seek_entry {
- ULONGLONG bytepos;
- ULONGLONG timepos;
-};
+static const WCHAR wszAudioStream[] = {'A','u','d','i','o',0};
+static const WCHAR wszVideoStream[] = {'V','i','d','e','o',0};
-typedef struct MPEGSplitterImpl
+struct quartz_context
{
- ParserImpl Parser;
- LONGLONG EndOfFile;
- LONGLONG duration;
- LONGLONG position;
- DWORD begin_offset;
- BYTE header[4];
-
- /* Whether we just seeked (or started playing) */
- BOOL seek;
-
- /* Seeking cache */
- ULONG seek_entries;
- struct seek_entry *seektable;
-} MPEGSplitterImpl;
+ PullPin *pin;
+ LONGLONG pos;
+ LONGLONG len;
+};
-static int MPEGSplitter_head_check(const BYTE *header)
+static int quartz_open(URLContext *ctx, const char *url, int flags)
{
- /* 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;
- }
+ struct quartz_context *qc;
+ LONGLONG avail;
- /* 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;
+ TRACE("ctx %p, url %s, flags %#x\n", ctx, url, flags);
- /* Nothing yet.. */
- return MPEG_NO_HEADER;
-}
+ qc = pav_malloc(sizeof(*qc));
+ if (!qc) return AVERROR(ENOMEM);
+ ctx->priv_data = qc;
-static const WCHAR wszAudioStream[] = {'A','u','d','i','o',0};
-static const WCHAR wszVideoStream[] = {'V','i','d','e','o',0};
+ qc->pos = 0;
+ qc->pin = NULL;
+ sscanf(url, "quartz://%p", &qc->pin);
-static const DWORD freqs[10] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000, 0 };
+ TRACE("pin %p\n", qc->pin);
-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,} },
+ IAsyncReader_Length(qc->pin->pReader, &qc->len, &avail);
- { {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,} }
-};
+ return 0;
+}
-static HRESULT parse_header(BYTE *header, LONGLONG *plen, LONGLONG *pduration)
+static int quartz_read(URLContext *ctx, unsigned char *data, int data_size)
{
- LONGLONG duration;
-
- int bitrate_index, freq_index, lsf = 1, mpeg1, layer, padding, bitrate, length;
-
- if (!(header[0] == 0xff && ((header[1]>>5)&0x7) == 0x7 &&
- ((header[1]>>1)&0x3) != 0 && ((header[2]>>4)&0xf) != 0xf &&
- ((header[2]>>2)&0x3) != 0x3))
- {
- FIXME("Not a valid header: %02x:%02x\n", header[0], header[1]);
- return E_INVALIDARG;
- }
+ struct quartz_context *qc = ctx->priv_data;
+ HRESULT hr;
- mpeg1 = (header[1]>>4)&0x1;
- if (mpeg1)
- lsf = ((header[1]>>3)&0x1)^1;
+ TRACE("ctx %p, data %p, data_size %d\n", ctx, data, data_size);
- 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);
+ if (data_size > qc->len - qc->pos) data_size = qc->len - qc->pos;
- bitrate = tabsel_123[lsf][layer-1][bitrate_index] * 1000;
- if (!bitrate || layer != 3)
+ hr = IAsyncReader_SyncRead(qc->pin->pReader, qc->pos, data_size, data);
+ if (FAILED(hr))
{
- FIXME("Not a valid header: %02x:%02x:%02x:%02x\n", header[0], header[1], header[2], header[3]);
- return E_INVALIDARG;
+ ERR("Failed to read data.\n");
+ return AVERROR(EIO);
}
+ TRACE("read %d bytes @ pos %s, len %s\n", data_size,
+ wine_dbgstr_longlong(qc->pos), wine_dbgstr_longlong(qc->len));
- if (layer == 3 || layer == 2)
- length = 144 * bitrate / freqs[freq_index] + padding;
- else
- length = 4 * (12 * bitrate / freqs[freq_index] + padding);
+ qc->pos += data_size;
- duration = (ULONGLONG)10000000 * (ULONGLONG)(length) / (ULONGLONG)(bitrate/8);
- *plen = length;
- if (pduration)
- *pduration += duration;
- return S_OK;
+ return data_size;
}
-static HRESULT FillBuffer(MPEGSplitterImpl *This, IMediaSample *pCurrentSample)
+static int64_t quartz_seek(URLContext *ctx, int64_t pos, int whence)
{
- Parser_OutputPin * pOutputPin = (Parser_OutputPin*)This->Parser.ppPins[1];
- LONGLONG length = 0;
- LONGLONG pos = BYTES_FROM_MEDIATIME(This->Parser.pInputPin->rtNext);
- LONGLONG time = This->position;
- HRESULT hr;
- BYTE *fbuf = NULL;
- DWORD len = IMediaSample_GetActualDataLength(pCurrentSample);
+ struct quartz_context *qc = ctx->priv_data;
- TRACE("Source length: %u\n", len);
- IMediaSample_GetPointer(pCurrentSample, &fbuf);
+ TRACE("ctx %p, pos %s, whence %#x\n", ctx, wine_dbgstr_longlong(pos), whence);
- /* 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);
-
- /* Queue the next sample */
- if (length + 4 == len)
+ switch(whence)
{
- PullPin *pin = This->Parser.pInputPin;
- LONGLONG stop = BYTES_FROM_MEDIATIME(pin->rtStop);
+ case SEEK_SET:
+ return qc->pos = pos;
- hr = S_OK;
- memcpy(This->header, fbuf + length, 4);
- while (FAILED(hr = parse_header(This->header, &length, NULL)))
- {
- memmove(This->header, This->header+1, 3);
- if (pos + 4 >= stop)
- break;
- IAsyncReader_SyncRead(pin->pReader, ++pos, 1, This->header + 3);
- }
- pin->rtNext = MEDIATIME_FROM_BYTES(pos);
-
- if (SUCCEEDED(hr))
- {
- /* 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);
-
- hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0);
+ case SEEK_CUR:
+ return qc->pos += pos;
- if (rtSampleStop > pin->rtStop)
- rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign));
+ case SEEK_END:
+ return qc->pos = qc->len - pos;
- IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop);
- IMediaSample_SetPreroll(sample, 0);
- IMediaSample_SetDiscontinuity(sample, 0);
- IMediaSample_SetSyncPoint(sample, 1);
- pin->rtCurrent = rtSampleStart;
- pin->rtNext = rtSampleStop;
+ case AVSEEK_SIZE:
+ return qc->len;
- if (SUCCEEDED(hr))
- hr = IAsyncReader_Request(pin->pReader, sample, 0);
- if (FAILED(hr))
- FIXME("o_Ox%08x\n", hr);
- }
+ default:
+ FIXME("Unhandled whence %#x\n", whence);
+ return -1;
}
- /* If not, we're presumably at the end of file */
-
- TRACE("Media time : %u.%03u\n", (DWORD)(This->position/10000000), (DWORD)((This->position/10000)%1000));
+}
- IMediaSample_SetTime(pCurrentSample, &time, &This->position);
+static int quartz_close(URLContext *ctx)
+{
+ struct quartz_context *qc = ctx->priv_data;
- hr = OutputPin_SendSample(&pOutputPin->pin, pCurrentSample);
+ TRACE("ctx %p\n", ctx);
- if (hr != S_OK)
- {
- if (hr != S_FALSE)
- TRACE("Error sending sample (%x)\n", hr);
- else
- TRACE("S_FALSE (%d), holding\n", IMediaSample_GetActualDataLength(pCurrentSample));
- }
+ pav_free(qc);
- return hr;
+ return 0;
}
+static URLProtocol quartz_prot =
+{
+ "quartz",
+ quartz_open,
+ quartz_read,
+ NULL,
+ quartz_seek,
+ quartz_close,
+};
-static HRESULT MPEGSplitter_process_sample(LPVOID iface)
+struct MPEGSplitterImpl
{
- MPEGSplitterImpl *This = iface;
- BYTE *pbSrcStream;
- DWORD cbSrcStream = 0;
- REFERENCE_TIME tStart, tStop, tAviStart = This->position;
- Parser_OutputPin * pOutputPin;
- IMediaSample *pSample;
- DWORD_PTR cookie;
- HRESULT hr;
+ ParserImpl Parser;
- hr = IAsyncReader_WaitForNext(This->Parser.pInputPin->pReader, 10000, &pSample, &cookie);
- if (FAILED(hr))
- {
- ERR("Processing error: %x\n", hr);
- if (hr == VFW_E_TIMEOUT)
- {
- assert(!pSample);
- return S_OK;
- }
- return hr;
- }
+ LONGLONG duration;
+ LONGLONG position;
+ BOOL discontinuity;
- pOutputPin = (Parser_OutputPin*)This->Parser.ppPins[1];
+ AVFormatContext *format_ctx;
+ AVPacket packet;
+ BOOL packet_held;
+};
- hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
- if (SUCCEEDED(hr))
- {
- cbSrcStream = IMediaSample_GetActualDataLength(pSample);
- hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
- }
+static HRESULT MPEGSplitter_process_sample(LPVOID iface)
+{
+ struct MPEGSplitterImpl *This = iface;
+ PullPin *input_pin = This->Parser.pInputPin;
+ Parser_OutputPin *output_pin;
+ REFERENCE_TIME sample_start, sample_stop;
+ IMediaSample *dst_sample;
+ AVRational time_base;
+ BYTE *dst_sample_data;
+ LONG dst_sample_size;
+ HRESULT hr;
+ int ret;
- /* Flush occurring */
- if (cbSrcStream == 0)
+ if (This->packet_held)
{
- FIXME(".. Why do I need you?\n");
- IMediaSample_Release(pSample);
- return S_OK;
+ TRACE("Using held packet\n");
+ This->packet_held = FALSE;
+ ret = 0;
}
-
- /* trace removed for performance reasons */
- /* TRACE("(%p), %llu -> %llu\n", pSample, tStart, tStop); */
-
- /* Now, try to find a new header */
- hr = FillBuffer(This, pSample);
- IMediaSample_Release(pSample);
- if (hr != S_OK)
+ else
{
- WARN("Failed with hres: %08x!\n", hr);
-
- /* Unset progression if denied! */
- if (hr == VFW_E_WRONG_STATE || hr == S_FALSE)
- {
- memcpy(This->header, pbSrcStream, 4);
- This->Parser.pInputPin->rtCurrent = tStart;
- This->position = tAviStart;
- }
+ ret = pav_read_frame(This->format_ctx, &This->packet);
}
- if (BYTES_FROM_MEDIATIME(tStop) >= This->EndOfFile || This->position >= This->Parser.mediaSeeking.llStop)
+ if (ret < 0 || (This->position >= This->Parser.mediaSeeking.llStop && This->Parser.mediaSeeking.llStop != -1))
{
unsigned int i;
- TRACE("End of file reached\n");
+ if (ret < 0) ERR("Failed to read a frame, %d\n", ret);
+ else av_free_packet(&This->packet);
- for (i = 0; i < This->Parser.cStreams; i++)
+ for (i = 0; i < This->Parser.cStreams; ++i)
{
- IPin* ppin;
+ IPin *pin;
- hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin);
+ hr = IPin_ConnectedTo(This->Parser.ppPins[i + 1], &pin);
if (SUCCEEDED(hr))
{
- hr = IPin_EndOfStream(ppin);
- IPin_Release(ppin);
+ hr = IPin_EndOfStream(pin);
+ IPin_Release(pin);
}
- if (FAILED(hr))
- WARN("Error sending EndOfStream to pin %u (%x)\n", i, hr);
+ if (FAILED(hr)) ERR("Error %#x sending EndOfStream to pin %u\n", hr, i);
}
/* 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");
+ output_pin = (Parser_OutputPin *)This->Parser.ppPins[This->packet.stream_index + 1];
+ time_base = This->format_ctx->streams[This->packet.stream_index]->time_base;
- return S_FALSE;
-}
+ hr = OutputPin_GetDeliveryBuffer(&output_pin->pin, &dst_sample, NULL, NULL, 0);
+ if (FAILED(hr))
+ {
+ ERR("Unable to get delivery buffer, %#x\n", hr);
+ goto fail;
+ }
+ dst_sample_size = IMediaSample_GetSize(dst_sample);
+ if (dst_sample_size < This->packet.size)
+ {
+ ERR("Sample size is too small %d < %d\n", dst_sample_size, This->packet.size);
+ hr = E_FAIL;
+ goto fail;
+ }
-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);
-
- pamt->formattype = FORMAT_WaveFormatEx;
- pamt->majortype = MEDIATYPE_Audio;
- pamt->subtype = MEDIASUBTYPE_MPEG1AudioPayload;
-
- pamt->lSampleSize = 0;
- pamt->bFixedSizeSamples = FALSE;
- 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);
-
- if (!bitrate_index)
+ hr = IMediaSample_GetPointer(dst_sample, &dst_sample_data);
+ if (FAILED(hr))
{
- /* Set to highest bitrate so samples will fit in for sure */
- FIXME("Variable-bitrate audio not fully supported.\n");
- bitrate_index = 15;
+ ERR("Unable to get pointer to buffer, %#x\n", hr);
+ goto fail;
}
- 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 /
- (format->nSamplesPerSec<<lsf) + 1;
- else if (layer == 2)
- format->nBlockAlign = format->nAvgBytesPerSec * 8 * 144 /
- format->nSamplesPerSec + 1;
+ if (This->packet.pts != AV_NOPTS_VALUE)
+ {
+ sample_start = (This->packet.pts * time_base.num * 10000000) / (time_base.den);
+ sample_stop = ((This->packet.pts + This->packet.duration) * time_base.num * 10000000) / (time_base.den);
+ }
else
- format->nBlockAlign = 4 * (format->nAvgBytesPerSec * 8 * 12 / format->nSamplesPerSec + 1);
-
- format->wBitsPerSample = 0;
+ {
+ sample_start = This->position;
+ sample_stop = sample_start + ((uint64_t)This->packet.duration * time_base.num * 10000000) / (time_base.den);
+ }
+ This->position = sample_stop;
- if (layer == 3)
+ memcpy(dst_sample_data, This->packet.data, This->packet.size);
+ hr = IMediaSample_SetActualDataLength(dst_sample, This->packet.size);
+ if (FAILED(hr))
{
- MPEGLAYER3WAVEFORMAT *mp3format = (MPEGLAYER3WAVEFORMAT*)format;
+ ERR("Failed to set destination buffer data length, %#x\n", hr);
+ goto fail;
+ }
- format->cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;
+ IMediaSample_SetTime(dst_sample, &sample_start, &sample_stop);
+ IMediaSample_SetPreroll(dst_sample, FALSE);
+ IMediaSample_SetDiscontinuity(dst_sample, This->discontinuity);
+ IMediaSample_SetSyncPoint(dst_sample, TRUE);
+ input_pin->rtCurrent = sample_start;
+ input_pin->rtNext = sample_stop;
- mp3format->wID = MPEGLAYER3_ID_MPEG;
- mp3format->fdwFlags = MPEGLAYER3_FLAG_PADDING_ON;
- mp3format->nBlockSize = format->nBlockAlign;
- mp3format->nFramesPerBlock = 1;
+ This->discontinuity = FALSE;
- /* 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;
+ hr = OutputPin_SendSample(&output_pin->pin, dst_sample);
+ IMediaSample_Release(dst_sample);
+ if (hr == S_FALSE || hr == VFW_E_WRONG_STATE)
+ {
+ TRACE("Holding packet\n");
+ This->packet_held = TRUE;
}
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;
+ av_free_packet(&This->packet);
+ if (hr != S_OK)
+ {
+ WARN("Error sending sample (%x)\n", hr);
+ This->discontinuity = TRUE;
+ }
}
- pamt->subtype.Data1 = format->wFormatTag;
- TRACE("MPEG audio stream detected:\n"
- "\tLayer %d (%#x)\n"
- "\tFrequency: %d\n"
- "\tChannels: %d (%d)\n"
- "\tBytesPerSec: %d\n",
- layer, format->wFormatTag, format->nSamplesPerSec,
- format->nChannels, mode, format->nAvgBytesPerSec);
-
- dump_AM_MEDIA_TYPE(pamt);
+ return hr;
- return S_OK;
+fail:
+ if (dst_sample) IMediaSample_Release(dst_sample);
+ av_free_packet(&This->packet);
+ This->discontinuity = TRUE;
+ 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)) return S_OK;
+ if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1System)) return S_OK;
+
+ if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1VideoCD))
+ FIXME("MPEG-1 VideoCD streams not yet supported.\n");
+
+ return S_FALSE;
+}
static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin, ALLOCATOR_PROPERTIES *props)
{
- PullPin *pPin = (PullPin *)iface;
- MPEGSplitterImpl *This = (MPEGSplitterImpl*)pPin->pin.pinInfo.pFilter;
- HRESULT hr;
- LONGLONG pos = 0; /* in bytes */
- BYTE header[10];
- int streamtype = 0;
- LONGLONG total, avail;
- AM_MEDIA_TYPE amt;
- PIN_INFO piOutput;
-
- IAsyncReader_Length(pPin->pReader, &total, &avail);
- This->EndOfFile = total;
-
- hr = IAsyncReader_SyncRead(pPin->pReader, pos, 4, header);
- if (SUCCEEDED(hr))
- pos += 4;
-
- /* Skip ID3 v2 tag, if any */
- if (SUCCEEDED(hr) && !memcmp("ID3", 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);
-
- while(SUCCEEDED(hr) && !(streamtype=MPEGSplitter_head_check(header)))
+ struct MPEGSplitterImpl *This = (struct MPEGSplitterImpl *)((PullPin *)iface)->pin.pinInfo.pFilter;
+ char url[12 + sizeof(iface) * 2];
+ LONGLONG duration = -1;
+ unsigned int i;
+ int ret;
+
+ sprintf(url, "quartz://%p", iface);
+ ret = pav_open_input_file(&This->format_ctx, url, NULL, 0, NULL);
+ if (ret)
{
- TRACE("%x:%x:%x:%x\n", header[0], header[1], header[2], header[3]);
- /* 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);
+ ERR("av_open_input_file failed %d\n", ret);
+ return E_FAIL;
+ }
+
+ ret = pav_find_stream_info(This->format_ctx);
+ if (ret < 0)
+ {
+ ERR("av_find_stream_info failed %d\n", ret);
+ pav_close_input_file(This->format_ctx);
+ This->format_ctx = NULL;
+ return E_FAIL;
}
- if (FAILED(hr))
- return hr;
- pos -= 4;
- This->begin_offset = pos;
- memcpy(This->header, header, 4);
- This->seektable[0].bytepos = pos;
- This->seektable[0].timepos = 0;
+ if (This->format_ctx->duration != AV_NOPTS_VALUE) duration = This->format_ctx->duration;
- switch(streamtype)
+ for (i = 0; i < This->format_ctx->nb_streams; ++i)
{
- case MPEG_AUDIO_HEADER:
+ AVCodecContext *codec_ctx = This->format_ctx->streams[i]->codec;
+ AM_MEDIA_TYPE media_type;
+ PIN_INFO output_pin;
+
+ switch (codec_ctx->codec_type)
{
- LONGLONG duration = 0;
- DWORD last_entry = 0;
+ case CODEC_TYPE_VIDEO:
+ {
+ MPEG1VIDEOINFO *format;
+ ULONG format_size = sizeof(*format);
- DWORD ticks = GetTickCount();
+ format = CoTaskMemAlloc(format_size);
+ if (!format)
+ {
+ ERR("Failed to alloc memory\n");
+ goto fail;
+ }
- hr = MPEGSplitter_init_audio(This, header, &piOutput, &amt);
- if (SUCCEEDED(hr))
- {
- WAVEFORMATEX *format = (WAVEFORMATEX*)amt.pbFormat;
-
- props->cbAlign = 1;
- props->cbPrefix = 0;
- /* Make the output buffer a multiple of the frame size */
- props->cbBuffer = 0x4000 / format->nBlockAlign *
- format->nBlockAlign;
- props->cBuffers = 3;
- hr = Parser_AddPin(&(This->Parser), &piOutput, props, &amt);
+ memset(format, 0, format_size);
+ format->dwStartTimeCode = 0;
+ format->cbSequenceHeader = 0;
+ format->hdr.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ format->hdr.bmiHeader.biWidth = codec_ctx->width;
+ format->hdr.bmiHeader.biHeight = codec_ctx->height;
+ format->hdr.bmiHeader.biPlanes = 1;
+ format->hdr.bmiHeader.biBitCount = 24;
+ format->hdr.bmiHeader.biCompression = BI_RGB;
+ format->hdr.bmiHeader.biSizeImage = 0;
+ format->hdr.bmiHeader.biXPelsPerMeter = 0;
+ format->hdr.bmiHeader.biYPelsPerMeter = 0;
+ format->hdr.bmiHeader.biClrUsed = 0;
+ format->hdr.bmiHeader.biClrImportant = 0;
+
+ media_type.majortype = MEDIATYPE_Video;
+ media_type.subtype = MEDIASUBTYPE_MPEG1Payload;
+ media_type.bFixedSizeSamples = FALSE;
+ media_type.bTemporalCompression = TRUE;
+ media_type.lSampleSize = 0;
+ media_type.formattype = FORMAT_MPEGVideo;
+ media_type.pUnk = NULL;
+ media_type.cbFormat = format_size;
+ media_type.pbFormat = (BYTE *)format;
+
+ output_pin.dir = PINDIR_OUTPUT;
+ output_pin.pFilter = (IBaseFilter*)This;
+ wsprintfW(output_pin.achName, wszVideoStream);
+
+ Parser_AddPin(&This->Parser, &output_pin, props, &media_type);
+ break;
}
- if (FAILED(hr))
+ case CODEC_TYPE_AUDIO:
{
- if (amt.pbFormat)
- CoTaskMemFree(amt.pbFormat);
- ERR("Could not create pin for MPEG audio stream (%x)\n", hr);
- break;
- }
+ int layer = codec_ctx->sub_id;
- /* 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;
- This->Parser.pInputPin->rtStop = MEDIATIME_FROM_BYTES(This->EndOfFile);
- This->Parser.pInputPin->rtStart = This->Parser.pInputPin->rtCurrent = MEDIATIME_FROM_BYTES(This->begin_offset);
+ media_type.majortype = MEDIATYPE_Audio;
+ media_type.subtype = MEDIASUBTYPE_MPEG1AudioPayload;
+ media_type.bFixedSizeSamples = FALSE;
+ media_type.bTemporalCompression = FALSE;
+ media_type.lSampleSize = 0;
+ media_type.formattype = FORMAT_WaveFormatEx;
+ media_type.pUnk = NULL;
- /* http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm has a whole read up on audio headers */
- while (pos + 3 < This->EndOfFile)
- {
- LONGLONG length = 0;
- hr = IAsyncReader_SyncRead(pPin->pReader, pos, 4, header);
- if (hr != S_OK)
- break;
- while (parse_header(header, &length, &duration))
+ if (layer > 3)
{
- /* 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);
- if (hr != S_OK || This->EndOfFile - pos < 4)
- break;
+ FIXME("Unhandled layer %d\n", layer);
+ goto fail;
}
- pos += length;
- if (This->seektable && (duration / SEEK_INTERVAL) > last_entry)
+ if (layer == 3)
{
- if (last_entry + 1 > duration / SEEK_INTERVAL)
+ MPEGLAYER3WAVEFORMAT *format;
+ ULONG format_size = sizeof(*format);
+
+ format = CoTaskMemAlloc(format_size);
+ if (!format)
{
- ERR("Somehow skipped %d interval lengths instead of 1\n", (DWORD)(duration/SEEK_INTERVAL) - (last_entry + 1));
+ ERR("Failed to alloc memory\n");
+ goto fail;
}
- ++last_entry;
- TRACE("Entry: %u\n", last_entry);
- if (last_entry >= This->seek_entries)
+ memset(format, 0, format_size);
+ format->wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;
+ format->wfx.nChannels = codec_ctx->channels;
+ format->wfx.nSamplesPerSec = codec_ctx->sample_rate;
+ format->wfx.nAvgBytesPerSec = codec_ctx->bit_rate / 8;
+ format->wfx.nBlockAlign = codec_ctx->frame_size;
+ format->wfx.wBitsPerSample = 0;
+
+ format->wID = MPEGLAYER3_ID_MPEG;
+ format->fdwFlags = MPEGLAYER3_FLAG_PADDING_ON;
+ format->nBlockSize = format->wfx.nBlockAlign;
+ format->nFramesPerBlock = 1;
+ format->nCodecDelay = 1393;
+ format->wfx.cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;
+
+ media_type.cbFormat = format_size;
+ media_type.pbFormat = (BYTE *)format;
+ media_type.subtype.Data1 = format->wfx.wFormatTag;
+ }
+ else
+ {
+ MPEG1WAVEFORMAT *format;
+ ULONG format_size = sizeof(*format);
+
+ format = CoTaskMemAlloc(format_size);
+ if (!format)
{
- This->seek_entries += 64;
- This->seektable = CoTaskMemRealloc(This->seektable, (This->seek_entries)*sizeof(struct seek_entry));
+ ERR("Failed to alloc memory\n");
+ goto fail;
}
- This->seektable[last_entry].bytepos = pos;
- This->seektable[last_entry].timepos = duration;
+
+ memset(format, 0, format_size);
+ format->wfx.wFormatTag = WAVE_FORMAT_MPEG;
+ format->wfx.nChannels = codec_ctx->channels;
+ format->wfx.nSamplesPerSec = codec_ctx->sample_rate;
+ format->wfx.nAvgBytesPerSec = codec_ctx->bit_rate / 8;
+ format->wfx.nBlockAlign = codec_ctx->frame_size;
+ format->fwHeadLayer = layer == 1 ? ACM_MPEG_LAYER1 : ACM_MPEG_LAYER2;
+ format->wfx.wBitsPerSample = 0;
+ format->dwHeadBitrate = codec_ctx->bit_rate;
+ format->fwHeadMode = ACM_MPEG_JOINTSTEREO;
+ format->fwHeadModeExt = 0xf;
+ format->wHeadEmphasis = 1;
+ format->fwHeadFlags = ACM_MPEG_ID_MPEG1;
+ format->wfx.cbSize = 22;
+
+ media_type.cbFormat = format_size;
+ media_type.pbFormat = (BYTE *)format;
+ media_type.subtype.Data1 = format->wfx.wFormatTag;
}
- TRACE("Pos: %x%08x/%x%08x\n", (DWORD)(pos >> 32), (DWORD)pos, (DWORD)(This->EndOfFile>>32), (DWORD)This->EndOfFile);
+ output_pin.dir = PINDIR_OUTPUT;
+ output_pin.pFilter = (IBaseFilter*)This;
+ wsprintfW(output_pin.achName, wszAudioStream);
+
+ Parser_AddPin(&This->Parser, &output_pin, props, &media_type);
+ break;
}
- hr = S_OK;
- TRACE("Duration: %d seconds\n", (DWORD)(duration / 10000000));
- TRACE("Parsing took %u ms\n", GetTickCount() - ticks);
- This->duration = duration;
-
- This->Parser.mediaSeeking.llCurrent = 0;
- This->Parser.mediaSeeking.llDuration = duration;
- This->Parser.mediaSeeking.llStop = duration;
- break;
- }
- 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;
+ default:
+ FIXME("Unhandled codec type %#x\n", codec_ctx->codec_type);
+ goto fail;
+ }
}
+
This->position = 0;
+ This->duration = duration;
+ This->Parser.mediaSeeking.llCurrent = 0;
+ This->Parser.mediaSeeking.llDuration = duration;
+ This->Parser.mediaSeeking.llStop = duration;
- return hr;
+ return S_OK;
+
+fail:
+ pav_close_input_file(This->format_ctx);
+ This->format_ctx = NULL;
+ return E_FAIL;
}
-static HRESULT MPEGSplitter_cleanup(LPVOID iface)
+static HRESULT MPEGSplitter_cleanup(void *iface)
{
- MPEGSplitterImpl *This = iface;
-
- TRACE("(%p)\n", This);
+ TRACE("iface %p\n", iface);
return S_OK;
}
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;
+ struct MPEGSplitterImpl *This = (struct MPEGSplitterImpl *)iface;
+ LONGLONG newpos = This->Parser.mediaSeeking.llCurrent;
+ PullPin *input_pin = This->Parser.pInputPin;
+ unsigned int i;
+ int ret;
if (newpos > This->duration)
{
- WARN("Requesting position %x%08x beyond end of stream %x%08x\n", (DWORD)(newpos>>32), (DWORD)newpos, (DWORD)(This->duration>>32), (DWORD)This->duration);
+ WARN("Requesting position %x%08x beyond end of stream %x%08x\n",
+ (DWORD)(newpos >> 32), (DWORD)newpos, (DWORD)(This->duration >> 32), (DWORD)This->duration);
return E_INVALIDARG;
}
- if (This->position/1000000 == newpos/1000000)
+ if (This->position / 1000000 == newpos / 1000000)
{
- TRACE("Requesting position %x%08x same as current position %x%08x\n", (DWORD)(newpos>>32), (DWORD)newpos, (DWORD)(This->position>>32), (DWORD)This->position);
+ TRACE("Requesting position %x%08x same as current position %x%08x\n",
+ (DWORD)(newpos>>32), (DWORD)newpos, (DWORD)(This->position>>32), (DWORD)This->position);
return S_OK;
}
- /* Position, cached */
- bytepos = This->seektable[newpos / SEEK_INTERVAL].bytepos;
- timepos = This->seektable[newpos / SEEK_INTERVAL].timepos;
-
- hr = IAsyncReader_SyncRead(pPin->pReader, bytepos, 4, header);
- while (bytepos + 3 < This->EndOfFile)
+ if (This->packet_held)
{
- LONGLONG length = 0;
- hr = IAsyncReader_SyncRead(pPin->pReader, bytepos, 4, header);
- if (hr != S_OK || timepos >= newpos)
- break;
-
- while (parse_header(header, &length, &timepos) && bytepos + 3 < This->EndOfFile)
- {
- /* 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);
- if (hr != S_OK)
- break;
- }
- bytepos += length;
- TRACE("Pos: %x%08x/%x%08x\n", (DWORD)(bytepos >> 32), (DWORD)bytepos, (DWORD)(This->EndOfFile>>32), (DWORD)This->EndOfFile);
+ av_free_packet(&This->packet);
+ This->packet_held = FALSE;
}
- if (SUCCEEDED(hr))
+ ret = pav_seek_frame(This->format_ctx, -1, newpos * AV_TIME_BASE, AVSEEK_FLAG_ANY);
+ if (ret < 0)
{
- PullPin *pin = This->Parser.pInputPin;
- IPin *victim = NULL;
+ ERR("Failed to seek to pos %s\n", wine_dbgstr_longlong(newpos));
+ return E_FAIL;
+ }
- TRACE("Moving sound to %08u bytes!\n", (DWORD)bytepos);
+ EnterCriticalSection(&input_pin->thread_lock);
- EnterCriticalSection(&pin->thread_lock);
- IPin_BeginFlush((IPin *)pin);
+ IPin_BeginFlush((IPin *)input_pin);
- /* Make sure this is done while stopped, BeginFlush takes care of this */
- EnterCriticalSection(&This->Parser.csFilter);
- memcpy(This->header, header, 4);
- IPin_ConnectedTo(This->Parser.ppPins[1], &victim);
- if (victim)
- {
- IPin_NewSegment(victim, newpos, This->duration, pin->dRate);
- IPin_Release(victim);
- }
+ EnterCriticalSection(&This->Parser.csFilter);
- 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);
+ for (i = 0; i < This->Parser.cStreams; ++i)
+ {
+ IPin *pin;
- TRACE("Done flushing\n");
- IPin_EndFlush((IPin *)pin);
- LeaveCriticalSection(&pin->thread_lock);
+ IPin_ConnectedTo(This->Parser.ppPins[1 + i], &pin);
+ if (pin)
+ {
+ IPin_NewSegment(pin, newpos, This->duration, input_pin->dRate);
+ IPin_Release(pin);
+ }
}
- return hr;
-}
-static HRESULT MPEGSplitter_disconnect(LPVOID iface)
-{
- /* TODO: Find memory leaks etc */
- return S_OK;
-}
+ This->discontinuity = TRUE;
+ This->position = newpos;
-static HRESULT MPEGSplitter_first_request(LPVOID iface)
-{
- MPEGSplitterImpl *This = iface;
- PullPin *pin = This->Parser.pInputPin;
- HRESULT hr;
- LONGLONG length;
- IMediaSample *sample;
+ LeaveCriticalSection(&This->Parser.csFilter);
- TRACE("Seeking? %d\n", This->seek);
- assert(parse_header(This->header, &length, NULL) == S_OK);
+ TRACE("Done flushing\n");
+ IPin_EndFlush((IPin *)input_pin);
- if (pin->rtCurrent >= pin->rtStop)
- {
- /* Last sample has already been queued, request nothing more */
- FIXME("Done!\n");
- return S_OK;
- }
+ LeaveCriticalSection(&input_pin->thread_lock);
- 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);
+ return S_OK;
+}
- if (rtSampleStop > pin->rtStop)
- rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign));
+static HRESULT MPEGSplitter_disconnect(void *iface)
+{
+ struct MPEGSplitterImpl *This = (struct MPEGSplitterImpl *)iface;
- hr = IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop);
+ TRACE("iface %p\n", iface);
- pin->rtCurrent = pin->rtNext;
- pin->rtNext = rtSampleStop;
+ pav_close_input_file(This->format_ctx);
+ This->format_ctx = NULL;
- IMediaSample_SetPreroll(sample, FALSE);
- IMediaSample_SetDiscontinuity(sample, This->seek);
- IMediaSample_SetSyncPoint(sample, 1);
- This->seek = 0;
+ return S_OK;
+}
- hr = IAsyncReader_Request(pin->pReader, sample, 0);
- }
- if (FAILED(hr))
- ERR("Horsemen of the apocalypse came to bring error 0x%08x\n", hr);
+static HRESULT MPEGSplitter_first_request(void *iface)
+{
+ TRACE("iface %p\n", iface);
- return hr;
+ return S_OK;
}
static const IBaseFilterVtbl MPEGSplitter_Vtbl =
@@ -790,41 +613,107 @@ static const IBaseFilterVtbl MPEGSplitter_Vtbl =
Parser_QueryVendorInfo
};
-HRESULT MPEGSplitter_create(IUnknown * pUnkOuter, LPVOID * ppv)
+static HRESULT init_func_ptrs()
{
- MPEGSplitterImpl *This;
- HRESULT hr = E_FAIL;
+ if (libavformat_handle) return S_OK;
- TRACE("(%p, %p)\n", pUnkOuter, ppv);
+ libavformat_handle = wine_dlopen(SONAME_LIBAVFORMAT, RTLD_NOW, NULL, 0);
+ if (!libavformat_handle)
+ {
+ WARN("Failed to load libavformat.\n");
+ goto fail;
+ }
- *ppv = NULL;
+ libavutil_handle = wine_dlopen(SONAME_LIBAVUTIL, RTLD_NOW, NULL, 0);
+ if (!libavutil_handle)
+ {
+ WARN("Failed to load libavutil.\n");
+ goto fail;
+ }
- if (pUnkOuter)
- return CLASS_E_NOAGGREGATION;
+#define LOAD_FUNCPTR(f) \
+ if (!(p##f = wine_dlsym(libavformat_handle, #f, NULL, 0))) \
+ { \
+ ERR("Failed to load %s\n", #f); \
+ goto fail; \
+ }
- This = CoTaskMemAlloc(sizeof(MPEGSplitterImpl));
- if (!This)
- return E_OUTOFMEMORY;
+ LOAD_FUNCPTR(av_close_input_file)
+ LOAD_FUNCPTR(av_find_stream_info)
+ LOAD_FUNCPTR(av_open_input_file)
+ LOAD_FUNCPTR(av_read_frame)
+ LOAD_FUNCPTR(av_register_all)
+ LOAD_FUNCPTR(av_seek_frame)
+ LOAD_FUNCPTR(register_protocol)
+#undef LOAD_FUNCPTR
+
+#define LOAD_FUNCPTR(f) \
+ if (!(p##f = wine_dlsym(libavutil_handle, #f, NULL, 0))) \
+ { \
+ ERR("Failed to load %s\n", #f); \
+ goto fail; \
+ }
+
+ LOAD_FUNCPTR(av_free)
+ LOAD_FUNCPTR(av_malloc)
+#undef LOAD_FUNCPTR
+
+ pav_register_all();
+ pregister_protocol(&quartz_prot);
+
+ return S_OK;
+
+fail:
+ wine_dlclose(libavformat_handle, NULL, 0);
+ libavformat_handle = NULL;
+ wine_dlclose(libavutil_handle, NULL, 0);
+ libavutil_handle = NULL;
+ return E_FAIL;
+}
+
+HRESULT MPEGSplitter_create(IUnknown *outer_unknown, void **filter)
+{
+ struct MPEGSplitterImpl *object;
+ HRESULT hr;
+
+ TRACE("outer_unknown %p, filter %p\n", outer_unknown, filter);
+
+ if (outer_unknown) return CLASS_E_NOAGGREGATION;
- ZeroMemory(This, sizeof(MPEGSplitterImpl));
- This->seektable = CoTaskMemAlloc(sizeof(struct seek_entry) * 64);
- if (!This->seektable)
+ hr = init_func_ptrs();
+ if (FAILED(hr)) return hr;
+
+ /* Note: This memory is managed by the parser filter once created */
+ object = CoTaskMemAlloc(sizeof(*object));
+ if (!object)
{
- CoTaskMemFree(This);
+ ERR("Failed to allocate memory\n");
return E_OUTOFMEMORY;
}
- This->seek_entries = 64;
+ ZeroMemory(object, sizeof(*object));
+
+ object->discontinuity = TRUE;
- 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);
+ hr = Parser_Create(&(object->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);
if (FAILED(hr))
{
- CoTaskMemFree(This);
+ CoTaskMemFree(object);
return hr;
}
- This->seek = 1;
- /* Note: This memory is managed by the parser filter once created */
- *ppv = This;
+ *filter = object;
return hr;
}
+
+#else
+
+HRESULT MPEGSplitter_create(IUnknown *outer_unknown, void **filter)
+{
+ WARN("No MPEG demuxing support\n");
+ return E_FAIL;
+}
+
+#endif
--
1.6.0.6
--------------020007090006040207030000--
More information about the wine-patches
mailing list