1
- using FFMpegCore ;
2
- using OnnxStack . Core . Config ;
1
+ using OnnxStack . Core . Config ;
3
2
using OnnxStack . Core . Image ;
4
3
using System ;
5
4
using System . Collections . Generic ;
6
5
using System . Diagnostics ;
7
6
using System . IO ;
8
7
using System . Linq ;
9
8
using System . Runtime . CompilerServices ;
9
+ using System . Text . Json ;
10
10
using System . Threading ;
11
11
using System . Threading . Tasks ;
12
12
@@ -73,7 +73,7 @@ private static async Task WriteVideoFramesAsync(IEnumerable<OnnxImage> onnxImage
73
73
foreach ( var image in onnxImages )
74
74
{
75
75
// Write each frame to the input stream of FFMPEG
76
- await videoWriter . StandardInput . BaseStream . WriteAsync ( image . GetImageBytes ( ) , cancellationToken ) ;
76
+ await image . CopyToStreamAsync ( videoWriter . StandardInput . BaseStream , cancellationToken ) ;
77
77
}
78
78
79
79
// Done close stream and wait for app to process
@@ -103,7 +103,7 @@ public static async Task WriteVideoStreamAsync(VideoInfo videoInfo, IAsyncEnumer
103
103
await foreach ( var frame in videoStream )
104
104
{
105
105
// Write each frame to the input stream of FFMPEG
106
- await frame . CopyToStreamAsync ( videoWriter . StandardInput . BaseStream ) ;
106
+ await frame . CopyToStreamAsync ( videoWriter . StandardInput . BaseStream , cancellationToken ) ;
107
107
}
108
108
109
109
// Done close stream and wait for app to process
@@ -118,12 +118,17 @@ public static async Task WriteVideoStreamAsync(VideoInfo videoInfo, IAsyncEnumer
118
118
/// </summary>
119
119
/// <param name="videoBytes">The video bytes.</param>
120
120
/// <returns></returns>
121
- public static async Task < VideoInfo > ReadVideoInfoAsync ( byte [ ] videoBytes )
121
+ public static async Task < VideoInfo > ReadVideoInfoAsync ( byte [ ] videoBytes , CancellationToken cancellationToken = default )
122
122
{
123
- using ( var memoryStream = new MemoryStream ( videoBytes ) )
123
+ string tempVideoPath = GetTempFilename ( ) ;
124
+ try
125
+ {
126
+ await File . WriteAllBytesAsync ( tempVideoPath , videoBytes , cancellationToken ) ;
127
+ return await ReadVideoInfoAsync ( tempVideoPath , cancellationToken ) ;
128
+ }
129
+ finally
124
130
{
125
- var result = await FFProbe . AnalyseAsync ( memoryStream ) . ConfigureAwait ( false ) ;
126
- return new VideoInfo ( result . PrimaryVideoStream . Width , result . PrimaryVideoStream . Height , result . Duration , ( int ) result . PrimaryVideoStream . FrameRate ) ;
131
+ DeleteTempFile ( tempVideoPath ) ;
127
132
}
128
133
}
129
134
@@ -133,10 +138,29 @@ public static async Task<VideoInfo> ReadVideoInfoAsync(byte[] videoBytes)
133
138
/// </summary>
134
139
/// <param name="filename">The filename.</param>
135
140
/// <returns></returns>
136
- public static async Task < VideoInfo > ReadVideoInfoAsync ( string filename )
141
+ public static async Task < VideoInfo > ReadVideoInfoAsync ( string filename , CancellationToken cancellationToken = default )
137
142
{
138
- var result = await FFProbe . AnalyseAsync ( filename ) . ConfigureAwait ( false ) ;
139
- return new VideoInfo ( result . PrimaryVideoStream . Width , result . PrimaryVideoStream . Height , result . Duration , ( int ) result . PrimaryVideoStream . FrameRate ) ;
143
+
144
+ using ( var metadataReader = CreateMetadataReader ( filename ) )
145
+ {
146
+ // Start FFMPEG
147
+ metadataReader . Start ( ) ;
148
+
149
+ var videoInfo = default ( VideoInfo ) ;
150
+ using ( StreamReader reader = metadataReader . StandardOutput )
151
+ {
152
+ string result = await reader . ReadToEndAsync ( ) ;
153
+ var videoMetadata = JsonSerializer . Deserialize < VideoMetadata > ( result ) ;
154
+ var videoStream = videoMetadata . Streams . FirstOrDefault ( ) ;
155
+ if ( videoStream is null )
156
+ throw new Exception ( "Failed to parse video stream metadata" ) ;
157
+
158
+ videoInfo = new VideoInfo ( videoStream . Height , videoStream . Width , videoStream . Duration , videoStream . FramesPerSecond ) ;
159
+ }
160
+
161
+ await metadataReader . WaitForExitAsync ( cancellationToken ) ;
162
+ return videoInfo ;
163
+ }
140
164
}
141
165
142
166
@@ -308,7 +332,7 @@ private static Process CreateReader(string inputFile, float fps)
308
332
{
309
333
var ffmpegProcess = new Process ( ) ;
310
334
ffmpegProcess . StartInfo . FileName = _configuration . FFmpegPath ;
311
- ffmpegProcess . StartInfo . Arguments = $ "-hide_banner -loglevel error -i \" { inputFile } \" -c:v png -r { fps } -f image2pipe -";
335
+ ffmpegProcess . StartInfo . Arguments = $ "-hide_banner -loglevel error -hwaccel:v auto - i \" { inputFile } \" -c:v png -r { fps } -f image2pipe -";
312
336
ffmpegProcess . StartInfo . RedirectStandardOutput = true ;
313
337
ffmpegProcess . StartInfo . UseShellExecute = false ;
314
338
ffmpegProcess . StartInfo . CreateNoWindow = true ;
@@ -329,14 +353,31 @@ private static Process CreateWriter(string outputFile, float fps, double aspectR
329
353
var codec = preserveTransparency ? "png" : "libx264" ;
330
354
var format = preserveTransparency ? "yuva420p" : "yuv420p" ;
331
355
ffmpegProcess . StartInfo . FileName = _configuration . FFmpegPath ;
332
- ffmpegProcess . StartInfo . Arguments = $ "-hide_banner -loglevel error -framerate { fps : F4} -i - -c:v { codec } -movflags +faststart -vf format={ format } -aspect { aspectRatio } { outputFile } ";
356
+ ffmpegProcess . StartInfo . Arguments = $ "-hide_banner -loglevel error -framerate { fps : F4} -hwaccel:v auto - i - -c:v { codec } -movflags +faststart -vf format={ format } -aspect { aspectRatio } { outputFile } ";
333
357
ffmpegProcess . StartInfo . RedirectStandardInput = true ;
334
358
ffmpegProcess . StartInfo . UseShellExecute = false ;
335
359
ffmpegProcess . StartInfo . CreateNoWindow = true ;
336
360
return ffmpegProcess ;
337
361
}
338
362
339
363
364
+ /// <summary>
365
+ /// Creates the metadata reader.
366
+ /// </summary>
367
+ /// <param name="inputFile">The input file.</param>
368
+ /// <returns></returns>
369
+ private static Process CreateMetadataReader ( string inputFile )
370
+ {
371
+ var ffprobeProcess = new Process ( ) ;
372
+ ffprobeProcess . StartInfo . FileName = _configuration . FFprobePath ;
373
+ ffprobeProcess . StartInfo . Arguments = $ "-v quiet -print_format json -show_format -show_streams { inputFile } ";
374
+ ffprobeProcess . StartInfo . RedirectStandardOutput = true ;
375
+ ffprobeProcess . StartInfo . UseShellExecute = false ;
376
+ ffprobeProcess . StartInfo . CreateNoWindow = true ;
377
+ return ffprobeProcess ;
378
+ }
379
+
380
+
340
381
/// <summary>
341
382
/// Determines whether we are at the start of a PNG image in the specified buffer.
342
383
/// </summary>
0 commit comments