aboutsummaryrefslogtreecommitdiffstats
path: root/unicode/src/media
diff options
context:
space:
mode:
authortobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2009-03-14 21:18:50 +0000
committertobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2009-03-14 21:18:50 +0000
commita92d9807237a024106b67f045822679f7ee4df54 (patch)
tree5f2c2501be7da18d2b82216fe60e645d1a2caaac /unicode/src/media
parentc61a339ad465ecb43897975538a77c3eb87110be (diff)
downloadusdx-a92d9807237a024106b67f045822679f7ee4df54.tar.gz
usdx-a92d9807237a024106b67f045822679f7ee4df54.tar.xz
usdx-a92d9807237a024106b67f045822679f7ee4df54.zip
merge with current trunk (just update)
git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@1634 b956fd51-792f-4845-bead-9b4dfca2ff2c
Diffstat (limited to 'unicode/src/media')
-rw-r--r--unicode/src/media/UAudioConverter.pas14
-rw-r--r--unicode/src/media/UAudioDecoder_Bass.pas6
-rw-r--r--unicode/src/media/UAudioDecoder_FFmpeg.pas26
-rw-r--r--unicode/src/media/UAudioPlayback_Bass.pas18
-rw-r--r--unicode/src/media/UAudioPlayback_SDL.pas14
-rw-r--r--unicode/src/media/UAudioPlayback_SoftMixer.pas38
-rw-r--r--unicode/src/media/UVideo.pas160
-rw-r--r--unicode/src/media/UVisualizer.pas15
8 files changed, 174 insertions, 117 deletions
diff --git a/unicode/src/media/UAudioConverter.pas b/unicode/src/media/UAudioConverter.pas
index 24131b16..657b80dd 100644
--- a/unicode/src/media/UAudioConverter.pas
+++ b/unicode/src/media/UAudioConverter.pas
@@ -70,7 +70,7 @@ type
function Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; override;
destructor Destroy(); override;
- function Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; override;
+ function Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; override;
function GetOutputBufferSize(InputSize: integer): integer; override;
function GetRatio(): double; override;
end;
@@ -87,7 +87,7 @@ type
function Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; override;
destructor Destroy(); override;
- function Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; override;
+ function Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; override;
function GetOutputBufferSize(InputSize: integer): integer; override;
function GetRatio(): double; override;
end;
@@ -103,7 +103,7 @@ type
function Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; override;
destructor Destroy(); override;
- function Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; override;
+ function Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; override;
function GetOutputBufferSize(InputSize: integer): integer; override;
function GetRatio(): double; override;
end;
@@ -173,7 +173,7 @@ begin
Result := cvt.len_ratio;
end;
-function TAudioConverter_SDL.Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer;
+function TAudioConverter_SDL.Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer;
begin
Result := -1;
@@ -242,7 +242,7 @@ begin
inherited;
end;
-function TAudioConverter_FFmpeg.Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer;
+function TAudioConverter_FFmpeg.Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer;
var
InputSampleCount: integer;
OutputSampleCount: integer;
@@ -360,11 +360,11 @@ begin
inherited;
end;
-function TAudioConverter_SRC.Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer;
+function TAudioConverter_SRC.Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer;
var
FloatInputBuffer: PSingle;
FloatOutputBuffer: PSingle;
- TempBuffer: PChar;
+ TempBuffer: PByteArray;
TempSize: integer;
NumSamples: integer;
OutputSize: integer;
diff --git a/unicode/src/media/UAudioDecoder_Bass.pas b/unicode/src/media/UAudioDecoder_Bass.pas
index 3c31175d..6bbdaeaa 100644
--- a/unicode/src/media/UAudioDecoder_Bass.pas
+++ b/unicode/src/media/UAudioDecoder_Bass.pas
@@ -65,7 +65,7 @@ type
function IsEOF(): boolean; override;
function IsError(): boolean; override;
- function ReadData(Buffer: PChar; BufSize: integer): integer; override;
+ function ReadData(Buffer: PByteArray; BufSize: integer): integer; override;
end;
type
@@ -193,7 +193,7 @@ begin
Result := Error;
end;
-function TBassDecodeStream.ReadData(Buffer: PChar; BufSize: integer): integer;
+function TBassDecodeStream.ReadData(Buffer: PByteArray; BufSize: integer): integer;
begin
Result := BASS_ChannelGetData(Handle, Buffer, BufSize);
// check error state (do not handle EOF as error)
@@ -237,7 +237,7 @@ begin
// TODO: use BASS_STREAM_PRESCAN for accurate seeking in VBR-files?
// disadvantage: seeking will slow down.
- Stream := BASS_StreamCreateFile(False, PChar(Filename), 0, 0, BASS_STREAM_DECODE);
+ Stream := BASS_StreamCreateFile(False, PAnsiChar(Filename), 0, 0, BASS_STREAM_DECODE);
if (Stream = 0) then
begin
//Log.LogError(BassCore.ErrorGetString(), 'TAudioDecoder_Bass.Open');
diff --git a/unicode/src/media/UAudioDecoder_FFmpeg.pas b/unicode/src/media/UAudioDecoder_FFmpeg.pas
index 399c2f52..2d221f40 100644
--- a/unicode/src/media/UAudioDecoder_FFmpeg.pas
+++ b/unicode/src/media/UAudioDecoder_FFmpeg.pas
@@ -57,7 +57,6 @@ implementation
uses
Classes,
- SysUtils,
Math,
UMusic,
UIni,
@@ -68,8 +67,9 @@ uses
avio,
mathematics, // used for av_rescale_q
rational,
- UMediaCore_FFmpeg,
SDL,
+ SysUtils,
+ UMediaCore_FFmpeg,
ULog,
UCommon,
UConfig;
@@ -129,14 +129,14 @@ type
// state-vars for DecodeFrame (locked by DecoderLock)
AudioPaket: TAVPacket;
- AudioPaketData: PChar;
+ AudioPaketData: PByteArray;
AudioPaketSize: integer;
AudioPaketSilence: integer; // number of bytes of silence to return
// state-vars for AudioCallback (locked by DecoderLock)
AudioBufferPos: integer;
AudioBufferSize: integer;
- AudioBuffer: PChar;
+ AudioBuffer: PByteArray;
Filename: string;
@@ -153,7 +153,7 @@ type
procedure PauseParser();
procedure ResumeParser();
- function DecodeFrame(Buffer: PChar; BufferSize: integer): integer;
+ function DecodeFrame(Buffer: PByteArray; BufferSize: integer): integer;
procedure FlushCodecBuffers();
procedure PauseDecoder();
procedure ResumeDecoder();
@@ -173,11 +173,11 @@ type
function IsEOF(): boolean; override;
function IsError(): boolean; override;
- function ReadData(Buffer: PChar; BufferSize: integer): integer; override;
+ function ReadData(Buffer: PByteArray; BufferSize: integer): integer; override;
end;
type
- TAudioDecoder_FFmpeg = class( TInterfacedObject, IAudioDecoder )
+ TAudioDecoder_FFmpeg = class(TInterfacedObject, IAudioDecoder)
public
function GetName: string;
@@ -289,7 +289,7 @@ begin
Self.Filename := Filename;
// open audio file
- if (av_open_input_file(FormatCtx, PChar(Filename), nil, 0, nil) <> 0) then
+ if (av_open_input_file(FormatCtx, PAnsiChar(Filename), nil, 0, nil) <> 0) then
begin
Log.LogError('av_open_input_file failed: "' + Filename + '"', 'UAudio_FFmpeg');
Exit;
@@ -310,7 +310,7 @@ begin
FormatCtx^.pb.eof_reached := 0;
{$IFDEF DebugFFmpegDecode}
- dump_format(FormatCtx, 0, pchar(Filename), 0);
+ dump_format(FormatCtx, 0, PAnsiChar(Filename), 0);
{$ENDIF}
AudioStreamIndex := FFmpegCore.FindAudioStreamIndex(FormatCtx);
@@ -862,7 +862,7 @@ begin
end;
end;
-function TFFmpegDecodeStream.DecodeFrame(Buffer: PChar; BufferSize: integer): integer;
+function TFFmpegDecodeStream.DecodeFrame(Buffer: PByteArray; BufferSize: integer): integer;
var
PaketDecodedSize: integer; // size of packet data used for decoding
DataSize: integer; // size of output data decoded by FFmpeg
@@ -945,7 +945,7 @@ begin
Exit;
// handle Status-packet
- if (PChar(AudioPaket.data) = STATUS_PACKET) then
+ if (PAnsiChar(AudioPaket.data) = STATUS_PACKET) then
begin
AudioPaket.data := nil;
AudioPaketData := nil;
@@ -986,7 +986,7 @@ begin
Continue;
end;
- AudioPaketData := PChar(AudioPaket.data);
+ AudioPaketData := AudioPaket.data;
AudioPaketSize := AudioPaket.size;
// if available, update the stream position to the presentation time of this package
@@ -1005,7 +1005,7 @@ begin
end;
end;
-function TFFmpegDecodeStream.ReadData(Buffer: PChar; BufferSize: integer): integer;
+function TFFmpegDecodeStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer;
var
CopyByteCount: integer; // number of bytes to copy
RemainByteCount: integer; // number of bytes left (remain) to read
diff --git a/unicode/src/media/UAudioPlayback_Bass.pas b/unicode/src/media/UAudioPlayback_Bass.pas
index d68ac1d4..923c1d7b 100644
--- a/unicode/src/media/UAudioPlayback_Bass.pas
+++ b/unicode/src/media/UAudioPlayback_Bass.pas
@@ -37,7 +37,6 @@ implementation
uses
Classes,
- SysUtils,
Math,
UIni,
UMain,
@@ -46,7 +45,8 @@ uses
UAudioCore_Bass,
ULog,
sdl,
- bass;
+ bass,
+ SysUtils;
type
PHDSP = ^HDSP;
@@ -90,7 +90,7 @@ type
function GetAudioFormatInfo(): TAudioFormatInfo; override;
- function ReadData(Buffer: PChar; BufferSize: integer): integer;
+ function ReadData(Buffer: PByteArray; BufferSize: integer): integer;
property EOF: boolean READ IsEOF;
end;
@@ -106,8 +106,8 @@ type
function Open(ChannelMap: integer; FormatInfo: TAudioFormatInfo): boolean; override;
procedure Close(); override;
- procedure WriteData(Buffer: PChar; BufferSize: integer); override;
- function ReadData(Buffer: PChar; BufferSize: integer): integer; override;
+ procedure WriteData(Buffer: PByteArray; BufferSize: integer); override;
+ function ReadData(Buffer: PByteArray; BufferSize: integer): integer; override;
function IsEOF(): boolean; override;
function IsError(): boolean; override;
end;
@@ -163,14 +163,14 @@ begin
Result := BytesRead;
end;
-function TBassPlaybackStream.ReadData(Buffer: PChar; BufferSize: integer): integer;
+function TBassPlaybackStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer;
var
AdjustedSize: integer;
RequestedSourceSize, SourceSize: integer;
SkipCount: integer;
SourceFormatInfo: TAudioFormatInfo;
FrameSize: integer;
- PadFrame: PChar;
+ PadFrame: PByteArray;
//Info: BASS_INFO;
//Latency: double;
begin
@@ -610,7 +610,7 @@ begin
inherited Close();
end;
-procedure TBassVoiceStream.WriteData(Buffer: PChar; BufferSize: integer);
+procedure TBassVoiceStream.WriteData(Buffer: PByteArray; BufferSize: integer);
var QueueSize: DWORD;
begin
if ((Handle <> 0) and (BufferSize > 0)) then
@@ -626,7 +626,7 @@ begin
end;
// Note: we do not need the read-function for the BASS implementation
-function TBassVoiceStream.ReadData(Buffer: PChar; BufferSize: integer): integer;
+function TBassVoiceStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer;
begin
Result := -1;
end;
diff --git a/unicode/src/media/UAudioPlayback_SDL.pas b/unicode/src/media/UAudioPlayback_SDL.pas
index b0887676..8403ef03 100644
--- a/unicode/src/media/UAudioPlayback_SDL.pas
+++ b/unicode/src/media/UAudioPlayback_SDL.pas
@@ -33,16 +33,14 @@ interface
{$I switches.inc}
-uses
- Classes,
- SysUtils,
- UMusic;
-
implementation
uses
+ Classes,
sdl,
+ SysUtils,
UAudioPlayback_SoftMixer,
+ UMusic,
ULog,
UIni,
UMain;
@@ -60,13 +58,13 @@ type
function GetLatency(): double; override;
public
function GetName: String; override;
- procedure MixBuffers(dst, src: PChar; size: Cardinal; volume: Single); override;
+ procedure MixBuffers(dst, src: PByteArray; size: Cardinal; volume: Single); override;
end;
{ TAudioPlayback_SDL }
-procedure SDLAudioCallback(userdata: Pointer; stream: PChar; len: integer); cdecl;
+procedure SDLAudioCallback(userdata: Pointer; stream: PByteArray; len: integer); cdecl;
var
Engine: TAudioPlayback_SDL;
begin
@@ -172,7 +170,7 @@ begin
Result := Latency;
end;
-procedure TAudioPlayback_SDL.MixBuffers(dst, src: PChar; size: Cardinal; volume: Single);
+procedure TAudioPlayback_SDL.MixBuffers(dst, src: PByteArray; size: Cardinal; volume: Single);
begin
SDL_MixAudio(PUInt8(dst), PUInt8(src), size, Round(volume * SDL_MIX_MAXVOLUME));
end;
diff --git a/unicode/src/media/UAudioPlayback_SoftMixer.pas b/unicode/src/media/UAudioPlayback_SoftMixer.pas
index f3797dd6..c8da6bcd 100644
--- a/unicode/src/media/UAudioPlayback_SoftMixer.pas
+++ b/unicode/src/media/UAudioPlayback_SoftMixer.pas
@@ -35,8 +35,8 @@ interface
uses
Classes,
- SysUtils,
sdl,
+ SysUtils,
URingBuffer,
UMusic,
UAudioPlaybackBase;
@@ -48,12 +48,12 @@ type
private
Engine: TAudioPlayback_SoftMixer;
- SampleBuffer: PChar;
+ SampleBuffer: PByteArray;
SampleBufferSize: integer;
SampleBufferCount: integer; // number of available bytes in SampleBuffer
SampleBufferPos: cardinal;
- SourceBuffer: PChar;
+ SourceBuffer: PByteArray;
SourceBufferSize: integer;
SourceBufferCount: integer; // number of available bytes in SourceBuffer
@@ -70,7 +70,7 @@ type
procedure Reset();
- procedure ApplySoundEffects(Buffer: PChar; BufferSize: integer);
+ procedure ApplySoundEffects(Buffer: PByteArray; BufferSize: integer);
function InitFormatConversion(): boolean;
procedure FlushBuffers();
@@ -100,7 +100,7 @@ type
function GetAudioFormatInfo(): TAudioFormatInfo; override;
- function ReadData(Buffer: PChar; BufferSize: integer): integer;
+ function ReadData(Buffer: PByteArray; BufferSize: integer): integer;
function GetPCMData(var Data: TPCMData): Cardinal; override;
procedure GetFFTData(var Data: TFFTData); override;
@@ -114,7 +114,7 @@ type
Engine: TAudioPlayback_SoftMixer;
ActiveStreams: TList;
- MixerBuffer: PChar;
+ MixerBuffer: PByteArray;
InternalLock: PSDL_Mutex;
AppVolume: single;
@@ -129,7 +129,7 @@ type
destructor Destroy(); override;
procedure AddStream(Stream: TAudioPlaybackStream);
procedure RemoveStream(Stream: TAudioPlaybackStream);
- function ReadData(Buffer: PChar; BufferSize: integer): integer;
+ function ReadData(Buffer: PByteArray; BufferSize: integer): integer;
property Volume: single read GetVolume write SetVolume;
end;
@@ -144,7 +144,7 @@ type
function StartAudioPlaybackEngine(): boolean; virtual; abstract;
procedure StopAudioPlaybackEngine(); virtual; abstract;
function FinalizeAudioPlaybackEngine(): boolean; virtual; abstract;
- procedure AudioCallback(Buffer: PChar; Size: integer); {$IFDEF HasInline}inline;{$ENDIF}
+ procedure AudioCallback(Buffer: PByteArray; Size: integer); {$IFDEF HasInline}inline;{$ENDIF}
function CreatePlaybackStream(): TAudioPlaybackStream; override;
public
@@ -159,7 +159,7 @@ type
function GetMixer(): TAudioMixerStream; {$IFDEF HasInline}inline;{$ENDIF}
function GetAudioFormatInfo(): TAudioFormatInfo;
- procedure MixBuffers(DstBuffer, SrcBuffer: PChar; Size: Cardinal; Volume: Single); virtual;
+ procedure MixBuffers(DstBuffer, SrcBuffer: PByteArray; Size: Cardinal; Volume: Single); virtual;
end;
type
@@ -174,8 +174,8 @@ type
function Open(ChannelMap: integer; FormatInfo: TAudioFormatInfo): boolean; override;
procedure Close(); override;
- procedure WriteData(Buffer: PChar; BufferSize: integer); override;
- function ReadData(Buffer: PChar; BufferSize: integer): integer; override;
+ procedure WriteData(Buffer: PByteArray; BufferSize: integer); override;
+ function ReadData(Buffer: PByteArray; BufferSize: integer): integer; override;
function IsEOF(): boolean; override;
function IsError(): boolean; override;
end;
@@ -276,7 +276,7 @@ begin
Unlock();
end;
-function TAudioMixerStream.ReadData(Buffer: PChar; BufferSize: integer): integer;
+function TAudioMixerStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer;
var
i: integer;
Size: integer;
@@ -545,7 +545,7 @@ begin
SourceBufferCount := 0;
end;
-procedure TGenericPlaybackStream.ApplySoundEffects(Buffer: PChar; BufferSize: integer);
+procedure TGenericPlaybackStream.ApplySoundEffects(Buffer: PByteArray; BufferSize: integer);
var
i: integer;
begin
@@ -558,7 +558,7 @@ begin
end;
end;
-function TGenericPlaybackStream.ReadData(Buffer: PChar; BufferSize: integer): integer;
+function TGenericPlaybackStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer;
var
ConversionInputCount: integer;
ConversionOutputSize: integer; // max. number of converted data (= buffer size)
@@ -573,7 +573,7 @@ var
SkipSourceCount: integer; // number of source-data bytes to skip
FillCount: integer; // number of bytes to fill with padding data
CopyCount: integer;
- PadFrame: PChar;
+ PadFrame: PByteArray;
i: integer;
begin
Result := -1;
@@ -986,7 +986,7 @@ begin
inherited Close();
end;
-procedure TGenericVoiceStream.WriteData(Buffer: PChar; BufferSize: integer);
+procedure TGenericVoiceStream.WriteData(Buffer: PByteArray; BufferSize: integer);
begin
// lock access to buffer
SDL_mutexP(BufferLock);
@@ -999,7 +999,7 @@ begin
end;
end;
-function TGenericVoiceStream.ReadData(Buffer: PChar; BufferSize: integer): integer;
+function TGenericVoiceStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer;
begin
Result := -1;
@@ -1059,7 +1059,7 @@ begin
Result := true;
end;
-procedure TAudioPlayback_SoftMixer.AudioCallback(Buffer: PChar; Size: integer);
+procedure TAudioPlayback_SoftMixer.AudioCallback(Buffer: PByteArray; Size: integer);
begin
MixerStream.ReadData(Buffer, Size);
end;
@@ -1102,7 +1102,7 @@ begin
MixerStream.Volume := Volume;
end;
-procedure TAudioPlayback_SoftMixer.MixBuffers(DstBuffer, SrcBuffer: PChar; Size: Cardinal; Volume: Single);
+procedure TAudioPlayback_SoftMixer.MixBuffers(DstBuffer, SrcBuffer: PByteArray; Size: Cardinal; Volume: Single);
var
SampleIndex: Cardinal;
SampleInt: Integer;
diff --git a/unicode/src/media/UVideo.pas b/unicode/src/media/UVideo.pas
index 06577d9e..35f8ab4d 100644
--- a/unicode/src/media/UVideo.pas
+++ b/unicode/src/media/UVideo.pas
@@ -126,6 +126,7 @@ type
fFrameBuffer: PByte; //**< stores a FFmpeg video frame
fFrameTex: GLuint; //**< OpenGL texture for FrameBuffer
+ fFrameTexValid: boolean; //**< if true, fFrameTex contains the current frame
fTexWidth, fTexHeight: cardinal;
{$IFDEF UseSWScale}
@@ -137,11 +138,11 @@ type
fTimeBase: extended; //**< FFmpeg time base per time unit
fTime: extended; //**< video time position (absolute)
- fLoopTime: extended; //**< video time position (relative to current loop)
+ fLoopTime: extended; //**< start time of the current loop
procedure Reset();
- function DecodeFrame(var AVPacket: TAVPacket; out pts: double): boolean;
- procedure SynchronizeVideo(Frame: PAVFrame; var pts: double);
+ function DecodeFrame(): boolean;
+ procedure SynchronizeTime(Frame: PAVFrame; var pts: double);
procedure GetVideoRect(var ScreenRect, TexRect: TRectCoords);
@@ -240,6 +241,7 @@ begin
fTime := 0;
fStream := nil;
fStreamIndex := -1;
+ fFrameTexValid := false;
fEOF := false;
@@ -453,7 +455,7 @@ begin
fOpened := False;
end;
-procedure TVideoPlayback_FFmpeg.SynchronizeVideo(Frame: PAVFrame; var pts: double);
+procedure TVideoPlayback_FFmpeg.SynchronizeTime(Frame: PAVFrame; var pts: double);
var
FrameDelay: double;
begin
@@ -473,12 +475,21 @@ begin
fTime := fTime + FrameDelay;
end;
-function TVideoPlayback_FFmpeg.DecodeFrame(var AVPacket: TAVPacket; out pts: double): boolean;
+{**
+ * Decode a new frame from the video stream.
+ * The decoded frame is stored in fAVFrame. fTime is updated to the new frame's
+ * time.
+ * @param pts will be updated to the presentation time of the decoded frame.
+ * returns true if a frame could be decoded. False if an error or EOF occured.
+ *}
+function TVideoPlayback_FFmpeg.DecodeFrame(): boolean;
var
FrameFinished: Integer;
VideoPktPts: int64;
pbIOCtx: PByteIOContext;
errnum: integer;
+ AVPacket: TAVPacket;
+ pts: double;
begin
Result := false;
FrameFinished := 0;
@@ -521,8 +532,17 @@ begin
end;
// no error -> wait for user input
- SDL_Delay(100);
+{
+ SDL_Delay(100); // initial version, left for documentation
continue;
+}
+
+ // Patch by Hawkear:
+ // Why should this function loop in an endless loop if there is an error?
+ // This runs in the main thread, so it halts the whole program
+ // Therefore, it is better to exit when an error occurs
+ Exit;
+
end;
// if we got a packet from the video stream, then decode it
@@ -555,9 +575,9 @@ begin
end;
pts := pts * av_q2d(fStream^.time_base);
- // synchronize on each complete frame
+ // synchronize time on each complete frame
if (frameFinished <> 0) then
- SynchronizeVideo(fAVFrame, pts);
+ SynchronizeTime(fAVFrame, pts);
end;
// free the packet from av_read_frame
@@ -569,13 +589,12 @@ end;
procedure TVideoPlayback_FFmpeg.GetFrame(Time: Extended);
var
- AVPacket: TAVPacket;
errnum: Integer;
- myTime: Extended;
+ NewTime: Extended;
TimeDifference: Extended;
DropFrameCount: Integer;
- pts: double;
i: Integer;
+ Success: boolean;
const
FRAME_DROPCOUNT = 3;
begin
@@ -585,41 +604,50 @@ begin
if fPaused then
Exit;
- // current time, relative to last fLoop (if any)
- myTime := Time - fLoopTime;
- // time since the last frame was returned
- TimeDifference := myTime - fTime;
+ // requested stream position (relative to the last loop's start)
+ NewTime := Time - fLoopTime;
- {$IFDEF DebugDisplay}
- DebugWriteln('Time: '+inttostr(floor(Time*1000)) + sLineBreak +
- 'VideoTime: '+inttostr(floor(fTime*1000)) + sLineBreak +
- 'TimeBase: '+inttostr(floor(fTimeBase*1000)) + sLineBreak +
- 'TimeDiff: '+inttostr(floor(TimeDifference*1000)));
- {$endif}
-
- // check if a new frame is needed
- if (fTime <> 0) and (TimeDifference < fTimeBase) then
+ // check if current texture still contains the active frame
+ if (fFrameTexValid) then
begin
- {$ifdef DebugFrames}
- // frame delay debug display
- GoldenRec.Spawn(200,15,1,16,0,-1,ColoredStar,$00ff00);
- {$endif}
+ // time since the last frame was returned
+ TimeDifference := NewTime - fTime;
{$IFDEF DebugDisplay}
- DebugWriteln('not getting new frame' + sLineBreak +
- 'Time: '+inttostr(floor(Time*1000)) + sLineBreak +
- 'VideoTime: '+inttostr(floor(fTime*1000)) + sLineBreak +
- 'TimeBase: '+inttostr(floor(fTimeBase*1000)) + sLineBreak +
- 'TimeDiff: '+inttostr(floor(TimeDifference*1000)));
+ DebugWriteln('Time: '+inttostr(floor(Time*1000)) + sLineBreak +
+ 'VideoTime: '+inttostr(floor(fTime*1000)) + sLineBreak +
+ 'TimeBase: '+inttostr(floor(fTimeBase*1000)) + sLineBreak +
+ 'TimeDiff: '+inttostr(floor(TimeDifference*1000)));
{$endif}
- // we do not need a new frame now
- Exit;
+ // check if last time is more than one frame in the past
+ if (TimeDifference < fTimeBase) then
+ begin
+ {$ifdef DebugFrames}
+ // frame delay debug display
+ GoldenRec.Spawn(200,15,1,16,0,-1,ColoredStar,$00ff00);
+ {$endif}
+
+ {$IFDEF DebugDisplay}
+ DebugWriteln('not getting new frame' + sLineBreak +
+ 'Time: '+inttostr(floor(Time*1000)) + sLineBreak +
+ 'VideoTime: '+inttostr(floor(fTime*1000)) + sLineBreak +
+ 'TimeBase: '+inttostr(floor(fTimeBase*1000)) + sLineBreak +
+ 'TimeDiff: '+inttostr(floor(TimeDifference*1000)));
+ {$endif}
+
+ // we do not need a new frame now
+ Exit;
+ end;
end;
- // update video-time to the next frame
- fTime := fTime + fTimeBase;
- TimeDifference := myTime - fTime;
+ {$IFDEF VideoBenchmark}
+ Log.BenchmarkStart(15);
+ {$ENDIF}
+
+ // fetch new frame (updates fTime)
+ Success := DecodeFrame();
+ TimeDifference := NewTime - fTime;
// check if we have to skip frames
if (TimeDifference >= FRAME_DROPCOUNT*fTimeBase) then
@@ -640,19 +668,18 @@ begin
// skip half of the frames, this is much smoother than to skip all at once
for i := 1 to DropFrameCount (*div 2*) do
- DecodeFrame(AVPacket, pts);
+ Success := DecodeFrame();
end;
- {$IFDEF VideoBenchmark}
- Log.BenchmarkStart(15);
- {$ENDIF}
-
- if (not DecodeFrame(AVPacket, pts)) then
+ // check if we got an EOF or error
+ if (not Success) then
begin
if fLoop then
begin
- // Record the time we looped. This is used to keep the loops in time. otherwise they speed
+ // we have to loop, so rewind
SetPosition(0);
+ // record the start-time of the current loop, so we can
+ // determine the position in the stream (fTime-fLoopTime) later.
fLoopTime := Time;
end;
Exit;
@@ -695,6 +722,9 @@ begin
fCodecContext^.width, fCodecContext^.height,
PIXEL_FMT_OPENGL, GL_UNSIGNED_BYTE, fAVFrameRGB^.data[0]);
+ if (not fFrameTexValid) then
+ fFrameTexValid := true;
+
{$ifdef DebugFrames}
//frame decode debug display
GoldenRec.Spawn(200, 35, 1, 16, 0, -1, ColoredStar, $ffff00);
@@ -738,7 +768,9 @@ begin
acoLetterBox: begin
ScaledVideoWidth := RenderW;
ScaledVideoHeight := RenderH * ScreenAspect/fAspect;
- end;
+ end
+ else
+ raise Exception.Create('Unhandled aspect correction!');
end;
// center video
@@ -762,8 +794,18 @@ var
begin
// have a nice black background to draw on
// (even if there were errors opening the vid)
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+ // TODO: Philipp: IMO TVideoPlayback should not clear the screen at
+ // all, because clearing is already done by the background class
+ // at this moment.
+ if (Screen = 1) then
+ begin
+ // It is important that we just clear once before we start
+ // drawing the first screen otherwise the first screen
+ // would be cleared by the drawgl called when the second
+ // screen is drawn
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+ end;
// exit if there's nothing to draw
if (not fOpened) then
@@ -858,6 +900,14 @@ procedure TVideoPlayback_FFmpeg.Stop;
begin
end;
+{**
+ * Sets the stream's position.
+ * The stream is set to the first keyframe with timestamp <= Time.
+ * Note that fTime is set to Time no matter if the actual position seeked to is
+ * at Time or the time of a preceding keyframe. fTime will be updated to the
+ * actual frame time when GetFrame() is called the next time.
+ * @param Time new position in seconds
+ *}
procedure TVideoPlayback_FFmpeg.SetPosition(Time: real);
var
SeekFlags: integer;
@@ -871,13 +921,18 @@ begin
// TODO: handle fLoop-times
//Time := Time mod VideoDuration;
- // backward seeking might fail without AVSEEK_FLAG_BACKWARD
- SeekFlags := AVSEEK_FLAG_ANY;
- if (Time < fTime) then
- SeekFlags := SeekFlags or AVSEEK_FLAG_BACKWARD;
+ // Do not use the AVSEEK_FLAG_ANY here. It will seek to any frame, even
+ // non keyframes (P-/B-frames). It will produce corrupted video frames as
+ // FFmpeg does not use the information of the preceding I-frame.
+ // The picture might be gray or green until the next keyframe occurs.
+ // Instead seek the first keyframe smaller than the requested time
+ // (AVSEEK_FLAG_BACKWARD). As this can be some seconds earlier than the
+ // requested time, let the sync in GetFrame() do its job.
+ SeekFlags := AVSEEK_FLAG_BACKWARD;
fTime := Time;
fEOF := false;
+ fFrameTexValid := false;
if (av_seek_frame(fFormatContext, fStreamIndex, Floor(Time/fTimeBase), SeekFlags) < 0) then
begin
@@ -890,7 +945,6 @@ end;
function TVideoPlayback_FFmpeg.GetPosition: real;
begin
- // TODO: return video-position in seconds
Result := fTime;
end;
diff --git a/unicode/src/media/UVisualizer.pas b/unicode/src/media/UVisualizer.pas
index 9af6d8c2..37e0268a 100644
--- a/unicode/src/media/UVisualizer.pas
+++ b/unicode/src/media/UVisualizer.pas
@@ -484,7 +484,11 @@ begin
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
- gluOrtho2D(0, 1, 0, 1);
+ // Use count of screens instead of 1 for the right corner
+ // otherwise we would draw the visualization streched over both screens
+ // another point is that we draw over the at this time drawn first
+ // screen, if Screen = 2
+ gluOrtho2D(0, Screens, 0, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
@@ -496,11 +500,12 @@ begin
glColor4f(1, 1, 1, 1);
// draw projectM frame
+ // Screen is 1 to 2. So current screen is from (Screen - 1) to (Screen)
glBegin(GL_QUADS);
- glTexCoord2f(0, 0); glVertex2f(0, 0);
- glTexCoord2f(1, 0); glVertex2f(1, 0);
- glTexCoord2f(1, 1); glVertex2f(1, 1);
- glTexCoord2f(0, 1); glVertex2f(0, 1);
+ glTexCoord2f(0, 0); glVertex2f((Screen - 1), 0);
+ glTexCoord2f(1, 0); glVertex2f(Screen, 0);
+ glTexCoord2f(1, 1); glVertex2f(Screen, 1);
+ glTexCoord2f(0, 1); glVertex2f((Screen - 1), 1);
glEnd();
glDisable(GL_TEXTURE_2D);