@ -1,4 +1,5 @@
@@ -1,4 +1,5 @@
using System.Buffers ;
using System.Globalization ;
using System.IO.Pipelines ;
using CliWrap ;
using ErsatzTV.Core ;
@ -55,17 +56,32 @@ public class MotionElement(
@@ -55,17 +56,32 @@ public class MotionElement(
Resolution squarePixelFrameSize ,
Resolution frameSize ,
int frameRate ,
TimeSpan seek ,
CancellationToken cancellationToken )
{
try
{
_ startTime = TimeSpan . FromSeconds ( motionElement . StartSeconds ? ? 0 ) ;
ProbeResult probeResult = await ProbeMotionElement ( frameSize ) ;
var overlayDuration = probeResult . Duration ;
// already past the time when this is supposed to play; don't do any more work
if ( _ startTime + overlayDuration < seek )
{
IsFinished = true ;
return ;
}
var pipe = new Pipe ( ) ;
_ pipeReader = pipe . Reader ;
_ startTime = TimeSpan . FromSeconds ( motionElement . StartSeconds ? ? 0 ) ;
var overlaySeekTime = TimeSpan . Zero ;
if ( _ startTime < seek )
{
overlaySeekTime = seek - _ startTime ;
}
SizeAndDecoder sizeAndDecoder = await ProbeMotionElement ( frameSize ) ;
Resolution sourceSize = sizeAndDecoder . Size ;
Resolution sourceSize = probeResult . Size ;
int scaledWidth = sourceSize . Width ;
int scaledHeight = sourceSize . Height ;
@ -118,23 +134,30 @@ public class MotionElement(
@@ -118,23 +134,30 @@ public class MotionElement(
List < string > arguments = [ "-nostdin" , "-hide_banner" , "-nostats" , "-loglevel" , "error" ] ;
foreach ( string decoder in sizeAndDecoder . Decoder )
foreach ( string decoder in probeResult . Decoder )
{
arguments . AddRange ( [ "-c:v" , decoder ] ) ;
}
if ( overlaySeekTime > TimeSpan . Zero )
{
arguments . AddRange ( [ "-ss" , overlaySeekTime . TotalSeconds . ToString ( CultureInfo . InvariantCulture ) ] ) ;
}
arguments . AddRange (
[
"-i" , motionElement . VideoPath ,
] ) ;
var videoFilter = $"fps={frameRate}" ;
if ( motionElement . Scale )
{
arguments . AddRange ( [ "-vf" , $"scale={targetSize.Width}:{targetSize.Height}" ] ) ;
videoFilter + = $",scale={targetSize.Width}:{targetSize.Height}" ;
}
arguments . AddRange (
[
"-vf" , videoFilter ,
"-f" , "image2pipe" ,
"-pix_fmt" , "bgra" ,
"-vcodec" , "rawvideo" ,
@ -157,7 +180,7 @@ public class MotionElement(
@@ -157,7 +180,7 @@ public class MotionElement(
}
catch ( Exception ex )
{
IsFail ed = true ;
IsFinish ed = true ;
logger . LogWarning ( ex , "Failed to initialize motion element; will disable for this content" ) ;
}
}
@ -169,7 +192,7 @@ public class MotionElement(
@@ -169,7 +192,7 @@ public class MotionElement(
TimeSpan channelTime ,
CancellationToken cancellationToken )
{
if ( contentTime < _ startTime | | _ isFinished )
if ( _ isFinished | | contentTime < _ startTime )
{
return Option < PreparedElementImage > . None ;
}
@ -225,7 +248,7 @@ public class MotionElement(
@@ -225,7 +248,7 @@ public class MotionElement(
}
}
private async Task < SizeAndDecoder > ProbeMotionElement ( Resolution frameSize )
private async Task < ProbeResult > ProbeMotionElement ( Resolution frameSize )
{
try
{
@ -249,9 +272,10 @@ public class MotionElement(
@@ -249,9 +272,10 @@ public class MotionElement(
} ;
}
return new SizeAndDecoder (
return new ProbeResult (
new Resolution { Width = mediaVersion . Width , Height = mediaVersion . Height } ,
decoder ) ;
decoder ,
mediaVersion . Duration ) ;
}
}
}
@ -260,8 +284,8 @@ public class MotionElement(
@@ -260,8 +284,8 @@ public class MotionElement(
// do nothing
}
return new SizeAndDecoder ( frameSize , Option < string > . None ) ;
return new ProbeResult ( frameSize , Option < string > . None , TimeSpan . Zero ) ;
}
private record SizeAndDecoder ( Resolution Size , Option < string > Decoder ) ;
private record ProbeResult ( Resolution Size , Option < string > Decoder , TimeSpan Duration ) ;
}