view loader/dshow/cmediasample.c @ 11619:179138947307

This patch contains bugfixes for the esd audio output driver that I uncovered while trying to send sound to a remote esd server over a wireless (11 mbs, just enough to handle to sound) link. First, the sound was full "ticking" sounds. I found a bug that prevented the "send the remainder of this block" code from ever being called - so large chunks of audio were simply being ignored. Fixing this bug removed the "ticking" from audio streams. Fixing this bug, however, uncovered another problem - when the socket buffer was full, doing a blocking write to finish the buffer would take far too long and would turn video into a chunky mess. I'd imagine this blocking write would be fine for an audio-only stream, but it turns out to hold up the video far too much. The solution in this patch is to write as much data as possible to the socket, and then return as soon as possible, reporting the number of bytes actually written accurately back to mplayer. I've tested it on both local and remote esd servers, and it works well. Patch by Benjamin Osheroff <ben@gimbo.net>
author attila
date Wed, 10 Dec 2003 12:19:13 +0000
parents 6454a2ba2e33
children f5537cc95b02
line wrap: on
line source

#include "cmediasample.h"
#include "wine/winerror.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/*
 * currently hack to make some extra room for DS Acel codec which
 * seems to overwrite allocated memory - FIXME better later
 * check the buffer allocation
 */
static const int SAFETY_ACEL = 1024;

static long STDCALL CMediaSample_QueryInterface(IUnknown* This,
						/* [in] */ const GUID* iid,
						/* [iid_is][out] */ void **ppv)
{
    Debug printf("CMediaSample_QueryInterface(%p) called\n", This);
    if (!ppv)
	return E_INVALIDARG;
    if (memcmp(iid, &IID_IUnknown, sizeof(*iid)) == 0)
    {
	*ppv = (void*)This;
	((IMediaSample*) This)->vt->AddRef(This);
	return 0;
    }
    if (memcmp(iid, &IID_IMediaSample, sizeof(*iid)) == 0)
    {
	*ppv = (void*)This;
	((IMediaSample*) This)->vt->AddRef(This);
	return 0;
    }
    return E_NOINTERFACE;
}

static long STDCALL CMediaSample_AddRef(IUnknown* This)
{
    Debug printf("CMediaSample_AddRef(%p) called\n", This);
    ((CMediaSample*)This)->refcount++;
    return 0;
}

void CMediaSample_Destroy(CMediaSample* This)
{

    Debug printf("CMediaSample_Destroy(%p) called (ref:%d)\n", This, This->refcount);
    free(This->vt);
    free(This->own_block);
    if (This->media_type.pbFormat)
	free(This->media_type.pbFormat);
    free(This);
}

static long STDCALL CMediaSample_Release(IUnknown* This)
{
    CMediaSample* parent = (CMediaSample*)This;
    Debug printf("CMediaSample_Release(%p) called  (new ref:%d)\n",
		 This, ((CMediaSample*)This)->refcount-1);

    if (--((CMediaSample*) This)->refcount == 0)
    {
	parent->all->vt->ReleaseBuffer((IMemAllocator*)(parent->all),
				       (IMediaSample*)This);
    }
    return 0;
}

static HRESULT STDCALL CMediaSample_GetPointer(IMediaSample* This,
					       /* [out] */ BYTE** ppBuffer)
{
    Debug printf("CMediaSample_GetPointer(%p) called -> %p, size: %d  %d\n", This, ((CMediaSample*) This)->block, ((CMediaSample*)This)->actual_size, ((CMediaSample*)This)->size);
    if (!ppBuffer)
	return E_INVALIDARG;
    *ppBuffer = (BYTE*) ((CMediaSample*) This)->block;
    return 0;
}

static long STDCALL CMediaSample_GetSize(IMediaSample * This)
{
    Debug printf("CMediaSample_GetSize(%p) called -> %d\n", This, ((CMediaSample*) This)->size);
    return ((CMediaSample*) This)->size;
}

static HRESULT STDCALL CMediaSample_GetTime(IMediaSample * This,
					    /* [out] */ REFERENCE_TIME *pTimeStart,
					    /* [out] */ REFERENCE_TIME *pTimeEnd)
{
    Debug printf("CMediaSample_GetTime(%p) called (UNIMPLEMENTED)\n", This);
    return E_NOTIMPL;
}

static HRESULT STDCALL CMediaSample_SetTime(IMediaSample * This,
					    /* [in] */ REFERENCE_TIME *pTimeStart,
					    /* [in] */ REFERENCE_TIME *pTimeEnd)
{
    Debug printf("CMediaSample_SetTime(%p) called (UNIMPLEMENTED)\n", This);
    return E_NOTIMPL;
}

