From 5f963e7213ec8690f99b389d7dbff0689e99c17e Mon Sep 17 00:00:00 2001 From: tobigun Date: Thu, 23 Jul 2009 19:26:19 +0000 Subject: UTF8 file protocol ('ufile:') added for FFmpeg unicode support git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@1889 b956fd51-792f-4845-bead-9b4dfca2ff2c --- unicode/src/media/UAudioDecoder_FFmpeg.pas | 4 +- unicode/src/media/UMediaCore_FFmpeg.pas | 145 ++++++++++++++++------------- unicode/src/media/UVideo.pas | 5 +- 3 files changed, 84 insertions(+), 70 deletions(-) (limited to 'unicode') diff --git a/unicode/src/media/UAudioDecoder_FFmpeg.pas b/unicode/src/media/UAudioDecoder_FFmpeg.pas index aed4390c..d079afdc 100644 --- a/unicode/src/media/UAudioDecoder_FFmpeg.pas +++ b/unicode/src/media/UAudioDecoder_FFmpeg.pas @@ -289,8 +289,8 @@ begin Self.Filename := Filename; - // open audio file (TODO: unicode support) - if (av_open_input_file(FormatCtx, PAnsiChar(Filename.ToNative), nil, 0, nil) <> 0) then + // use custom 'ufile' protocol for UTF-8 support + if (av_open_input_file(FormatCtx, PAnsiChar('ufile:'+FileName.ToUTF8), nil, 0, nil) <> 0) then begin Log.LogError('av_open_input_file failed: "' + Filename.ToNative + '"', 'UAudio_FFmpeg'); Exit; diff --git a/unicode/src/media/UMediaCore_FFmpeg.pas b/unicode/src/media/UMediaCore_FFmpeg.pas index 5f56b467..b4951fe1 100644 --- a/unicode/src/media/UMediaCore_FFmpeg.pas +++ b/unicode/src/media/UMediaCore_FFmpeg.pas @@ -101,12 +101,29 @@ implementation uses SysUtils; +function FFmpegStreamOpen(h: PURLContext; filename: PChar; flags: cint): cint; cdecl; forward; +function FFmpegStreamRead(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; forward; +function FFmpegStreamWrite(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; forward; +function FFmpegStreamSeek(h: PURLContext; pos: int64; whence: cint): int64; cdecl; forward; +function FFmpegStreamClose(h: PURLContext): cint; cdecl; forward; + +const + UTF8FileProtocol: TURLProtocol = ( + name: 'ufile'; + url_open: FFmpegStreamOpen; + url_read: FFmpegStreamRead; + url_write: FFmpegStreamWrite; + url_seek: FFmpegStreamSeek; + url_close: FFmpegStreamClose; + ); + var Instance: TMediaCore_FFmpeg; constructor TMediaCore_FFmpeg.Create(); begin inherited; + av_register_protocol(@UTF8FileProtocol); AVCodecLock := SDL_CreateMutex(); end; @@ -224,106 +241,102 @@ begin Result := true; end; -function FFmpegStreamRead(Opaque: Pointer; Buffer: PByteArray; BufSize: cint): cint; cdecl; + +{** + * UTF-8 Filename wrapper based on: + * http://www.mail-archive.com/libav-user@mplayerhq.hu/msg02460.html + *} + +function FFmpegStreamOpen(h: PURLContext; filename: PChar; flags: cint): cint; cdecl; var Stream: TStream; + Mode: word; + ProtPrefix: string; + FilePath: IPath; begin - Stream := TStream(Opaque); + // check for protocol prefix ('ufile:') and strip it + ProtPrefix := Format('%s:', [UTF8FileProtocol.name]); + if (StrLComp(filename, PChar(ProtPrefix), Length(ProtPrefix)) = 0) then + begin + Inc(filename, Length(ProtPrefix)); + end; + + FilePath := Path(filename); + + if ((flags and URL_RDWR) <> 0) then + Mode := fmCreate + else if ((flags and URL_WRONLY) <> 0) then + Mode := fmCreate // TODO: fmCreate is Read+Write -> reopen with fmOpenWrite + else + Mode := fmOpenRead; + + Result := 0; + + try + Stream := TBinaryFileStream.Create(FilePath, Mode); + h.priv_data := Stream; + except + Result := AVERROR_NOENT; + end; +end; + +function FFmpegStreamRead(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; +var + Stream: TStream; +begin + Stream := TStream(h.priv_data); if (Stream = nil) then raise EInvalidContainer.Create('FFmpegStreamRead on nil'); try - Result := Stream.Read(Buffer[0], BufSize); + Result := Stream.Read(buf[0], size); except Result := -1; end; end; -function FFmpegStreamWrite(Opaque: Pointer; Buffer: PByteArray; BufSize: cint): cint; cdecl; +function FFmpegStreamWrite(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; var Stream: TStream; begin - Stream := TStream(Opaque); + Stream := TStream(h.priv_data); if (Stream = nil) then raise EInvalidContainer.Create('FFmpegStreamWrite on nil'); try - Result := Stream.Write(Buffer[0], BufSize); + Result := Stream.Write(buf[0], size); except Result := -1; end; end; -function FFmpegStreamSeek(Opaque: Pointer; Offset: cint64; Whence: cint): cint64; cdecl; +function FFmpegStreamSeek(h: PURLContext; pos: int64; whence: cint): int64; cdecl; var Stream : TStream; - Origin : Word; + Origin : TSeekOrigin; begin - Stream := TStream(opaque); + Stream := TStream(h.priv_data); if (Stream = nil) then raise EInvalidContainer.Create('FFmpegStreamSeek on nil'); case whence of - 0 : Origin := soFromBeginning; - 1 : Origin := soFromCurrent; - 2 : Origin := soFromEnd; + 0 {SEEK_SET}: Origin := soBeginning; + 1 {SEEK_CUR}: Origin := soCurrent; + 2 {SEEK_END}: Origin := soEnd; + AVSEEK_SIZE: begin + Result := Stream.Size; + Exit; + end else - Origin := soFromBeginning; + Origin := soBeginning; end; - Result := Stream.Seek(offset,origin); + Result := Stream.Seek(pos, Origin); end; -const - AVFileBufSize = 32768; - -type - PAVFile = ^TAVFile; - TAVFile = record - ByteIOCtx: TByteIOContext; - Buffer: array [0 .. AVFileBufSize-1] of byte; - end; - -function AVOpenInputFile(var ic_ptr: PAVFormatContext; filename: IPath; - fmt: PAVInputFormat; buf_size: cint; - ap: PAVFormatParameters): PAVFile; +function FFmpegStreamClose(h: PURLContext): cint; cdecl; var - ProbeData: TAVProbeData; - FilenameStr: UTF8String; - FileStream: TBinaryFileStream; - AVFile: PAVFile; + Stream : TStream; begin - Result := nil; - New(AVFile); - - // copy UTF-8 string so ProbeData.filename will be valid at av_probe_input_format() - FilenameStr := Filename.ToUTF8; - - if (fmt = nil) then - begin - ProbeData.filename := PChar(FilenameStr); - ProbeData.buf := @AVFile.Buffer; - ProbeData.buf_size := AVFileBufSize; - - fmt := av_probe_input_format(@ProbeData, 1); - if (fmt = nil) then - begin - Dispose(AVFile); - Exit; - end; - end; - - FileStream := TBinaryFileStream.Create(Filename, fmOpenReadWrite); - if (init_put_byte(@AVFile.ByteIOCtx, @AVFile.Buffer, AVFileBufSize, 0, FileStream, - FFmpegStreamRead, FFmpegStreamWrite, FFmpegStreamSeek) < 0) then - begin - Dispose(AVFile); - Exit; - end; - - if (av_open_input_stream(ic_ptr, @AVFile.ByteIOCtx, PChar(FilenameStr), fmt, ap) < 0) then - begin - Dispose(AVFile); - Exit; - end; - - Result := AVFile; + Stream := TStream(h.priv_data); + Stream.Free; + Result := 0; end; diff --git a/unicode/src/media/UVideo.pas b/unicode/src/media/UVideo.pas index de01fd68..8d441e6c 100644 --- a/unicode/src/media/UVideo.pas +++ b/unicode/src/media/UVideo.pas @@ -262,7 +262,8 @@ begin Reset(); - errnum := av_open_input_file(fFormatContext, PChar(FileName.ToNative), nil, 0, nil); + // use custom 'ufile' protocol for UTF-8 support + errnum := av_open_input_file(fFormatContext, PAnsiChar('ufile:'+FileName.ToUTF8), nil, 0, nil); if (errnum <> 0) then begin Log.LogError('Failed to open file "'+ FileName.ToNative +'" ('+FFmpegCore.GetErrorString(errnum)+')'); @@ -435,7 +436,7 @@ begin fAVFrame := nil; fAVFrameRGB := nil; fFrameBuffer := nil; - + if (fCodecContext <> nil) then begin // avcodec_close() is not thread-safe -- cgit v1.2.3