mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2025-08-28 18:23:54 +02:00
334 lines
11 KiB
C
334 lines
11 KiB
C
/*
|
|
* Copyright 2024 Rémi Bernon for CodeWeavers
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#define COBJMACROS
|
|
#include "unixlib.h"
|
|
#include "winnls.h"
|
|
#include "mfidl.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(dmo);
|
|
|
|
|
|
static struct stream_context *stream_context_create( struct winedmo_stream *stream, UINT64 stream_size )
|
|
{
|
|
static const UINT BUFFER_SIZE = 0x40000;
|
|
struct stream_context *context;
|
|
|
|
if (!(context = malloc( sizeof(*context) + BUFFER_SIZE ))) return NULL;
|
|
context->stream = (UINT_PTR)stream;
|
|
context->length = stream_size;
|
|
context->position = 0;
|
|
context->capacity = BUFFER_SIZE;
|
|
context->size = 0;
|
|
|
|
return context;
|
|
}
|
|
|
|
static void stream_context_destroy( struct stream_context *context )
|
|
{
|
|
free( context );
|
|
}
|
|
|
|
|
|
static struct stream_context *get_stream_context( UINT64 handle )
|
|
{
|
|
return (struct stream_context *)(UINT_PTR)handle;
|
|
}
|
|
|
|
static struct winedmo_stream *get_stream( UINT64 handle )
|
|
{
|
|
return (struct winedmo_stream *)(UINT_PTR)handle;
|
|
}
|
|
|
|
static NTSTATUS WINAPI seek_callback( void *args, ULONG size )
|
|
{
|
|
struct seek_callback_params *params = args;
|
|
struct stream_context *context = get_stream_context( params->context );
|
|
struct winedmo_stream *stream = get_stream( context->stream );
|
|
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
|
|
UINT64 pos = params->offset;
|
|
|
|
TRACE( "stream %p, offset %#I64x\n", stream, params->offset );
|
|
|
|
if (!stream->p_seek || (status = stream->p_seek( stream, &pos )))
|
|
WARN( "Failed to seek stream %p, status %#lx\n", stream, status );
|
|
else
|
|
TRACE( "Seeked stream %p to %#I64x\n", stream, pos );
|
|
|
|
return NtCallbackReturn( &pos, sizeof(pos), status );
|
|
}
|
|
|
|
static NTSTATUS WINAPI read_callback( void *args, ULONG size )
|
|
{
|
|
struct read_callback_params *params = args;
|
|
struct stream_context *context = get_stream_context( params->context );
|
|
struct winedmo_stream *stream = get_stream( context->stream );
|
|
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
|
|
ULONG ret = params->size;
|
|
|
|
TRACE( "stream %p, size %#x\n", stream, params->size );
|
|
|
|
if (!stream->p_read || (status = stream->p_read( stream, context->buffer, &ret )))
|
|
WARN( "Failed to read from stream %p, status %#lx\n", stream, status );
|
|
else
|
|
TRACE( "Read %#lx bytes from stream %p\n", ret, stream );
|
|
|
|
return NtCallbackReturn( &ret, sizeof(ret), status );
|
|
}
|
|
|
|
|
|
BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved )
|
|
{
|
|
TRACE( "instance %p, reason %lu, reserved %p\n", instance, reason, reserved );
|
|
|
|
if (reason == DLL_PROCESS_ATTACH)
|
|
{
|
|
struct process_attach_params params =
|
|
{
|
|
.seek_callback = (UINT_PTR)seek_callback,
|
|
.read_callback = (UINT_PTR)read_callback,
|
|
};
|
|
NTSTATUS status;
|
|
|
|
DisableThreadLibraryCalls( instance );
|
|
|
|
status = __wine_init_unix_call();
|
|
if (!status) status = UNIX_CALL( process_attach, ¶ms );
|
|
if (status) WARN( "Failed to init unixlib, status %#lx\n", status );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void buffer_lock( DMO_OUTPUT_DATA_BUFFER *buffer, struct sample *sample )
|
|
{
|
|
BYTE *data;
|
|
HRESULT hr;
|
|
DWORD size;
|
|
|
|
if (FAILED(hr = IMediaBuffer_GetBufferAndLength( buffer->pBuffer, &data, &size )))
|
|
ERR( "Failed to get media buffer data %p, hr %#lx\n", buffer, hr );
|
|
if (FAILED(hr = IMediaBuffer_GetMaxLength( buffer->pBuffer, &size )))
|
|
ERR( "Failed to get media buffer max length %p, hr %#lx\n", buffer, hr );
|
|
|
|
sample->data = (UINT_PTR)data;
|
|
sample->size = size;
|
|
}
|
|
|
|
static void buffer_unlock( DMO_OUTPUT_DATA_BUFFER *buffer, struct sample *sample, NTSTATUS status )
|
|
{
|
|
IMFSample *object;
|
|
HRESULT hr;
|
|
|
|
if (FAILED(hr = IMediaBuffer_SetLength( buffer->pBuffer, status ? 0 : sample->size )))
|
|
ERR( "Failed to update buffer length, hr %#lx\n", hr );
|
|
|
|
buffer->dwStatus = 0;
|
|
if (SUCCEEDED(hr = IMediaBuffer_QueryInterface( buffer->pBuffer, &IID_IMFSample, (void **)&object )))
|
|
{
|
|
if (sample->dts != INT64_MIN) IMFSample_SetUINT64( object, &MFSampleExtension_DecodeTimestamp, sample->dts );
|
|
if (sample->pts != INT64_MIN) IMFSample_SetSampleTime( object, sample->pts );
|
|
if (sample->duration != INT64_MIN) IMFSample_SetSampleDuration( object, sample->duration );
|
|
if (sample->flags & SAMPLE_FLAG_SYNC_POINT) IMFSample_SetUINT32( object, &MFSampleExtension_CleanPoint, 1 );
|
|
IMFSample_Release( object );
|
|
}
|
|
|
|
if ((buffer->rtTimestamp = sample->pts) != INT64_MIN) buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIME;
|
|
if ((buffer->rtTimelength = sample->duration) != INT64_MIN) buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH;
|
|
if (sample->flags & SAMPLE_FLAG_SYNC_POINT) buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT;
|
|
if (sample->flags & SAMPLE_FLAG_INCOMPLETE) buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
|
|
}
|
|
|
|
|
|
NTSTATUS CDECL winedmo_demuxer_check( const char *mime_type )
|
|
{
|
|
struct demuxer_check_params params = {0};
|
|
NTSTATUS status;
|
|
|
|
TRACE( "mime_type %s\n", debugstr_a(mime_type) );
|
|
lstrcpynA( params.mime_type, mime_type, sizeof(params.mime_type) );
|
|
|
|
if ((status = UNIX_CALL( demuxer_check, ¶ms ))) WARN( "returning %#lx\n", status );
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS CDECL winedmo_demuxer_create( const WCHAR *url, struct winedmo_stream *stream, UINT64 stream_size, INT64 *duration,
|
|
UINT *stream_count, WCHAR *mime_type, struct winedmo_demuxer *demuxer )
|
|
{
|
|
struct demuxer_create_params params = {0};
|
|
char *tmp = NULL;
|
|
NTSTATUS status;
|
|
UINT len;
|
|
|
|
TRACE( "url %s, stream %p, stream_size %#I64x, mime_type %p, demuxer %p\n", debugstr_w(url),
|
|
stream, stream_size, mime_type, demuxer );
|
|
|
|
if (!(params.context = stream_context_create( stream, stream_size ))) return STATUS_NO_MEMORY;
|
|
|
|
if (url && (len = WideCharToMultiByte( CP_ACP, 0, url, -1, NULL, 0, NULL, NULL )) && (tmp = malloc( len )))
|
|
{
|
|
WideCharToMultiByte( CP_ACP, 0, url, -1, tmp, len, NULL, NULL );
|
|
params.url = tmp;
|
|
}
|
|
status = UNIX_CALL( demuxer_create, ¶ms );
|
|
free( tmp );
|
|
|
|
if (status)
|
|
{
|
|
WARN( "demuxer_create failed, status %#lx\n", status );
|
|
stream_context_destroy( params.context );
|
|
return status;
|
|
}
|
|
|
|
*duration = params.duration;
|
|
*stream_count = params.stream_count;
|
|
MultiByteToWideChar( CP_ACP, 0, params.mime_type, -1, mime_type, 256 );
|
|
*demuxer = params.demuxer;
|
|
TRACE( "created demuxer %#I64x, stream %p, duration %I64d, stream_count %u, mime_type %s\n",
|
|
demuxer->handle, stream, params.duration, params.stream_count, debugstr_a(params.mime_type) );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS CDECL winedmo_demuxer_destroy( struct winedmo_demuxer *demuxer )
|
|
{
|
|
struct demuxer_destroy_params params = {.demuxer = *demuxer};
|
|
NTSTATUS status;
|
|
|
|
if (!demuxer->handle) return STATUS_SUCCESS;
|
|
|
|
TRACE( "demuxer %#I64x\n", demuxer->handle );
|
|
|
|
demuxer->handle = 0;
|
|
status = UNIX_CALL( demuxer_destroy, ¶ms );
|
|
if (status) WARN( "demuxer_destroy failed, status %#lx\n", status );
|
|
else stream_context_destroy( params.context );
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS CDECL winedmo_demuxer_read( struct winedmo_demuxer demuxer, UINT *stream, DMO_OUTPUT_DATA_BUFFER *buffer, UINT *buffer_size )
|
|
{
|
|
struct demuxer_read_params params = {.demuxer = demuxer};
|
|
NTSTATUS status;
|
|
|
|
TRACE( "demuxer %#I64x, stream %p, buffer %p, buffer_size %p\n", demuxer.handle, stream, buffer, buffer_size );
|
|
|
|
buffer_lock( buffer, ¶ms.sample );
|
|
status = UNIX_CALL( demuxer_read, ¶ms );
|
|
buffer_unlock( buffer, ¶ms.sample, status );
|
|
*buffer_size = params.sample.size;
|
|
*stream = params.stream;
|
|
|
|
if (status)
|
|
{
|
|
if (status == STATUS_END_OF_FILE) WARN( "Reached end of media file.\n" );
|
|
else if (status != STATUS_BUFFER_TOO_SMALL) ERR( "Failed to read sample, status %#lx\n", status );
|
|
return status;
|
|
}
|
|
|
|
TRACE( "Got buffer %p, buffer_size %#x on stream %u\n", buffer->pBuffer, *buffer_size, *stream );
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS CDECL winedmo_demuxer_seek( struct winedmo_demuxer demuxer, INT64 timestamp )
|
|
{
|
|
struct demuxer_seek_params params = {.demuxer = demuxer, .timestamp = timestamp};
|
|
NTSTATUS status;
|
|
|
|
TRACE( "demuxer %#I64x, timestamp %I64d\n", demuxer.handle, timestamp );
|
|
|
|
if ((status = UNIX_CALL( demuxer_seek, ¶ms )))
|
|
{
|
|
WARN( "Failed to set position, status %#lx\n", status );
|
|
return status;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS CDECL winedmo_demuxer_stream_lang( struct winedmo_demuxer demuxer, UINT stream, WCHAR *buffer, UINT len )
|
|
{
|
|
struct demuxer_stream_lang_params params = {.demuxer = demuxer, .stream = stream};
|
|
NTSTATUS status;
|
|
|
|
TRACE( "demuxer %#I64x, stream %u\n", demuxer.handle, stream );
|
|
|
|
if ((status = UNIX_CALL( demuxer_stream_lang, ¶ms )))
|
|
{
|
|
WARN( "Failed to get stream lang, status %#lx\n", status );
|
|
return status;
|
|
}
|
|
|
|
len = MultiByteToWideChar( CP_UTF8, 0, params.buffer, -1, buffer, len );
|
|
buffer[len - 1] = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS CDECL winedmo_demuxer_stream_name( struct winedmo_demuxer demuxer, UINT stream, WCHAR *buffer, UINT len )
|
|
{
|
|
struct demuxer_stream_name_params params = {.demuxer = demuxer, .stream = stream};
|
|
NTSTATUS status;
|
|
|
|
TRACE( "demuxer %#I64x, stream %u\n", demuxer.handle, stream );
|
|
|
|
if ((status = UNIX_CALL( demuxer_stream_name, ¶ms )))
|
|
{
|
|
WARN( "Failed to get stream name, status %#lx\n", status );
|
|
return status;
|
|
}
|
|
|
|
len = MultiByteToWideChar( CP_UTF8, 0, params.buffer, -1, buffer, len );
|
|
buffer[len - 1] = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
static HRESULT get_media_type( UINT code, void *params, struct media_type *media_type,
|
|
GUID *major, union winedmo_format **format )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
media_type->format = NULL;
|
|
if ((status = WINE_UNIX_CALL( code, params )) && status == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
if (!(media_type->format = malloc( media_type->format_size ))) return STATUS_NO_MEMORY;
|
|
status = WINE_UNIX_CALL( code, params );
|
|
}
|
|
|
|
if (!status)
|
|
{
|
|
*major = media_type->major;
|
|
*format = media_type->format;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
WARN( "Failed to get media type, code %#x, status %#lx\n", code, status );
|
|
free( media_type->format );
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS CDECL winedmo_demuxer_stream_type( struct winedmo_demuxer demuxer, UINT stream,
|
|
GUID *major, union winedmo_format **format )
|
|
{
|
|
struct demuxer_stream_type_params params = {.demuxer = demuxer, .stream = stream};
|
|
TRACE( "demuxer %#I64x, stream %u, major %p, format %p\n", demuxer.handle, stream, major, format );
|
|
return get_media_type( unix_demuxer_stream_type, ¶ms, ¶ms.media_type, major, format );
|
|
}
|