static HRESULT STDCALL CMediaSample_IsSyncPoint(IMediaSample * This)
{
    Debug printf("CMediaSample_IsSyncPoint(%p) called\n", This);
    if (((CMediaSample*)This)->isSyncPoint)
	return 0;
    return 1;
}

static HRESULT STDCALL CMediaSample_SetSyncPoint(IMediaSample * This,
						 long bIsSyncPoint)
{
    Debug printf("CMediaSample_SetSyncPoint(%p) called\n", This);
    ((CMediaSample*)This)->isSyncPoint = bIsSyncPoint;
    return 0;
}

static HRESULT STDCALL CMediaSample_IsPreroll(IMediaSample * This)
{
    Debug printf("CMediaSample_IsPreroll(%p) called\n", This);

    if (((CMediaSample*)This)->isPreroll)
	return 0;//S_OK

    return 1;//S_FALSE
}

static HRESULT STDCALL CMediaSample_SetPreroll(IMediaSample * This,
					       long bIsPreroll)
{
    Debug printf("CMediaSample_SetPreroll(%p) called\n", This);
    ((CMediaSample*)This)->isPreroll=bIsPreroll;
    return 0;
}

static long STDCALL CMediaSample_GetActualDataLength(IMediaSample* This)
{
    Debug printf("CMediaSample_GetActualDataLength(%p) called -> %d\n", This, ((CMediaSample*)This)->actual_size);
    return ((CMediaSample*)This)->actual_size;
}

static HRESULT STDCALL CMediaSample_SetActualDataLength(IMediaSample* This,
							long __MIDL_0010)
{
    CMediaSample* cms = (CMediaSample*)This;
    Debug printf("CMediaSample_SetActualDataLength(%p, %ld) called\n", This, __MIDL_0010);

    if (__MIDL_0010 > cms->size)
    {
        char* c = cms->own_block;
	Debug printf("CMediaSample - buffer overflow   %ld %d   %p %p\n",
		     __MIDL_0010, ((CMediaSample*)This)->size, cms->own_block, cms->block);
	cms->own_block = (char*) realloc(cms->own_block, (size_t) __MIDL_0010 + SAFETY_ACEL);
	if (c == cms->block)
	    cms->block = cms->own_block;
        cms->size = __MIDL_0010;
    }
    cms->actual_size = __MIDL_0010;
    return 0;
}

static HRESULT STDCALL CMediaSample_GetMediaType(IMediaSample* This,
						 AM_MEDIA_TYPE** ppMediaType)
{
    AM_MEDIA_TYPE* t;
    Debug printf("CMediaSample_GetMediaType(%p) called\n", This);
    if(!ppMediaType)
	return E_INVALIDARG;
    if(!((CMediaSample*)This)->type_valid)
    {
	*ppMediaType=0;
	return 1;
    }

    t = &((CMediaSample*)This)->media_type;
    //    if(t.pbFormat)free(t.pbFormat);
    (*ppMediaType) = (AM_MEDIA_TYPE*)malloc(sizeof(AM_MEDIA_TYPE));
    **ppMediaType = *t;
    (*ppMediaType)->pbFormat = (char*)malloc(t->cbFormat);
    memcpy((*ppMediaType)->pbFormat, t->pbFormat, t->cbFormat);
    //    *ppMediaType=0; //media type was not changed
    return 0;
}

static HRESULT STDCALL CMediaSample_SetMediaType(IMediaSample * This,
						 AM_MEDIA_TYPE *pMediaType)
{
    AM_MEDIA_TYPE* t;
    Debug printf("CMediaSample_SetMediaType(%p) called\n", This);
    if (!pMediaType)
	return E_INVALIDARG;
    t = &((CMediaSample*)This)->media_type;
    if (t->pbFormat)
	free(t->pbFormat);
    t = pMediaType;
    if (t->cbFormat)
    {
	t->pbFormat = (char*)malloc(t->cbFormat);
	memcpy(t->pbFormat, pMediaType->pbFormat, t->cbFormat);
    }
    else
        t->pbFormat = 0;
    ((CMediaSample*) This)->type_valid=1;

    return 0;
}

static HRESULT STDCALL CMediaSample_IsDiscontinuity(IMediaSample * This)
{
    Debug printf("CMediaSample_IsDiscontinuity(%p) called\n", This);
    return ((CMediaSample*) This)->isDiscontinuity;
}

