aboutsummaryrefslogtreecommitdiffstats
path: root/us_maker_edition/src/media
diff options
context:
space:
mode:
Diffstat (limited to 'us_maker_edition/src/media')
-rw-r--r--us_maker_edition/src/media/UAudioDecoder_FFmpeg.pas551
-rw-r--r--us_maker_edition/src/media/UAudioInput_Bass.pas7
-rw-r--r--us_maker_edition/src/media/UMediaCore_FFmpeg.pas75
-rw-r--r--us_maker_edition/src/media/UMedia_dummy.pas126
-rw-r--r--us_maker_edition/src/media/UVideo.pas521
-rw-r--r--us_maker_edition/src/media/UVisualizer.pas129
6 files changed, 1027 insertions, 382 deletions
diff --git a/us_maker_edition/src/media/UAudioDecoder_FFmpeg.pas b/us_maker_edition/src/media/UAudioDecoder_FFmpeg.pas
index 7ca98885..c64d79c2 100644
--- a/us_maker_edition/src/media/UAudioDecoder_FFmpeg.pas
+++ b/us_maker_edition/src/media/UAudioDecoder_FFmpeg.pas
@@ -86,60 +86,60 @@ const
type
TFFmpegDecodeStream = class(TAudioDecodeStream)
private
- StateLock: PSDL_Mutex;
+ fStateLock: PSDL_Mutex;
- EOFState: boolean; // end-of-stream flag (locked by StateLock)
- ErrorState: boolean; // error flag (locked by StateLock)
+ fEOFState: boolean; // end-of-stream flag (locked by StateLock)
+ fErrorState: boolean; // error flag (locked by StateLock)
- QuitRequest: boolean; // (locked by StateLock)
- ParserIdleCond: PSDL_Cond;
+ fQuitRequest: boolean; // (locked by StateLock)
+ fParserIdleCond: PSDL_Cond;
// parser pause/resume data
- ParserLocked: boolean;
- ParserPauseRequestCount: integer;
- ParserUnlockedCond: PSDL_Cond;
- ParserResumeCond: PSDL_Cond;
-
- SeekRequest: boolean; // (locked by StateLock)
- SeekFlags: integer; // (locked by StateLock)
- SeekPos: double; // stream position to seek for (in secs) (locked by StateLock)
- SeekFlush: boolean; // true if the buffers should be flushed after seeking (locked by StateLock)
+ fParserLocked: boolean;
+ fParserPauseRequestCount: integer;
+ fParserUnlockedCond: PSDL_Cond;
+ fParserResumeCond: PSDL_Cond;
+
+ fSeekRequest: boolean; // (locked by StateLock)
+ fSeekFlags: integer; // (locked by StateLock)
+ fSeekPos: double; // stream position to seek for (in secs) (locked by StateLock)
+ fSeekFlush: boolean; // true if the buffers should be flushed after seeking (locked by StateLock)
SeekFinishedCond: PSDL_Cond;
- Loop: boolean; // (locked by StateLock)
+ fLoop: boolean; // (locked by StateLock)
- ParseThread: PSDL_Thread;
- PacketQueue: TPacketQueue;
+ fParseThread: PSDL_Thread;
+ fPacketQueue: TPacketQueue;
- FormatInfo: TAudioFormatInfo;
+ fFormatInfo: TAudioFormatInfo;
// FFmpeg specific data
- FormatCtx: PAVFormatContext;
- CodecCtx: PAVCodecContext;
- Codec: PAVCodec;
+ fFormatCtx: PAVFormatContext;
+ fCodecCtx: PAVCodecContext;
+ fCodec: PAVCodec;
- AudioStreamIndex: integer;
- AudioStream: PAVStream;
- AudioStreamPos: double; // stream position in seconds (locked by DecoderLock)
+ fAudioStreamIndex: integer;
+ fAudioStream: PAVStream;
+ fAudioStreamPos: double; // stream position in seconds (locked by DecoderLock)
// decoder pause/resume data
- DecoderLocked: boolean;
- DecoderPauseRequestCount: integer;
- DecoderUnlockedCond: PSDL_Cond;
- DecoderResumeCond: PSDL_Cond;
+ fDecoderLocked: boolean;
+ fDecoderPauseRequestCount: integer;
+ fDecoderUnlockedCond: PSDL_Cond;
+ fDecoderResumeCond: PSDL_Cond;
// state-vars for DecodeFrame (locked by DecoderLock)
- AudioPaket: TAVPacket;
- AudioPaketData: PByteArray;
- AudioPaketSize: integer;
- AudioPaketSilence: integer; // number of bytes of silence to return
+ fAudioPaket: TAVPacket;
+ fAudioPaketData: PByteArray;
+ fAudioPaketSize: integer;
+ fAudioPaketSilence: integer; // number of bytes of silence to return
// state-vars for AudioCallback (locked by DecoderLock)
- AudioBufferPos: integer;
- AudioBufferSize: integer;
- AudioBuffer: PByteArray;
+ fAudioBufferPos: integer;
+ fAudioBufferSize: integer;
+ fAudioBuffer: PByteArray;
- Filename: IPath;
+ fFilename: IPath;
procedure SetPositionIntern(Time: real; Flush: boolean; Blocking: boolean);
procedure SetEOF(State: boolean); {$IFDEF HasInline}inline;{$ENDIF}
@@ -199,13 +199,13 @@ constructor TFFmpegDecodeStream.Create();
begin
inherited Create();
- StateLock := SDL_CreateMutex();
- ParserUnlockedCond := SDL_CreateCond();
- ParserResumeCond := SDL_CreateCond();
- ParserIdleCond := SDL_CreateCond();
+ fStateLock := SDL_CreateMutex();
+ fParserUnlockedCond := SDL_CreateCond();
+ fParserResumeCond := SDL_CreateCond();
+ fParserIdleCond := SDL_CreateCond();
SeekFinishedCond := SDL_CreateCond();
- DecoderUnlockedCond := SDL_CreateCond();
- DecoderResumeCond := SDL_CreateCond();
+ fDecoderUnlockedCond := SDL_CreateCond();
+ fDecoderResumeCond := SDL_CreateCond();
// according to the documentation of avcodec_decode_audio(2), sample-data
// should be aligned on a 16 byte boundary. Otherwise internal calls
@@ -222,33 +222,33 @@ begin
// AudioBuffer was not aligned to a 16 byte boundary. The {$ALIGN x} directive
// was not applicable as Delphi in contrast to FPC provides at most 8 byte
// alignment ({$ALIGN 16} is not supported) by this directive.
- AudioBuffer := GetAlignedMem(AUDIO_BUFFER_SIZE, 16);
+ fAudioBuffer := GetAlignedMem(AUDIO_BUFFER_SIZE, 16);
Reset();
end;
procedure TFFmpegDecodeStream.Reset();
begin
- ParseThread := nil;
+ fParseThread := nil;
- EOFState := false;
- ErrorState := false;
- Loop := false;
- QuitRequest := false;
+ fEOFState := false;
+ fErrorState := false;
+ fLoop := false;
+ fQuitRequest := false;
- AudioPaketData := nil;
- AudioPaketSize := 0;
- AudioPaketSilence := 0;
+ fAudioPaketData := nil;
+ fAudioPaketSize := 0;
+ fAudioPaketSilence := 0;
- AudioBufferPos := 0;
- AudioBufferSize := 0;
+ fAudioBufferPos := 0;
+ fAudioBufferSize := 0;
- ParserLocked := false;
- ParserPauseRequestCount := 0;
- DecoderLocked := false;
- DecoderPauseRequestCount := 0;
+ fParserLocked := false;
+ fParserPauseRequestCount := 0;
+ fDecoderLocked := false;
+ fDecoderPauseRequestCount := 0;
- FillChar(AudioPaket, SizeOf(TAVPacket), 0);
+ FillChar(fAudioPaket, SizeOf(TAVPacket), 0);
end;
{*
@@ -258,15 +258,15 @@ destructor TFFmpegDecodeStream.Destroy();
begin
Close();
- SDL_DestroyMutex(StateLock);
- SDL_DestroyCond(ParserUnlockedCond);
- SDL_DestroyCond(ParserResumeCond);
- SDL_DestroyCond(ParserIdleCond);
+ SDL_DestroyMutex(fStateLock);
+ SDL_DestroyCond(fParserUnlockedCond);
+ SDL_DestroyCond(fParserResumeCond);
+ SDL_DestroyCond(fParserIdleCond);
SDL_DestroyCond(SeekFinishedCond);
- SDL_DestroyCond(DecoderUnlockedCond);
- SDL_DestroyCond(DecoderResumeCond);
+ SDL_DestroyCond(fDecoderUnlockedCond);
+ SDL_DestroyCond(fDecoderResumeCond);
- FreeAlignedMem(AudioBuffer);
+ FreeAlignedMem(fAudioBuffer);
inherited;
end;
@@ -287,20 +287,20 @@ begin
Exit;
end;
- Self.Filename := Filename;
+ Self.fFilename := Filename;
// use custom 'ufile' protocol for UTF-8 support
- if (av_open_input_file(FormatCtx, PAnsiChar('ufile:'+FileName.ToUTF8), nil, 0, nil) <> 0) then
+ if (av_open_input_file(fFormatCtx, PAnsiChar('ufile:'+FileName.ToUTF8), nil, 0, nil) <> 0) then
begin
Log.LogError('av_open_input_file failed: "' + Filename.ToNative + '"', 'UAudio_FFmpeg');
Exit;
end;
// generate PTS values if they do not exist
- FormatCtx^.flags := FormatCtx^.flags or AVFMT_FLAG_GENPTS;
+ fFormatCtx^.flags := fFormatCtx^.flags or AVFMT_FLAG_GENPTS;
// retrieve stream information
- if (av_find_stream_info(FormatCtx) < 0) then
+ if (av_find_stream_info(fFormatCtx) < 0) then
begin
Log.LogError('av_find_stream_info failed: "' + Filename.ToNative + '"', 'UAudio_FFmpeg');
Close();
@@ -308,14 +308,14 @@ begin
end;
// FIXME: hack used by ffplay. Maybe should not use url_feof() to test for the end
- FormatCtx^.pb.eof_reached := 0;
+ fFormatCtx^.pb.eof_reached := 0;
{$IFDEF DebugFFmpegDecode}
- dump_format(FormatCtx, 0, PAnsiChar(Filename.ToNative), 0);
+ dump_format(fFormatCtx, 0, PAnsiChar(Filename.ToNative), 0);
{$ENDIF}
- AudioStreamIndex := FFmpegCore.FindAudioStreamIndex(FormatCtx);
- if (AudioStreamIndex < 0) then
+ fAudioStreamIndex := FFmpegCore.FindAudioStreamIndex(fFormatCtx);
+ if (fAudioStreamIndex < 0) then
begin
Log.LogError('FindAudioStreamIndex: No Audio-stream found "' + Filename.ToNative + '"', 'UAudio_FFmpeg');
Close();
@@ -324,9 +324,9 @@ begin
//Log.LogStatus('AudioStreamIndex is: '+ inttostr(ffmpegStreamID), 'UAudio_FFmpeg');
- AudioStream := FormatCtx.streams[AudioStreamIndex];
- AudioStreamPos := 0;
- CodecCtx := AudioStream^.codec;
+ fAudioStream := fFormatCtx.streams[fAudioStreamIndex];
+ fAudioStreamPos := 0;
+ fCodecCtx := fAudioStream^.codec;
// TODO: should we use this or not? Should we allow 5.1 channel audio?
(*
@@ -338,21 +338,21 @@ begin
{$IFEND}
*)
- Codec := avcodec_find_decoder(CodecCtx^.codec_id);
- if (Codec = nil) then
+ fCodec := avcodec_find_decoder(fCodecCtx^.codec_id);
+ if (fCodec = nil) then
begin
Log.LogError('Unsupported codec!', 'UAudio_FFmpeg');
- CodecCtx := nil;
+ fCodecCtx := nil;
Close();
Exit;
end;
// set debug options
- CodecCtx^.debug_mv := 0;
- CodecCtx^.debug := 0;
+ fCodecCtx^.debug_mv := 0;
+ fCodecCtx^.debug := 0;
// detect bug-workarounds automatically
- CodecCtx^.workaround_bugs := FF_BUG_AUTODETECT;
+ fCodecCtx^.workaround_bugs := FF_BUG_AUTODETECT;
// error resilience strategy (careful/compliant/agressive/very_aggressive)
//CodecCtx^.error_resilience := FF_ER_CAREFUL; //FF_ER_COMPLIANT;
// allow non spec compliant speedup tricks.
@@ -362,7 +362,7 @@ begin
// fail if called concurrently by different threads.
FFmpegCore.LockAVCodec();
try
- AVResult := avcodec_open(CodecCtx, Codec);
+ AVResult := avcodec_open(fCodecCtx, fCodec);
finally
FFmpegCore.UnlockAVCodec();
end;
@@ -375,23 +375,23 @@ begin
// now initialize the audio-format
- if (not FFmpegCore.ConvertFFmpegToAudioFormat(CodecCtx^.sample_fmt, SampleFormat)) then
+ if (not FFmpegCore.ConvertFFmpegToAudioFormat(fCodecCtx^.sample_fmt, SampleFormat)) then
begin
// try standard format
SampleFormat := asfS16;
end;
- if CodecCtx^.channels > 255 then
+ if fCodecCtx^.channels > 255 then
Log.LogStatus('Error: CodecCtx^.channels > 255', 'TFFmpegDecodeStream.Open');
- FormatInfo := TAudioFormatInfo.Create(
- byte(CodecCtx^.channels),
- CodecCtx^.sample_rate,
+ fFormatInfo := TAudioFormatInfo.Create(
+ byte(fCodecCtx^.channels),
+ fCodecCtx^.sample_rate,
SampleFormat
);
- PacketQueue := TPacketQueue.Create();
+ fPacketQueue := TPacketQueue.Create();
// finally start the decode thread
- ParseThread := SDL_CreateThread(@ParseThreadMain, Self);
+ fParseThread := SDL_CreateThread(@ParseThreadMain, Self);
Result := true;
end;
@@ -403,47 +403,47 @@ begin
// wake threads waiting for packet-queue data
// Note: normally, there are no waiting threads. If there were waiting
// ones, they would block the audio-callback thread.
- if (assigned(PacketQueue)) then
- PacketQueue.Abort();
+ if (assigned(fPacketQueue)) then
+ fPacketQueue.Abort();
// send quit request (to parse-thread etc)
- SDL_mutexP(StateLock);
- QuitRequest := true;
- SDL_CondBroadcast(ParserIdleCond);
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ fQuitRequest := true;
+ SDL_CondBroadcast(fParserIdleCond);
+ SDL_mutexV(fStateLock);
// abort parse-thread
- if (ParseThread <> nil) then
+ if (fParseThread <> nil) then
begin
// and wait until it terminates
- SDL_WaitThread(ParseThread, ThreadResult);
- ParseThread := nil;
+ SDL_WaitThread(fParseThread, ThreadResult);
+ fParseThread := nil;
end;
// Close the codec
- if (CodecCtx <> nil) then
+ if (fCodecCtx <> nil) then
begin
// avcodec_close() is not thread-safe
FFmpegCore.LockAVCodec();
try
- avcodec_close(CodecCtx);
+ avcodec_close(fCodecCtx);
finally
FFmpegCore.UnlockAVCodec();
end;
- CodecCtx := nil;
+ fCodecCtx := nil;
end;
// Close the video file
- if (FormatCtx <> nil) then
+ if (fFormatCtx <> nil) then
begin
- av_close_input_file(FormatCtx);
- FormatCtx := nil;
+ av_close_input_file(fFormatCtx);
+ fFormatCtx := nil;
end;
PerformOnClose();
- FreeAndNil(PacketQueue);
- FreeAndNil(FormatInfo);
+ FreeAndNil(fPacketQueue);
+ FreeAndNil(fFormatInfo);
end;
function TFFmpegDecodeStream.GetLength(): real;
@@ -451,54 +451,54 @@ begin
// do not forget to consider the start_time value here
// there is a type size mismatch warnign because start_time and duration are cint64.
// So, in principle there could be an overflow when doing the sum.
- Result := (FormatCtx^.start_time + FormatCtx^.duration) / AV_TIME_BASE;
+ Result := (fFormatCtx^.start_time + fFormatCtx^.duration) / AV_TIME_BASE;
end;
function TFFmpegDecodeStream.GetAudioFormatInfo(): TAudioFormatInfo;
begin
- Result := FormatInfo;
+ Result := fFormatInfo;
end;
function TFFmpegDecodeStream.IsEOF(): boolean;
begin
- SDL_mutexP(StateLock);
- Result := EOFState;
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ Result := fEOFState;
+ SDL_mutexV(fStateLock);
end;
procedure TFFmpegDecodeStream.SetEOF(State: boolean);
begin
- SDL_mutexP(StateLock);
- EOFState := State;
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ fEOFState := State;
+ SDL_mutexV(fStateLock);
end;
function TFFmpegDecodeStream.IsError(): boolean;
begin
- SDL_mutexP(StateLock);
- Result := ErrorState;
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ Result := fErrorState;
+ SDL_mutexV(fStateLock);
end;
procedure TFFmpegDecodeStream.SetError(State: boolean);
begin
- SDL_mutexP(StateLock);
- ErrorState := State;
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ fErrorState := State;
+ SDL_mutexV(fStateLock);
end;
function TFFmpegDecodeStream.IsSeeking(): boolean;
begin
- SDL_mutexP(StateLock);
- Result := SeekRequest;
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ Result := fSeekRequest;
+ SDL_mutexV(fStateLock);
end;
function TFFmpegDecodeStream.IsQuit(): boolean;
begin
- SDL_mutexP(StateLock);
- Result := QuitRequest;
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ Result := fQuitRequest;
+ SDL_mutexV(fStateLock);
end;
function TFFmpegDecodeStream.GetPosition(): real;
@@ -509,11 +509,11 @@ begin
// ReadData() does not return all of the buffer retrieved by DecodeFrame().
// Determine the size of the unused part of the decode-buffer.
- BufferSizeSec := (AudioBufferSize - AudioBufferPos) /
- FormatInfo.BytesPerSec;
+ BufferSizeSec := (fAudioBufferSize - fAudioBufferPos) /
+ fFormatInfo.BytesPerSec;
// subtract the size of unused buffer-data from the audio clock.
- Result := AudioStreamPos - BufferSizeSec;
+ Result := fAudioStreamPos - BufferSizeSec;
ResumeDecoder();
end;
@@ -525,16 +525,16 @@ end;
function TFFmpegDecodeStream.GetLoop(): boolean;
begin
- SDL_mutexP(StateLock);
- Result := Loop;
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ Result := fLoop;
+ SDL_mutexV(fStateLock);
end;
procedure TFFmpegDecodeStream.SetLoop(Enabled: boolean);
begin
- SDL_mutexP(StateLock);
- Loop := Enabled;
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ fLoop := Enabled;
+ SDL_mutexV(fStateLock);
end;
@@ -544,25 +544,25 @@ end;
procedure TFFmpegDecodeStream.PauseParser();
begin
- if (SDL_ThreadID() = ParseThread.threadid) then
+ if (SDL_ThreadID() = fParseThread.threadid) then
Exit;
- SDL_mutexP(StateLock);
- Inc(ParserPauseRequestCount);
- while (ParserLocked) do
- SDL_CondWait(ParserUnlockedCond, StateLock);
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ Inc(fParserPauseRequestCount);
+ while (fParserLocked) do
+ SDL_CondWait(fParserUnlockedCond, fStateLock);
+ SDL_mutexV(fStateLock);
end;
procedure TFFmpegDecodeStream.ResumeParser();
begin
- if (SDL_ThreadID() = ParseThread.threadid) then
+ if (SDL_ThreadID() = fParseThread.threadid) then
Exit;
- SDL_mutexP(StateLock);
- Dec(ParserPauseRequestCount);
- SDL_CondSignal(ParserResumeCond);
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ Dec(fParserPauseRequestCount);
+ SDL_CondSignal(fParserResumeCond);
+ SDL_mutexV(fStateLock);
end;
procedure TFFmpegDecodeStream.SetPositionIntern(Time: real; Flush: boolean; Blocking: boolean);
@@ -575,36 +575,36 @@ begin
// - Last lock the state lock because we are manipulating some shared state-vars.
PauseParser();
PauseDecoder();
- SDL_mutexP(StateLock);
+ SDL_mutexP(fStateLock);
try
- EOFState := false;
- ErrorState := false;
+ fEOFState := false;
+ fErrorState := false;
// do not seek if we are already at the correct position.
// This is important especially for seeking to position 0 if we already are
// at the beginning. Although seeking with AVSEEK_FLAG_BACKWARD for pos 0 works,
// it is still a bit choppy (although much better than w/o AVSEEK_FLAG_BACKWARD).
- if (Time = AudioStreamPos) then
+ if (Time = fAudioStreamPos) then
Exit;
// configure seek parameters
- SeekPos := Time;
- SeekFlush := Flush;
- SeekFlags := AVSEEK_FLAG_ANY;
- SeekRequest := true;
+ fSeekPos := Time;
+ fSeekFlush := Flush;
+ fSeekFlags := AVSEEK_FLAG_ANY;
+ fSeekRequest := true;
// Note: the BACKWARD-flag seeks to the first position <= the position
// searched for. Otherwise e.g. position 0 might not be seeked correct.
// For some reason ffmpeg sometimes doesn't use position 0 but the key-frame
// following. In streams with few key-frames (like many flv-files) the next
// key-frame after 0 might be 5secs ahead.
- if (Time <= AudioStreamPos) then
- SeekFlags := SeekFlags or AVSEEK_FLAG_BACKWARD;
+ if (Time <= fAudioStreamPos) then
+ fSeekFlags := fSeekFlags or AVSEEK_FLAG_BACKWARD;
// send a reuse signal in case the parser was stopped (e.g. because of an EOF)
- SDL_CondSignal(ParserIdleCond);
+ SDL_CondSignal(fParserIdleCond);
finally
- SDL_mutexV(StateLock);
+ SDL_mutexV(fStateLock);
ResumeDecoder();
ResumeParser();
end;
@@ -612,10 +612,10 @@ begin
// in blocking mode, wait until seeking is done
if (Blocking) then
begin
- SDL_mutexP(StateLock);
- while (SeekRequest) do
- SDL_CondWait(SeekFinishedCond, StateLock);
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ while (fSeekRequest) do
+ SDL_CondWait(SeekFinishedCond, fStateLock);
+ SDL_mutexV(fStateLock);
end;
end;
@@ -635,10 +635,10 @@ begin
while (ParseLoop()) do
begin
// wait for reuse or destruction of stream
- SDL_mutexP(StateLock);
- while (not (SeekRequest or QuitRequest)) do
- SDL_CondWait(ParserIdleCond, StateLock);
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ while (not (fSeekRequest or fQuitRequest)) do
+ SDL_CondWait(fParserIdleCond, fStateLock);
+ SDL_mutexV(fStateLock);
end;
end;
@@ -669,19 +669,19 @@ var
// instead and give priority to the threads requesting the parser to pause.
procedure LockParser();
begin
- SDL_mutexP(StateLock);
- while (ParserPauseRequestCount > 0) do
- SDL_CondWait(ParserResumeCond, StateLock);
- ParserLocked := true;
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ while (fParserPauseRequestCount > 0) do
+ SDL_CondWait(fParserResumeCond, fStateLock);
+ fParserLocked := true;
+ SDL_mutexV(fStateLock);
end;
procedure UnlockParser();
begin
- SDL_mutexP(StateLock);
- ParserLocked := false;
- SDL_CondBroadcast(ParserUnlockedCond);
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ fParserLocked := false;
+ SDL_CondBroadcast(fParserUnlockedCond);
+ SDL_mutexV(fStateLock);
end;
begin
@@ -699,92 +699,92 @@ begin
end;
// handle seek-request (Note: no need to lock SeekRequest here)
- if (SeekRequest) then
+ if (fSeekRequest) then
begin
// first try: seek on the audio stream
- SeekTarget := Round(SeekPos / av_q2d(AudioStream^.time_base));
+ SeekTarget := Round(fSeekPos / av_q2d(fAudioStream^.time_base));
StartSilence := 0;
- if (SeekTarget < AudioStream^.start_time) then
- StartSilence := (AudioStream^.start_time - SeekTarget) * av_q2d(AudioStream^.time_base);
- ErrorCode := av_seek_frame(FormatCtx, AudioStreamIndex, SeekTarget, SeekFlags);
+ if (SeekTarget < fAudioStream^.start_time) then
+ StartSilence := (fAudioStream^.start_time - SeekTarget) * av_q2d(fAudioStream^.time_base);
+ ErrorCode := av_seek_frame(fFormatCtx, fAudioStreamIndex, SeekTarget, fSeekFlags);
if (ErrorCode < 0) then
begin
// second try: seek on the default stream (necessary for flv-videos and some ogg-files)
- SeekTarget := Round(SeekPos * AV_TIME_BASE);
+ SeekTarget := Round(fSeekPos * AV_TIME_BASE);
StartSilence := 0;
- if (SeekTarget < FormatCtx^.start_time) then
- StartSilence := (FormatCtx^.start_time - SeekTarget) / AV_TIME_BASE;
- ErrorCode := av_seek_frame(FormatCtx, -1, SeekTarget, SeekFlags);
+ if (SeekTarget < fFormatCtx^.start_time) then
+ StartSilence := (fFormatCtx^.start_time - SeekTarget) / AV_TIME_BASE;
+ ErrorCode := av_seek_frame(fFormatCtx, -1, SeekTarget, fSeekFlags);
end;
// pause decoder and lock state (keep the lock-order to avoid deadlocks).
// Note that the decoder does not block in the packet-queue in seeking state,
// so locking the decoder here does not cause a dead-lock.
PauseDecoder();
- SDL_mutexP(StateLock);
+ SDL_mutexP(fStateLock);
try
if (ErrorCode < 0) then
begin
// seeking failed
- ErrorState := true;
- Log.LogStatus('Seek Error in "'+FormatCtx^.filename+'"', 'UAudioDecoder_FFmpeg');
+ fErrorState := true;
+ Log.LogError('Seek Error in "'+fFormatCtx^.filename+'"', 'UAudioDecoder_FFmpeg');
end
else
begin
- if (SeekFlush) then
+ if (fSeekFlush) then
begin
// flush queue (we will send a Flush-Packet when seeking is finished)
- PacketQueue.Flush();
+ fPacketQueue.Flush();
// flush the decode buffers
- AudioBufferSize := 0;
- AudioBufferPos := 0;
- AudioPaketSize := 0;
- AudioPaketSilence := 0;
+ fAudioBufferSize := 0;
+ fAudioBufferPos := 0;
+ fAudioPaketSize := 0;
+ fAudioPaketSilence := 0;
FlushCodecBuffers();
// Set preliminary stream position. The position will be set to
// the correct value as soon as the first packet is decoded.
- AudioStreamPos := SeekPos;
+ fAudioStreamPos := fSeekPos;
end
else
begin
// request avcodec buffer flush
- PacketQueue.PutStatus(PKT_STATUS_FLAG_FLUSH, nil);
+ fPacketQueue.PutStatus(PKT_STATUS_FLAG_FLUSH, nil);
end;
// fill the gap between position 0 and start_time with silence
// but not if we are in loop mode
- if ((StartSilence > 0) and (not Loop)) then
+ if ((StartSilence > 0) and (not fLoop)) then
begin
GetMem(StartSilencePtr, SizeOf(StartSilence));
StartSilencePtr^ := StartSilence;
- PacketQueue.PutStatus(PKT_STATUS_FLAG_EMPTY, StartSilencePtr);
+ fPacketQueue.PutStatus(PKT_STATUS_FLAG_EMPTY, StartSilencePtr);
end;
end;
- SeekRequest := false;
+ fSeekRequest := false;
SDL_CondBroadcast(SeekFinishedCond);
finally
- SDL_mutexV(StateLock);
+ SDL_mutexV(fStateLock);
ResumeDecoder();
end;
end;
- if (PacketQueue.GetSize() > MAX_AUDIOQ_SIZE) then
+ if (fPacketQueue.GetSize() > MAX_AUDIOQ_SIZE) then
begin
SDL_Delay(10);
Continue;
end;
- if (av_read_frame(FormatCtx, Packet) < 0) then
+ if (av_read_frame(fFormatCtx, Packet) < 0) then
begin
// failed to read a frame, check reason
{$IF (LIBAVFORMAT_VERSION_MAJOR >= 52)}
- ByteIOCtx := FormatCtx^.pb;
+ ByteIOCtx := fFormatCtx^.pb;
{$ELSE}
- ByteIOCtx := @FormatCtx^.pb;
+ ByteIOCtx := @fFormatCtx^.pb;
{$IFEND}
// check for end-of-file (eof is not an error)
@@ -799,7 +799,7 @@ begin
else
begin
// signal end-of-file
- PacketQueue.PutStatus(PKT_STATUS_FLAG_EOF, nil);
+ fPacketQueue.PutStatus(PKT_STATUS_FLAG_EOF, nil);
Exit;
end;
end;
@@ -808,17 +808,26 @@ begin
if (url_ferror(ByteIOCtx) <> 0) then
begin
// an error occured -> abort and wait for repositioning or termination
- PacketQueue.PutStatus(PKT_STATUS_FLAG_ERROR, nil);
+ fPacketQueue.PutStatus(PKT_STATUS_FLAG_ERROR, nil);
Exit;
end;
- // no error -> wait for user input
- SDL_Delay(100);
- Continue;
+ // url_feof() does not detect an EOF for some files
+ // so we have to do it this way.
+ if ((fFormatCtx^.file_size <> 0) and
+ (ByteIOCtx^.pos >= fFormatCtx^.file_size)) then
+ begin
+ fPacketQueue.PutStatus(PKT_STATUS_FLAG_EOF, nil);
+ Exit;
+ end;
+
+ // unknown error occured, exit
+ fPacketQueue.PutStatus(PKT_STATUS_FLAG_ERROR, nil);
+ Exit;
end;
- if (Packet.stream_index = AudioStreamIndex) then
- PacketQueue.Put(@Packet)
+ if (Packet.stream_index = fAudioStreamIndex) then
+ fPacketQueue.Put(@Packet)
else
av_free_packet(@Packet);
@@ -835,28 +844,28 @@ end;
procedure TFFmpegDecodeStream.PauseDecoder();
begin
- SDL_mutexP(StateLock);
- Inc(DecoderPauseRequestCount);
- while (DecoderLocked) do
- SDL_CondWait(DecoderUnlockedCond, StateLock);
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ Inc(fDecoderPauseRequestCount);
+ while (fDecoderLocked) do
+ SDL_CondWait(fDecoderUnlockedCond, fStateLock);
+ SDL_mutexV(fStateLock);
end;
procedure TFFmpegDecodeStream.ResumeDecoder();
begin
- SDL_mutexP(StateLock);
- Dec(DecoderPauseRequestCount);
- SDL_CondSignal(DecoderResumeCond);
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ Dec(fDecoderPauseRequestCount);
+ SDL_CondSignal(fDecoderResumeCond);
+ SDL_mutexV(fStateLock);
end;
procedure TFFmpegDecodeStream.FlushCodecBuffers();
begin
// if no flush operation is specified, avcodec_flush_buffers will not do anything.
- if (@CodecCtx.codec.flush <> nil) then
+ if (@fCodecCtx.codec.flush <> nil) then
begin
// flush buffers used by avcodec_decode_audio, etc.
- avcodec_flush_buffers(CodecCtx);
+ avcodec_flush_buffers(fCodecCtx);
end
else
begin
@@ -865,8 +874,8 @@ begin
// We will just reopen the codec.
FFmpegCore.LockAVCodec();
try
- avcodec_close(CodecCtx);
- avcodec_open(CodecCtx, Codec);
+ avcodec_close(fCodecCtx);
+ avcodec_open(fCodecCtx, fCodec);
finally
FFmpegCore.UnlockAVCodec();
end;
@@ -892,27 +901,27 @@ begin
begin
// for titles with start_time > 0 we have to generate silence
// until we reach the pts of the first data packet.
- if (AudioPaketSilence > 0) then
+ if (fAudioPaketSilence > 0) then
begin
- DataSize := Min(AudioPaketSilence, BufferSize);
+ DataSize := Min(fAudioPaketSilence, BufferSize);
FillChar(Buffer[0], DataSize, 0);
- Dec(AudioPaketSilence, DataSize);
- AudioStreamPos := AudioStreamPos + DataSize / FormatInfo.BytesPerSec;
+ Dec(fAudioPaketSilence, DataSize);
+ fAudioStreamPos := fAudioStreamPos + DataSize / fFormatInfo.BytesPerSec;
Result := DataSize;
Exit;
end;
// read packet data
- while (AudioPaketSize > 0) do
+ while (fAudioPaketSize > 0) do
begin
DataSize := BufferSize;
{$IF LIBAVCODEC_VERSION >= 51030000} // 51.30.0
- PaketDecodedSize := avcodec_decode_audio2(CodecCtx, PSmallint(Buffer),
- DataSize, AudioPaketData, AudioPaketSize);
+ PaketDecodedSize := avcodec_decode_audio2(fCodecCtx, PSmallint(Buffer),
+ DataSize, fAudioPaketData, fAudioPaketSize);
{$ELSE}
- PaketDecodedSize := avcodec_decode_audio(CodecCtx, PSmallint(Buffer),
- DataSize, AudioPaketData, AudioPaketSize);
+ PaketDecodedSize := avcodec_decode_audio(fCodecCtx, PSmallint(Buffer),
+ DataSize, fAudioPaketData, fAudioPaketSize);
{$IFEND}
if(PaketDecodedSize < 0) then
@@ -921,19 +930,19 @@ begin
{$IFDEF DebugFFmpegDecode}
DebugWriteln('Skip audio frame');
{$ENDIF}
- AudioPaketSize := 0;
+ fAudioPaketSize := 0;
Break;
end;
- Inc(AudioPaketData, PaketDecodedSize);
- Dec(AudioPaketSize, PaketDecodedSize);
+ Inc(fAudioPaketData, PaketDecodedSize);
+ Dec(fAudioPaketSize, PaketDecodedSize);
// check if avcodec_decode_audio returned data, otherwise fetch more frames
if (DataSize <= 0) then
Continue;
// update stream position by the amount of fetched data
- AudioStreamPos := AudioStreamPos + DataSize / FormatInfo.BytesPerSec;
+ fAudioStreamPos := fAudioStreamPos + DataSize / fFormatInfo.BytesPerSec;
// we have data, return it and come back for more later
Result := DataSize;
@@ -941,8 +950,8 @@ begin
end;
// free old packet data
- if (AudioPaket.data <> nil) then
- av_free_packet(@AudioPaket);
+ if (fAudioPaket.data <> nil) then
+ av_free_packet(@fAudioPaket);
// do not block queue on seeking (to avoid deadlocks on the DecoderLock)
if (IsSeeking()) then
@@ -952,17 +961,17 @@ begin
// request a new packet and block if none available.
// If this fails, the queue was aborted.
- if (PacketQueue.Get(AudioPaket, BlockQueue) <= 0) then
+ if (fPacketQueue.Get(fAudioPaket, BlockQueue) <= 0) then
Exit;
// handle Status-packet
- if (PAnsiChar(AudioPaket.data) = STATUS_PACKET) then
+ if (PAnsiChar(fAudioPaket.data) = STATUS_PACKET) then
begin
- AudioPaket.data := nil;
- AudioPaketData := nil;
- AudioPaketSize := 0;
+ fAudioPaket.data := nil;
+ fAudioPaketData := nil;
+ fAudioPaketSize := 0;
- case (AudioPaket.flags) of
+ case (fAudioPaket.flags) of
PKT_STATUS_FLAG_FLUSH:
begin
// just used if SetPositionIntern was called without the flush flag.
@@ -984,9 +993,9 @@ begin
end;
PKT_STATUS_FLAG_EMPTY:
begin
- SilenceDuration := PDouble(PacketQueue.GetStatusInfo(AudioPaket))^;
- AudioPaketSilence := Round(SilenceDuration * FormatInfo.SampleRate) * FormatInfo.FrameSize;
- PacketQueue.FreeStatusInfo(AudioPaket);
+ SilenceDuration := PDouble(fPacketQueue.GetStatusInfo(fAudioPaket))^;
+ fAudioPaketSilence := Round(SilenceDuration * fFormatInfo.SampleRate) * fFormatInfo.FrameSize;
+ fPacketQueue.FreeStatusInfo(fAudioPaket);
end
else
begin
@@ -997,20 +1006,20 @@ begin
Continue;
end;
- AudioPaketData := AudioPaket.data;
- AudioPaketSize := AudioPaket.size;
+ fAudioPaketData := fAudioPaket.data;
+ fAudioPaketSize := fAudioPaket.size;
// if available, update the stream position to the presentation time of this package
- if(AudioPaket.pts <> AV_NOPTS_VALUE) then
+ if(fAudioPaket.pts <> AV_NOPTS_VALUE) then
begin
{$IFDEF DebugFFmpegDecode}
- TmpPos := AudioStreamPos;
+ TmpPos := fAudioStreamPos;
{$ENDIF}
- AudioStreamPos := av_q2d(AudioStream^.time_base) * AudioPaket.pts;
+ fAudioStreamPos := av_q2d(fAudioStream^.time_base) * fAudioPaket.pts;
{$IFDEF DebugFFmpegDecode}
- DebugWriteln('Timestamp: ' + floattostrf(AudioStreamPos, ffFixed, 15, 3) + ' ' +
+ DebugWriteln('Timestamp: ' + floattostrf(fAudioStreamPos, ffFixed, 15, 3) + ' ' +
'(Calc: ' + floattostrf(TmpPos, ffFixed, 15, 3) + '), ' +
- 'Diff: ' + floattostrf(AudioStreamPos-TmpPos, ffFixed, 15, 3));
+ 'Diff: ' + floattostrf(fAudioStreamPos-TmpPos, ffFixed, 15, 3));
{$ENDIF}
end;
end;
@@ -1025,19 +1034,19 @@ var
// prioritize pause requests
procedure LockDecoder();
begin
- SDL_mutexP(StateLock);
- while (DecoderPauseRequestCount > 0) do
- SDL_CondWait(DecoderResumeCond, StateLock);
- DecoderLocked := true;
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ while (fDecoderPauseRequestCount > 0) do
+ SDL_CondWait(fDecoderResumeCond, fStateLock);
+ fDecoderLocked := true;
+ SDL_mutexV(fStateLock);
end;
procedure UnlockDecoder();
begin
- SDL_mutexP(StateLock);
- DecoderLocked := false;
- SDL_CondBroadcast(DecoderUnlockedCond);
- SDL_mutexV(StateLock);
+ SDL_mutexP(fStateLock);
+ fDecoderLocked := false;
+ SDL_CondBroadcast(fDecoderUnlockedCond);
+ SDL_mutexV(fStateLock);
end;
begin
@@ -1056,15 +1065,15 @@ begin
while (BufferPos < BufferSize) do
begin
// check if we need more data
- if (AudioBufferPos >= AudioBufferSize) then
+ if (fAudioBufferPos >= fAudioBufferSize) then
begin
- AudioBufferPos := 0;
+ fAudioBufferPos := 0;
// we have already sent all our data; get more
- AudioBufferSize := DecodeFrame(AudioBuffer, AUDIO_BUFFER_SIZE);
+ fAudioBufferSize := DecodeFrame(fAudioBuffer, AUDIO_BUFFER_SIZE);
// check for errors or EOF
- if(AudioBufferSize < 0) then
+ if(fAudioBufferSize < 0) then
begin
Result := BufferPos;
Exit;
@@ -1072,16 +1081,16 @@ begin
end;
// calc number of new bytes in the decode-buffer
- CopyByteCount := AudioBufferSize - AudioBufferPos;
+ CopyByteCount := fAudioBufferSize - fAudioBufferPos;
// resize copy-count if more bytes available than needed (remaining bytes are used the next time)
RemainByteCount := BufferSize - BufferPos;
if (CopyByteCount > RemainByteCount) then
CopyByteCount := RemainByteCount;
- Move(AudioBuffer[AudioBufferPos], Buffer[BufferPos], CopyByteCount);
+ Move(fAudioBuffer[fAudioBufferPos], Buffer[BufferPos], CopyByteCount);
Inc(BufferPos, CopyByteCount);
- Inc(AudioBufferPos, CopyByteCount);
+ Inc(fAudioBufferPos, CopyByteCount);
end;
finally
UnlockDecoder();
diff --git a/us_maker_edition/src/media/UAudioInput_Bass.pas b/us_maker_edition/src/media/UAudioInput_Bass.pas
index b8f914c5..0e79b343 100644
--- a/us_maker_edition/src/media/UAudioInput_Bass.pas
+++ b/us_maker_edition/src/media/UAudioInput_Bass.pas
@@ -471,9 +471,12 @@ begin
Flags := BASS_RecordGetInput(SourceIndex, PSingle(nil)^);
if (Flags <> -1) then
begin
- // is the current source a mic-source?
- if ((Flags and BASS_INPUT_TYPE_MIC) <> 0) then
+ // chech if current source is a mic (and none was set before)
+ if ((Flags and BASS_INPUT_TYPE_MIC) <> 0) and
+ (BassDevice.MicSource = -1) then
+ begin
BassDevice.MicSource := SourceIndex;
+ end;
end;
Inc(SourceIndex);
diff --git a/us_maker_edition/src/media/UMediaCore_FFmpeg.pas b/us_maker_edition/src/media/UMediaCore_FFmpeg.pas
index 2d572ff2..eb136995 100644
--- a/us_maker_edition/src/media/UMediaCore_FFmpeg.pas
+++ b/us_maker_edition/src/media/UMediaCore_FFmpeg.pas
@@ -41,6 +41,7 @@ uses
avformat,
avutil,
avio,
+ swscale,
UMusic,
ULog,
UPath;
@@ -121,9 +122,83 @@ const
var
Instance: TMediaCore_FFmpeg;
+function AV_VERSION_INT(a, b, c: cardinal): cuint;
+begin
+ Result := (a shl 16) or (b shl 8) or c;
+end;
+
+procedure CheckVersions();
+var
+ libVersion: cuint;
+ headerVersion: cuint;
+
+ function hexVerToStr(Version: cuint): string;
+ var
+ Major, Minor, Release: cardinal;
+ begin
+ Major := (Version shr 16) and $FF;;
+ Minor := (Version shr 8) and $FF;
+ Release := Version and $FF;
+ Result := Format('%d.%d.%d', [Major, Minor, Release]);
+ end;
+
+begin
+ libVersion := avcodec_version();
+ headerVersion := AV_VERSION_INT(
+ LIBAVCODEC_VERSION_MAJOR,
+ LIBAVCODEC_VERSION_MINOR,
+ LIBAVCODEC_VERSION_RELEASE);
+ if (libVersion <> headerVersion) then
+ begin
+ Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.',
+ ['libavcodec', hexVerToStr(headerVersion), hexVerToStr(libVersion)]));
+ end;
+
+ {$IF LIBAVFORMAT_VERSION >= 52020000} // 52.20.0
+ libVersion := avformat_version();
+ headerVersion := AV_VERSION_INT(
+ LIBAVFORMAT_VERSION_MAJOR,
+ LIBAVFORMAT_VERSION_MINOR,
+ LIBAVFORMAT_VERSION_RELEASE);
+ if (libVersion <> headerVersion) then
+ begin
+ Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.',
+ ['libavformat', hexVerToStr(headerVersion), hexVerToStr(libVersion)]));
+ end;
+ {$IFEND}
+
+ {$IF LIBAVUTIL_VERSION >= 49008000} // 49.8.0
+ libVersion := avutil_version();
+ headerVersion := AV_VERSION_INT(
+ LIBAVUTIL_VERSION_MAJOR,
+ LIBAVUTIL_VERSION_MINOR,
+ LIBAVUTIL_VERSION_RELEASE);
+ if (libVersion <> headerVersion) then
+ begin
+ Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.',
+ ['libavutil', hexVerToStr(headerVersion), hexVerToStr(libVersion)]));
+ end;
+ {$IFEND}
+
+ {$IF LIBSWSCALE_VERSION >= 000006001} // 0.6.1
+ libVersion := swscale_version();
+ headerVersion := AV_VERSION_INT(
+ LIBSWSCALE_VERSION_MAJOR,
+ LIBSWSCALE_VERSION_MINOR,
+ LIBSWSCALE_VERSION_RELEASE);
+ if (libVersion <> headerVersion) then
+ begin
+ Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.',
+ ['libswscale', hexVerToStr(headerVersion), hexVerToStr(libVersion)]));
+ end;
+ {$IFEND}
+end;
+
constructor TMediaCore_FFmpeg.Create();
begin
inherited;
+
+ CheckVersions();
av_register_protocol(@UTF8FileProtocol);
AVCodecLock := SDL_CreateMutex();
end;
diff --git a/us_maker_edition/src/media/UMedia_dummy.pas b/us_maker_edition/src/media/UMedia_dummy.pas
index 8ebfd3a9..46cbe6b8 100644
--- a/us_maker_edition/src/media/UMedia_dummy.pas
+++ b/us_maker_edition/src/media/UMedia_dummy.pas
@@ -112,9 +112,43 @@ type
procedure SetPosition(Time: real);
function GetPosition: real;
- procedure GetFrame(Time: Extended);
- procedure DrawGL(Screen: integer);
+ procedure SetScreen(Screen: integer);
+ function GetScreen(): integer;
+
+ procedure SetScreenPosition(X, Y, Z: double);
+ procedure GetScreenPosition(var X, Y, Z: double);
+
+ procedure SetWidth(Width: double);
+ function GetWidth(): double;
+
+ procedure SetHeight(Height: double);
+ function GetHeight(): double;
+
+ procedure SetFrameRange(Range: TRectCoords);
+ function GetFrameRange(): TRectCoords;
+
+ function GetFrameAspect(): real;
+
+ procedure SetAspectCorrection(AspectCorrection: TAspectCorrection);
+ function GetAspectCorrection(): TAspectCorrection;
+
+ procedure SetAlpha(Alpha: double);
+ function GetAlpha(): double;
+
+ procedure SetReflectionSpacing(Spacing: double);
+ function GetReflectionSpacing(): double;
+ procedure GetFrame(Time: Extended);
+ procedure Draw();
+ procedure DrawReflection();
+
+ property Screen: integer read GetScreen;
+ property Width: double read GetWidth write SetWidth;
+ property Height: double read GetHeight write SetWidth;
+ property Alpha: double read GetAlpha write SetAlpha;
+ property ReflectionSpacing: double read GetReflectionSpacing write SetReflectionSpacing;
+ property FrameAspect: real read GetFrameAspect;
+ property AspectCorrection: TAspectCorrection read GetAspectCorrection;
property Loop: boolean read GetLoop write SetLoop;
property Position: real read GetPosition write SetPosition;
end;
@@ -329,11 +363,97 @@ begin
Result := 0;
end;
+procedure TVideo_Dummy.SetScreen(Screen: integer);
+begin
+end;
+
+function TVideo_Dummy.GetScreen(): integer;
+begin
+ Result := 0;
+end;
+
+procedure TVideo_Dummy.SetScreenPosition(X, Y, Z: double);
+begin
+end;
+
+procedure TVideo_Dummy.GetScreenPosition(var X, Y, Z: double);
+begin
+ X := 0;
+ Y := 0;
+ Z := 0;
+end;
+
+procedure TVideo_Dummy.SetWidth(Width: double);
+begin
+end;
+
+function TVideo_Dummy.GetWidth(): double;
+begin
+ Result := 0;
+end;
+
+procedure TVideo_Dummy.SetHeight(Height: double);
+begin
+end;
+
+function TVideo_Dummy.GetHeight(): double;
+begin
+ Result := 0;
+end;
+
+procedure TVideo_Dummy.SetFrameRange(Range: TRectCoords);
+begin
+end;
+
+function TVideo_Dummy.GetFrameRange(): TRectCoords;
+begin
+ Result.Left := 0;
+ Result.Right := 0;
+ Result.Upper := 0;
+ Result.Lower := 0;
+end;
+
+function TVideo_Dummy.GetFrameAspect(): real;
+begin
+ Result := 0;
+end;
+
+procedure TVideo_Dummy.SetAspectCorrection(AspectCorrection: TAspectCorrection);
+begin
+end;
+
+function TVideo_Dummy.GetAspectCorrection(): TAspectCorrection;
+begin
+ Result := acoStretch;
+end;
+
+procedure TVideo_Dummy.SetAlpha(Alpha: double);
+begin
+end;
+
+function TVideo_Dummy.GetAlpha(): double;
+begin
+ Result := 0;
+end;
+
+procedure TVideo_Dummy.SetReflectionSpacing(Spacing: double);
+begin
+end;
+
+function TVideo_Dummy.GetReflectionSpacing(): double;
+begin
+ Result := 0;
+end;
+
procedure TVideo_Dummy.GetFrame(Time: Extended);
begin
end;
-procedure TVideo_Dummy.DrawGL(Screen: integer);
+procedure TVideo_Dummy.Draw();
+begin
+end;
+
+procedure TVideo_Dummy.DrawReflection();
begin
end;
diff --git a/us_maker_edition/src/media/UVideo.pas b/us_maker_edition/src/media/UVideo.pas
index c7d59fc8..add7bdc8 100644
--- a/us_maker_edition/src/media/UVideo.pas
+++ b/us_maker_edition/src/media/UVideo.pas
@@ -48,24 +48,6 @@ interface
{$DEFINE PIXEL_FMT_BGR}
{$ENDIF}
-type
- {**
- * vacStretch: Stretch to screen width and height
- * - ignores aspect
- * + no borders
- * + no image data loss
- * vacCrop: Stretch to screen width or height, crop the other dimension
- * + keeps aspect
- * + no borders
- * - frame borders are cropped (image data loss)
- * vacLetterBox: Stretch to screen width, add bars at or crop top and bottom
- * + keeps aspect
- * - borders at top and bottom
- * o top/bottom is cropped if width < height (unusual)
- *}
- TAspectCorrection = (acoStretch, acoCrop, acoLetterBox);
-
-
implementation
uses
@@ -112,12 +94,9 @@ const
PIXEL_FMT_SIZE = 3;
{$ENDIF}
-type
- TRectCoords = record
- Left, Right: double;
- Upper, Lower: double;
- end;
+ ReflectionH = 0.5; //reflection height (50%)
+type
IVideo_FFmpeg = interface (IVideo)
['{E640E130-C8C0-4399-AF02-67A3569313AB}']
function Open(const FileName: IPath): boolean;
@@ -149,11 +128,25 @@ type
fSwScaleContext: PSwsContext;
{$ENDIF}
+ fScreen: integer; //actual screen to draw on
+
+ fPosX: double;
+ fPosY: double;
+ fPosZ: double;
+ fWidth: double;
+ fHeight: double;
+
+ fFrameRange: TRectCoords;
+
+ fAlpha: double;
+ fReflectionSpacing: double;
+
+
fAspect: real; //**< width/height ratio
fAspectCorrection: TAspectCorrection;
- fTimeBase: extended; //**< FFmpeg time base per time unit
- fFrameTime: extended; //**< video time position (absolute)
+ fFrameDuration: extended; //**< duration of a video frame in seconds (= 1/fps)
+ fFrameTime: extended; //**< video time position (absolute)
fLoopTime: extended; //**< start time of the current loop
fPboEnabled: boolean;
@@ -163,6 +156,8 @@ type
procedure SynchronizeTime(Frame: PAVFrame; var pts: double);
procedure GetVideoRect(var ScreenRect, TexRect: TRectCoords);
+ procedure DrawBorders(ScreenRect: TRectCoords);
+ procedure DrawBordersReflected(ScreenRect: TRectCoords; AlphaUpper, AlphaLower: double);
procedure ShowDebugInfo();
@@ -183,8 +178,39 @@ type
procedure SetPosition(Time: real);
function GetPosition: real;
- procedure GetFrame(Time: Extended);
- procedure DrawGL(Screen: integer);
+ procedure SetScreen(Screen: integer);
+ function GetScreen(): integer;
+
+ procedure SetScreenPosition(X, Y, Z: double);
+ procedure GetScreenPosition(var X, Y, Z: double);
+
+ procedure SetWidth(Width: double);
+ function GetWidth(): double;
+
+ procedure SetHeight(Height: double);
+ function GetHeight(): double;
+
+ {**
+ * Sub-image of the video frame to draw.
+ * This can be used for zooming or similar purposes.
+ *}
+ procedure SetFrameRange(Range: TRectCoords);
+ function GetFrameRange(): TRectCoords;
+
+ function GetFrameAspect(): real;
+
+ procedure SetAspectCorrection(AspectCorrection: TAspectCorrection);
+ function GetAspectCorrection(): TAspectCorrection;
+
+ procedure SetAlpha(Alpha: double);
+ function GetAlpha(): double;
+
+ procedure SetReflectionSpacing(Spacing: double);
+ function GetReflectionSpacing(): double;
+
+ procedure GetFrame(Time: Extended);
+ procedure Draw();
+ procedure DrawReflection();
end;
TVideoPlayback_FFmpeg = class( TInterfacedObject, IVideoPlayback )
@@ -406,19 +432,18 @@ begin
fAspect := fAspect * fCodecContext^.width /
fCodecContext^.height;
- fTimeBase := 1/av_q2d(fStream^.r_frame_rate);
+ fFrameDuration := 1/av_q2d(fStream^.r_frame_rate);
- // hack to get reasonable timebase (for divx and others)
- if (fTimeBase < 0.02) then // 0.02 <-> 50 fps
+ // hack to get reasonable framerate (for divx and others)
+ if (fFrameDuration < 0.02) then // 0.02 <-> 50 fps
begin
- fTimeBase := av_q2d(fStream^.r_frame_rate);
- while (fTimeBase > 50) do
- fTimeBase := fTimeBase/10;
- fTimeBase := 1/fTimeBase;
+ fFrameDuration := av_q2d(fStream^.r_frame_rate);
+ while (fFrameDuration > 50) do
+ fFrameDuration := fFrameDuration/10;
+ fFrameDuration := 1/fFrameDuration;
end;
- Log.LogInfo('VideoTimeBase: ' + floattostr(fTimeBase), 'TVideoPlayback_ffmpeg.Open');
- Log.LogInfo('Framerate: '+inttostr(floor(1/fTimeBase))+'fps', 'TVideoPlayback_ffmpeg.Open');
+ Log.LogInfo('Framerate: '+inttostr(floor(1/fFrameDuration))+'fps', 'TVideoPlayback_ffmpeg.Open');
{$IFDEF UseSWScale}
// if available get a SWScale-context -> faster than the deprecated img_convert().
@@ -484,7 +509,7 @@ begin
fOpened := False;
fPaused := False;
- fTimeBase := 0;
+ fFrameDuration := 0;
fFrameTime := 0;
fStream := nil;
fStreamIndex := -1;
@@ -498,6 +523,22 @@ begin
fPboId := 0;
fAspectCorrection := acoCrop;
+
+ fScreen := 1;
+
+ fPosX := 0;
+ fPosY := 0;
+ fPosZ := 0;
+ fWidth := RenderW;
+ fHeight := RenderH;
+
+ fFrameRange.Left := 0;
+ fFrameRange.Right := 1;
+ fFrameRange.Upper := 0;
+ fFrameRange.Lower := 1;
+
+ fAlpha := 1;
+ fReflectionSpacing := 0;
end;
procedure TVideo_FFmpeg.Close;
@@ -686,12 +727,6 @@ begin
Exit;
{*
- * TODO:
- * Check if it is correct to assume that fTimeBase is the time of one frame?
- * The tutorial and FFPlay do not make this assumption.
- *}
-
- {*
* Synchronization - begin
*}
@@ -710,12 +745,12 @@ begin
{$IFDEF DebugDisplay}
DebugWriteln('Time: '+inttostr(floor(Time*1000)) + sLineBreak +
'VideoTime: '+inttostr(floor(fFrameTime*1000)) + sLineBreak +
- 'TimeBase: '+inttostr(floor(fTimeBase*1000)) + sLineBreak +
+ 'TimeBase: '+inttostr(floor(fFrameDuration*1000)) + sLineBreak +
'TimeDiff: '+inttostr(floor(TimeDifference*1000)));
{$endif}
// check if time has reached the next frame
- if (TimeDiff < fTimeBase) then
+ if (TimeDiff < fFrameDuration) then
begin
{$ifdef DebugFrames}
// frame delay debug display
@@ -726,7 +761,7 @@ begin
DebugWriteln('not getting new frame' + sLineBreak +
'Time: '+inttostr(floor(Time*1000)) + sLineBreak +
'VideoTime: '+inttostr(floor(fFrameTime*1000)) + sLineBreak +
- 'TimeBase: '+inttostr(floor(fTimeBase*1000)) + sLineBreak +
+ 'TimeBase: '+inttostr(floor(fFrameDuration*1000)) + sLineBreak +
'TimeDiff: '+inttostr(floor(TimeDifference*1000)));
{$endif}
@@ -745,9 +780,9 @@ begin
// check if we have to skip frames
// Either if we are one frame behind or if the skip threshold has been reached.
- // Do not skip if the difference is less than fTimeBase as there is no next frame.
- // Note: We assume that fTimeBase is the length of one frame.
- if (TimeDiff >= Max(fTimeBase, SKIP_FRAME_DIFF)) then
+ // Do not skip if the difference is less than fFrameDuration as there is no next frame.
+ // Note: We assume that fFrameDuration is the length of one frame.
+ if (TimeDiff >= Max(fFrameDuration, SKIP_FRAME_DIFF)) then
begin
{$IFDEF DebugFrames}
//frame drop debug display
@@ -755,13 +790,13 @@ begin
{$ENDIF}
{$IFDEF DebugDisplay}
DebugWriteln('skipping frames' + sLineBreak +
- 'TimeBase: '+inttostr(floor(fTimeBase*1000)) + sLineBreak +
+ 'TimeBase: '+inttostr(floor(fFrameDuration*1000)) + sLineBreak +
'TimeDiff: '+inttostr(floor(TimeDifference*1000)));
{$endif}
// update video-time
- DropFrameCount := Trunc(TimeDiff / fTimeBase);
- fFrameTime := fFrameTime + DropFrameCount*fTimeBase;
+ DropFrameCount := Trunc(TimeDiff / fFrameDuration);
+ fFrameTime := fFrameTime + DropFrameCount*fFrameDuration;
// skip frames
for i := 1 to DropFrameCount do
@@ -889,72 +924,142 @@ procedure TVideo_FFmpeg.GetVideoRect(var ScreenRect, TexRect: TRectCoords);
var
ScreenAspect: double; // aspect of screen resolution
ScaledVideoWidth, ScaledVideoHeight: double;
+
begin
// Three aspects to take into account:
// 1. Screen/display resolution (e.g. 1920x1080 -> 16:9)
- // 2. Render aspect (fixed to 800x600 -> 4:3)
+ // 2. Render aspect (fWidth x fHeight -> variable)
// 3. Movie aspect (video frame aspect stored in fAspect)
- ScreenAspect := ScreenW / ScreenH;
+ ScreenAspect := fWidth*((ScreenW/Screens)/RenderW)/(fHeight*(ScreenH/RenderH));
case fAspectCorrection of
acoStretch: begin
- ScaledVideoWidth := RenderW;
- ScaledVideoHeight := RenderH;
+ ScaledVideoWidth := fWidth;
+ ScaledVideoHeight := fHeight;
end;
+
acoCrop: begin
if (ScreenAspect >= fAspect) then
begin
- ScaledVideoWidth := RenderW;
- ScaledVideoHeight := RenderH * ScreenAspect/fAspect;
- end
- else
+ ScaledVideoWidth := fWidth;
+ ScaledVideoHeight := fHeight * ScreenAspect/fAspect;
+ end else
begin
- ScaledVideoHeight := RenderH;
- ScaledVideoWidth := RenderW * fAspect/ScreenAspect;
+ ScaledVideoHeight := fHeight;
+ ScaledVideoWidth := fWidth * fAspect/ScreenAspect;
end;
end;
+
acoLetterBox: begin
- ScaledVideoWidth := RenderW;
- ScaledVideoHeight := RenderH * ScreenAspect/fAspect;
- end
- else
+ if (ScreenAspect <= fAspect) then
+ begin
+ ScaledVideoWidth := fWidth;
+ ScaledVideoHeight := fHeight * ScreenAspect/fAspect;
+ end else
+ begin
+ ScaledVideoHeight := fHeight;
+ ScaledVideoWidth := fWidth * fAspect/ScreenAspect;
+ end;
+ end else
raise Exception.Create('Unhandled aspect correction!');
end;
- // center video
- ScreenRect.Left := (RenderW - ScaledVideoWidth) / 2;
+ //center video
+ ScreenRect.Left := (fWidth - ScaledVideoWidth) / 2 + fPosX;
ScreenRect.Right := ScreenRect.Left + ScaledVideoWidth;
- ScreenRect.Upper := (RenderH - ScaledVideoHeight) / 2;
+ ScreenRect.Upper := (fHeight - ScaledVideoHeight) / 2 + fPosY;
ScreenRect.Lower := ScreenRect.Upper + ScaledVideoHeight;
// texture contains right/lower (power-of-2) padding.
// Determine the texture coords of the video frame.
- TexRect.Left := 0;
- TexRect.Right := fCodecContext^.width / fTexWidth;
- TexRect.Upper := 0;
- TexRect.Lower := fCodecContext^.height / fTexHeight;
+ TexRect.Left := (fCodecContext^.width / fTexWidth) * fFrameRange.Left;
+ TexRect.Right := (fCodecContext^.width / fTexWidth) * fFrameRange.Right;
+ TexRect.Upper := (fCodecContext^.height / fTexHeight) * fFrameRange.Upper;
+ TexRect.Lower := (fCodecContext^.height / fTexHeight) * fFrameRange.Lower;
end;
-procedure TVideo_FFmpeg.DrawGL(Screen: integer);
-var
- ScreenRect: TRectCoords;
- TexRect: TRectCoords;
+procedure TVideo_FFmpeg.DrawBorders(ScreenRect: TRectCoords);
+ procedure DrawRect(left, right, upper, lower: double);
+ begin
+ glColor4f(0, 0, 0, fAlpha);
+ glBegin(GL_QUADS);
+ glVertex3f(left, upper, fPosZ);
+ glVertex3f(right, upper, fPosZ);
+ glVertex3f(right, lower, fPosZ);
+ glVertex3f(left, lower, fPosZ);
+ glEnd;
+ end;
begin
- // have a nice black background to draw on
- // (even if there were errors opening the vid)
- // 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
+ //upper border
+ if(ScreenRect.Upper > fPosY) then
+ DrawRect(fPosX, fPosX+fWidth, fPosY, ScreenRect.Upper);
+
+ //lower border
+ if(ScreenRect.Lower < fPosY+fHeight) then
+ DrawRect(fPosX, fPosX+fWidth, ScreenRect.Lower, fPosY+fHeight);
+
+ //left border
+ if(ScreenRect.Left > fPosX) then
+ DrawRect(fPosX, ScreenRect.Left, fPosY, fPosY+fHeight);
+
+ //right border
+ if(ScreenRect.Right < fPosX+fWidth) then
+ DrawRect(ScreenRect.Right, fPosX+fWidth, fPosY, fPosY+fHeight);
+end;
+
+procedure TVideo_FFmpeg.DrawBordersReflected(ScreenRect: TRectCoords; AlphaUpper, AlphaLower: double);
+var
+ rPosUpper, rPosLower: double;
+
+ procedure DrawRect(left, right, upper, lower: double);
+ var
+ AlphaTop: double;
+ AlphaBottom: double;
+
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);
+ AlphaTop := AlphaUpper+(AlphaLower-AlphaUpper)*(upper-rPosUpper)/(fHeight*ReflectionH);
+ AlphaBottom := AlphaLower+(AlphaUpper-AlphaLower)*(rPosLower-lower)/(fHeight*ReflectionH);
+
+ glBegin(GL_QUADS);
+ glColor4f(0, 0, 0, AlphaTop);
+ glVertex3f(left, upper, fPosZ);
+ glVertex3f(right, upper, fPosZ);
+
+ glColor4f(0, 0, 0, AlphaBottom);
+ glVertex3f(right, lower, fPosZ);
+ glVertex3f(left, lower, fPosZ);
+ glEnd;
end;
+begin
+ rPosUpper := fPosY+fHeight+fReflectionSpacing;
+ rPosLower := rPosUpper+fHeight*ReflectionH;
+
+ //upper border
+ if(ScreenRect.Upper > rPosUpper) then
+ DrawRect(fPosX, fPosX+fWidth, rPosUpper, ScreenRect.Upper);
+ //lower border
+ if(ScreenRect.Lower < rPosLower) then
+ DrawRect(fPosX, fPosX+fWidth, ScreenRect.Lower, rPosLower);
+
+ //left border
+ if(ScreenRect.Left > fPosX) then
+ DrawRect(fPosX, ScreenRect.Left, rPosUpper, rPosLower);
+
+ //right border
+ if(ScreenRect.Right < fPosX+fWidth) then
+ DrawRect(ScreenRect.Right, fPosX+fWidth, rPosUpper, rPosLower);
+end;
+
+
+procedure TVideo_FFmpeg.Draw();
+var
+ ScreenRect: TRectCoords;
+ TexRect: TRectCoords;
+ HeightFactor: double;
+ WidthFactor: double;
+
+begin
// exit if there's nothing to draw
if (not fOpened) then
Exit;
@@ -966,31 +1071,53 @@ begin
// get texture and screen positions
GetVideoRect(ScreenRect, TexRect);
- // we could use blending for brightness control, but do we need this?
- glDisable(GL_BLEND);
+ WidthFactor := (ScreenW/Screens) / RenderW;
+ HeightFactor := ScreenH / RenderH;
+
+ glScissor(
+ round(fPosX*WidthFactor + (ScreenW/Screens)*(fScreen-1)),
+ round((RenderH-fPosY-fHeight)*HeightFactor),
+ round(fWidth*WidthFactor),
+ round(fHeight*HeightFactor)
+ );
+
+ glEnable(GL_SCISSOR_TEST);
+ glEnable(GL_BLEND);
+ glDepthRange(0, 10);
+ glDepthFunc(GL_LEQUAL);
+ glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fFrameTex);
- glColor3f(1, 1, 1);
+ glColor4f(1, 1, 1, fAlpha);
glBegin(GL_QUADS);
// upper-left coord
glTexCoord2f(TexRect.Left, TexRect.Upper);
- glVertex2f(ScreenRect.Left, ScreenRect.Upper);
+ glVertex3f(ScreenRect.Left, ScreenRect.Upper, fPosZ);
// lower-left coord
glTexCoord2f(TexRect.Left, TexRect.Lower);
- glVertex2f(ScreenRect.Left, ScreenRect.Lower);
+ glVertex3f(ScreenRect.Left, ScreenRect.Lower, fPosZ);
// lower-right coord
glTexCoord2f(TexRect.Right, TexRect.Lower);
- glVertex2f(ScreenRect.Right, ScreenRect.Lower);
+ glVertex3f(ScreenRect.Right, ScreenRect.Lower, fPosZ);
// upper-right coord
glTexCoord2f(TexRect.Right, TexRect.Upper);
- glVertex2f(ScreenRect.Right, ScreenRect.Upper);
+ glVertex3f(ScreenRect.Right, ScreenRect.Upper, fPosZ);
glEnd;
+
glDisable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ //draw black borders
+ DrawBorders(ScreenRect);
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+ glDisable(GL_SCISSOR_TEST);
{$IFDEF VideoBenchmark}
Log.BenchmarkEnd(15);
- Log.LogBenchmark('DrawGL', 15);
+ Log.LogBenchmark('Draw', 15);
{$ENDIF}
{$IF Defined(Info) or Defined(DebugFrames)}
@@ -998,10 +1125,98 @@ begin
{$IFEND}
end;
+procedure TVideo_FFmpeg.DrawReflection();
+var
+ ScreenRect: TRectCoords;
+ TexRect: TRectCoords;
+ HeightFactor: double;
+ WidthFactor: double;
+
+ AlphaTop: double;
+ AlphaBottom: double;
+
+ AlphaUpper: double;
+ AlphaLower: double;
+
+begin
+ // exit if there's nothing to draw
+ if (not fOpened) then
+ Exit;
+
+ // get texture and screen positions
+ GetVideoRect(ScreenRect, TexRect);
+
+ WidthFactor := (ScreenW/Screens) / RenderW;
+ HeightFactor := ScreenH / RenderH;
+
+ glScissor(
+ round(fPosX*WidthFactor + (ScreenW/Screens)*(fScreen-1)),
+ round((RenderH-fPosY-fHeight-fReflectionSpacing-fHeight*ReflectionH)*HeightFactor),
+ round(fWidth*WidthFactor),
+ round(fHeight*HeightFactor*ReflectionH)
+ );
+
+ glEnable(GL_SCISSOR_TEST);
+ glEnable(GL_BLEND);
+ glDepthRange(0, 10);
+ glDepthFunc(GL_LEQUAL);
+ glEnable(GL_DEPTH_TEST);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, fFrameTex);
+
+ //calculate new ScreenRect coordinates for Reflection
+ ScreenRect.Lower := fPosY + fHeight + fReflectionSpacing
+ + (ScreenRect.Upper-fPosY) + (ScreenRect.Lower-ScreenRect.Upper)*ReflectionH;
+ ScreenRect.Upper := fPosY + fHeight + fReflectionSpacing
+ + (ScreenRect.Upper-fPosY);
+
+ AlphaUpper := fAlpha-0.3;
+ AlphaLower := 0;
+
+ AlphaTop := AlphaUpper-(AlphaLower-AlphaUpper)*
+ (ScreenRect.Upper-fPosY-fHeight-fReflectionSpacing)/fHeight;
+ AlphaBottom := AlphaLower+(AlphaUpper-AlphaLower)*
+ (fPosY+fHeight+fReflectionSpacing+fHeight*ReflectionH-ScreenRect.Lower)/fHeight;
+
+ glBegin(GL_QUADS);
+ //Top Left
+ glColor4f(1, 1, 1, AlphaTop);
+ glTexCoord2f(TexRect.Left, TexRect.Lower);
+ glVertex3f(ScreenRect.Left, ScreenRect.Upper, fPosZ);
+
+ //Bottom Left
+ glColor4f(1, 1, 1, AlphaBottom);
+ glTexCoord2f(TexRect.Left, (TexRect.Lower-TexRect.Upper)*(1-ReflectionH));
+ glVertex3f(ScreenRect.Left, ScreenRect.Lower, fPosZ);
+
+ //Bottom Right
+ glColor4f(1, 1, 1, AlphaBottom);
+ glTexCoord2f(TexRect.Right, (TexRect.Lower-TexRect.Upper)*(1-ReflectionH));
+ glVertex3f(ScreenRect.Right, ScreenRect.Lower, fPosZ);
+
+ //Top Right
+ glColor4f(1, 1, 1, AlphaTop);
+ glTexCoord2f(TexRect.Right, TexRect.Lower);
+ glVertex3f(ScreenRect.Right, ScreenRect.Upper, fPosZ);
+ glEnd;
+
+ glDisable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ //draw black borders
+ DrawBordersReflected(ScreenRect, AlphaUpper, AlphaLower);
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+ glDisable(GL_SCISSOR_TEST);
+end;
+
procedure TVideo_FFmpeg.ShowDebugInfo();
begin
{$IFDEF Info}
- if (fFrameTime+fTimeBase < 0) then
+ if (fFrameTime+fFrameDuration < 0) then
begin
glColor4f(0.7, 1, 0.3, 1);
SetFontStyle (1);
@@ -1093,7 +1308,10 @@ begin
fEOF := false;
fFrameTexValid := false;
- if (av_seek_frame(fFormatContext, fStreamIndex, Floor(Time/fTimeBase), SeekFlags) < 0) then
+ if (av_seek_frame(fFormatContext,
+ fStreamIndex,
+ Round(Time / av_q2d(fStream^.time_base)),
+ SeekFlags) < 0) then
begin
Log.LogError('av_seek_frame() failed', 'TVideoPlayback_ffmpeg.SetPosition');
Exit;
@@ -1107,6 +1325,111 @@ begin
Result := fFrameTime;
end;
+procedure TVideo_FFmpeg.SetScreen(Screen: integer);
+begin
+ fScreen := Screen;
+end;
+
+function TVideo_FFmpeg.GetScreen(): integer;
+begin
+ Result := fScreen;
+end;
+
+
+procedure TVideo_FFmpeg.SetScreenPosition(X, Y, Z: double);
+begin
+ fPosX := X;
+ fPosY := Y;
+ fPosZ := Z;
+end;
+
+procedure TVideo_FFmpeg.GetScreenPosition(var X, Y, Z: double);
+begin
+ X := fPosX;
+ Y := fPosY;
+ Z := fPosZ;
+end;
+
+
+procedure TVideo_FFmpeg.SetWidth(Width: double);
+begin
+ fWidth := Width;
+end;
+
+function TVideo_FFmpeg.GetWidth(): double;
+begin
+ Result := fWidth;
+end;
+
+
+procedure TVideo_FFmpeg.SetHeight(Height: double);
+begin
+ fHeight := Height;
+end;
+
+function TVideo_FFmpeg.GetHeight(): double;
+begin
+ Result := fHeight;
+end;
+
+
+procedure TVideo_FFmpeg.SetFrameRange(Range: TRectCoords);
+begin
+ fFrameRange := Range;
+end;
+
+function TVideo_FFmpeg.GetFrameRange(): TRectCoords;
+begin
+ Result := fFrameRange;
+end;
+
+
+function TVideo_FFmpeg.GetFrameAspect(): real;
+begin
+ Result := fAspect;
+end;
+
+
+procedure TVideo_FFmpeg.SetAspectCorrection(AspectCorrection: TAspectCorrection);
+begin
+ fAspectCorrection := AspectCorrection;
+end;
+
+function TVideo_FFmpeg.GetAspectCorrection(): TAspectCorrection;
+begin
+ Result := fAspectCorrection;
+end;
+
+
+
+procedure TVideo_FFmpeg.SetAlpha(Alpha: double);
+begin
+ fAlpha := Alpha;
+
+ if (fAlpha>1) then
+ fAlpha := 1;
+
+ if (fAlpha<0) then
+ fAlpha := 0;
+end;
+
+function TVideo_FFmpeg.GetAlpha(): double;
+begin
+ Result := fAlpha;
+end;
+
+
+procedure TVideo_FFmpeg.SetReflectionSpacing(Spacing: double);
+begin
+ fReflectionSpacing := Spacing;
+end;
+
+function TVideo_FFmpeg.GetReflectionSpacing(): double;
+begin
+ Result := fReflectionSpacing;
+end;
+
+
initialization
MediaManager.Add(TVideoPlayback_FFmpeg.Create);
diff --git a/us_maker_edition/src/media/UVisualizer.pas b/us_maker_edition/src/media/UVisualizer.pas
index 4f553521..1cdc3500 100644
--- a/us_maker_edition/src/media/UVisualizer.pas
+++ b/us_maker_edition/src/media/UVisualizer.pas
@@ -110,6 +110,8 @@ type
fState: TProjectMState;
+ fScreen: integer;
+
fVisualTex: GLuint;
fPCMData: TPCMData;
fRndPCMcount: integer;
@@ -144,8 +146,35 @@ type
procedure SetLoop(Enable: boolean);
function GetLoop(): boolean;
+ procedure SetScreen(Screen: integer);
+ function GetScreen(): integer;
+
+ procedure SetScreenPosition(X, Y, Z: double);
+ procedure GetScreenPosition(var X, Y, Z: double);
+
+ procedure SetWidth(Width: double);
+ function GetWidth(): double;
+
+ procedure SetHeight(Height: double);
+ function GetHeight(): double;
+
+ procedure SetFrameRange(Range: TRectCoords);
+ function GetFrameRange(): TRectCoords;
+
+ function GetFrameAspect(): real;
+
+ procedure SetAspectCorrection(AspectCorrection: TAspectCorrection);
+ function GetAspectCorrection(): TAspectCorrection;
+
+ procedure SetAlpha(Alpha: double);
+ function GetAlpha(): double;
+
+ procedure SetReflectionSpacing(Spacing: double);
+ function GetReflectionSpacing(): double;
+
procedure GetFrame(Time: Extended);
- procedure DrawGL(Screen: integer);
+ procedure Draw();
+ procedure DrawReflection();
end;
TVideoPlayback_ProjectM = class( TInterfacedObject, IVideoVisualization )
@@ -262,6 +291,88 @@ begin
Result := true;
end;
+procedure TVideo_ProjectM.SetScreen(Screen: integer);
+begin
+end;
+
+function TVideo_ProjectM.GetScreen(): integer;
+begin
+ Result := 0;
+end;
+
+procedure TVideo_ProjectM.SetScreenPosition(X, Y, Z: double);
+begin
+end;
+
+procedure TVideo_ProjectM.GetScreenPosition(var X, Y, Z: double);
+begin
+ X := 0;
+ Y := 0;
+ Z := 0;
+end;
+
+procedure TVideo_ProjectM.SetWidth(Width: double);
+begin
+end;
+
+function TVideo_ProjectM.GetWidth(): double;
+begin
+ Result := 0;
+end;
+
+procedure TVideo_ProjectM.SetHeight(Height: double);
+begin
+end;
+
+function TVideo_ProjectM.GetHeight(): double;
+begin
+ Result := 0;
+end;
+
+procedure TVideo_ProjectM.SetFrameRange(Range: TRectCoords);
+begin
+end;
+
+function TVideo_ProjectM.GetFrameRange(): TRectCoords;
+begin
+ Result.Left := 0;
+ Result.Right := 0;
+ Result.Upper := 0;
+ Result.Lower := 0;
+end;
+
+function TVideo_ProjectM.GetFrameAspect(): real;
+begin
+ Result := 0;
+end;
+
+procedure TVideo_ProjectM.SetAspectCorrection(AspectCorrection: TAspectCorrection);
+begin
+end;
+
+function TVideo_ProjectM.GetAspectCorrection(): TAspectCorrection;
+begin
+ Result := acoStretch;
+end;
+
+procedure TVideo_ProjectM.SetAlpha(Alpha: double);
+begin
+end;
+
+function TVideo_ProjectM.GetAlpha(): double;
+begin
+ Result := 1;
+end;
+
+procedure TVideo_ProjectM.SetReflectionSpacing(Spacing: double);
+begin
+end;
+
+function TVideo_ProjectM.GetReflectionSpacing(): double;
+begin
+ Result := 0;
+end;
+
{**
* Returns the stack depth of the given OpenGL matrix mode stack.
*}
@@ -485,11 +596,11 @@ end;
* Draws the current frame to screen.
* TODO: this is not used yet. Data is directly drawn on GetFrame().
*}
-procedure TVideo_ProjectM.DrawGL(Screen: integer);
+procedure TVideo_ProjectM.Draw();
begin
{$IFDEF UseTexture}
// have a nice black background to draw on
- if (Screen = 1) then
+ if (fScreen = 1) then
begin
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
@@ -521,10 +632,10 @@ begin
// draw projectM frame
// Screen is 1 to 2. So current screen is from (Screen - 1) to (Screen)
glBegin(GL_QUADS);
- 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);
+ glTexCoord2f(0, 0); glVertex2f((fScreen - 1), 0);
+ glTexCoord2f(1, 0); glVertex2f(fScreen, 0);
+ glTexCoord2f(1, 1); glVertex2f(fScreen, 1);
+ glTexCoord2f(0, 1); glVertex2f((fScreen - 1), 1);
glEnd();
glDisable(GL_TEXTURE_2D);
@@ -538,6 +649,10 @@ begin
{$ENDIF}
end;
+procedure TVideo_ProjectM.DrawReflection();
+begin
+end;
+
{**
* Produces random "sound"-data in case no audio-data is available.
* Otherwise the visualization will look rather boring.