static HRESULT STDCALL CMediaSample_SetDiscontinuity(IMediaSample * This,
						     long bDiscontinuity)
{
    Debug printf("CMediaSample_SetDiscontinuity(%p) called (%ld)\n", This, bDiscontinuity);
    ((CMediaSample*) This)->isDiscontinuity = bDiscontinuity;
    return 0;
}

static HRESULT STDCALL CMediaSample_GetMediaTime(IMediaSample * This,
						 /* [out] */ LONGLONG *pTimeStart,
						 /* [out] */ LONGLONG *pTimeEnd)
{
    Debug printf("CMediaSample_GetMediaTime(%p) called\n", This);
    if (pTimeStart)
	*pTimeStart = ((CMediaSample*) This)->time_start;
    if (pTimeEnd)
	*pTimeEnd = ((CMediaSample*) This)->time_end;
    return 0;
}

static HRESULT STDCALL CMediaSample_SetMediaTime(IMediaSample * This,
						 /* [in] */ LONGLONG *pTimeStart,
						 /* [in] */ LONGLONG *pTimeEnd)
{
    Debug printf("CMediaSample_SetMediaTime(%p) called\n", This);
    if (pTimeStart)
	((CMediaSample*) This)->time_start = *pTimeStart;
    if (pTimeEnd)
        ((CMediaSample*) This)->time_end = *pTimeEnd;
    return 0;
}

// extension for direct memory write or decompressed data
static void CMediaSample_SetPointer(CMediaSample* This, char* pointer)
{
    Debug printf("CMediaSample_SetPointer(%p) called  -> %p\n", This, pointer);
    if (pointer)
	This->block = pointer;
    else
	This->block = This->own_block;
}

static void CMediaSample_ResetPointer(CMediaSample* This)
{
    Debug printf("CMediaSample_ResetPointer(%p) called\n", This);
    This->block = This->own_block;
}

CMediaSample* CMediaSampleCreate(IMemAllocator* allocator, int _size)
{
    CMediaSample* This = (CMediaSample*) malloc(sizeof(CMediaSample));
    if (!This)
	return NULL;

    // some hack here!
    // it looks like Acelp decoder is actually accessing
    // the allocated memory before it sets the new size for it ???
    // -- maybe it's being initialized with wrong parameters
    // anyway this is fixes the problem somehow with some reserves
    //
    // using different trick for now - in DS_Audio modify sample size
    //if (_size < 0x1000)
    //    _size = (_size + 0xfff) & ~0xfff;

    This->vt = (IMediaSample_vt*) malloc(sizeof(IMediaSample_vt));
    This->own_block = (char*) malloc((size_t)_size + SAFETY_ACEL);
    This->media_type.pbFormat = 0;

    if (!This->vt || !This->own_block)
    {
	CMediaSample_Destroy(This);
	return NULL;
    }

    This->vt->QueryInterface = CMediaSample_QueryInterface;
    This->vt->AddRef = CMediaSample_AddRef;
    This->vt->Release = CMediaSample_Release;
    This->vt->GetPointer = CMediaSample_GetPointer;
    This->vt->GetSize = CMediaSample_GetSize;
    This->vt->GetTime = CMediaSample_GetTime;
    This->vt->SetTime = CMediaSample_SetTime;
    This->vt->IsSyncPoint = CMediaSample_IsSyncPoint;
    This->vt->SetSyncPoint = CMediaSample_SetSyncPoint;
    This->vt->IsPreroll = CMediaSample_IsPreroll;
    This->vt->SetPreroll = CMediaSample_SetPreroll;
    This->vt->GetActualDataLength = CMediaSample_GetActualDataLength;
    This->vt->SetActualDataLength = CMediaSample_SetActualDataLength;
    This->vt->GetMediaType = CMediaSample_GetMediaType;
    This->vt->SetMediaType = CMediaSample_SetMediaType;
    This->vt->IsDiscontinuity = CMediaSample_IsDiscontinuity;
    This->vt->SetDiscontinuity = CMediaSample_SetDiscontinuity;
    This->vt->GetMediaTime = CMediaSample_GetMediaTime;
    This->vt->SetMediaTime = CMediaSample_SetMediaTime;

    This->all = allocator;
    This->size = _size;
    This->refcount = 0; // increased by MemAllocator
    This->actual_size = 0;
    This->isPreroll = 0;
    This->isDiscontinuity = 1;
    This->time_start = 0;
    This->time_end = 0;
    This->type_valid = 0;
    This->block = This->own_block;

    This->SetPointer = CMediaSample_SetPointer;
    This->ResetPointer = CMediaSample_ResetPointer;

    Debug printf("CMediaSample_Create(%p) called - sample size %d, buffer %p\n",
		 This, This->size, This->block);

    return This;
}