@@ -83,6 +83,36 @@ private static async Task WriteVideoFramesAsync(IEnumerable<OnnxImage> onnxImage
8383 }
8484
8585
86+ /// <summary>
87+ /// Writes the video stream to file.
88+ /// </summary>
89+ /// <param name="onnxImages">The onnx image stream.</param>
90+ /// <param name="filename">The filename.</param>
91+ /// <param name="frameRate">The frame rate.</param>
92+ /// <param name="aspectRatio">The aspect ratio.</param>
93+ /// <param name="cancellationToken">The cancellation token.</param>
94+ public static async Task WriteVideoStreamAsync ( VideoInfo videoInfo , IAsyncEnumerable < OnnxImage > videoStream , string filename , CancellationToken cancellationToken = default )
95+ {
96+ if ( File . Exists ( filename ) )
97+ File . Delete ( filename ) ;
98+
99+ using ( var videoWriter = CreateWriter ( filename , videoInfo . FrameRate , videoInfo . AspectRatio ) )
100+ {
101+ // Start FFMPEG
102+ videoWriter . Start ( ) ;
103+ await foreach ( var frame in videoStream )
104+ {
105+ // Write each frame to the input stream of FFMPEG
106+ await videoWriter . StandardInput . BaseStream . WriteAsync ( frame . GetImageBytes ( ) , cancellationToken ) ;
107+ }
108+
109+ // Done close stream and wait for app to process
110+ videoWriter . StandardInput . BaseStream . Close ( ) ;
111+ await videoWriter . WaitForExitAsync ( cancellationToken ) ;
112+ }
113+ }
114+
115+
86116 /// <summary>
87117 /// Reads the video information.
88118 /// </summary>
@@ -119,9 +149,16 @@ public static async Task<VideoInfo> ReadVideoInfoAsync(string filename)
119149 /// <returns></returns>
120150 public static async Task < List < OnnxImage > > ReadVideoFramesAsync ( byte [ ] videoBytes , float frameRate = 15 , CancellationToken cancellationToken = default )
121151 {
122- return await CreateFramesInternalAsync ( videoBytes , frameRate , cancellationToken )
123- . Select ( x => new OnnxImage ( x ) )
124- . ToListAsync ( cancellationToken ) ;
152+ string tempVideoPath = GetTempFilename ( ) ;
153+ try
154+ {
155+ await File . WriteAllBytesAsync ( tempVideoPath , videoBytes , cancellationToken ) ;
156+ return await ReadVideoStreamAsync ( tempVideoPath , frameRate , cancellationToken ) . ToListAsync ( cancellationToken ) ;
157+ }
158+ finally
159+ {
160+ DeleteTempFile ( tempVideoPath ) ;
161+ }
125162 }
126163
127164
@@ -134,10 +171,23 @@ public static async Task<List<OnnxImage>> ReadVideoFramesAsync(byte[] videoBytes
134171 /// <returns></returns>
135172 public static async Task < List < OnnxImage > > ReadVideoFramesAsync ( string filename , float frameRate = 15 , CancellationToken cancellationToken = default )
136173 {
137- var videoBytes = await File . ReadAllBytesAsync ( filename , cancellationToken ) ;
138- return await CreateFramesInternalAsync ( videoBytes , frameRate , cancellationToken )
139- . Select ( x => new OnnxImage ( x ) )
140- . ToListAsync ( cancellationToken ) ;
174+ return await ReadVideoStreamAsync ( filename , frameRate , cancellationToken ) . ToListAsync ( cancellationToken ) ;
175+ }
176+
177+
178+ /// <summary>
179+ /// Reads the video frames as a stream.
180+ /// </summary>
181+ /// <param name="filename">The filename.</param>
182+ /// <param name="frameRate">The frame rate.</param>
183+ /// <param name="cancellationToken">The cancellation token.</param>
184+ /// <returns></returns>
185+ public static async IAsyncEnumerable < OnnxImage > ReadVideoStreamAsync ( string filename , float frameRate = 15 , [ EnumeratorCancellation ] CancellationToken cancellationToken = default )
186+ {
187+ await foreach ( var frameBytes in CreateFramesInternalAsync ( filename , frameRate , cancellationToken ) )
188+ {
189+ yield return new OnnxImage ( frameBytes ) ;
190+ }
141191 }
142192
143193
@@ -152,76 +202,67 @@ public static async Task<List<OnnxImage>> ReadVideoFramesAsync(string filename,
152202 /// <param name="cancellationToken">The cancellation token.</param>
153203 /// <returns></returns>
154204 /// <exception cref="Exception">Invalid PNG header</exception>
155- private static async IAsyncEnumerable < byte [ ] > CreateFramesInternalAsync ( byte [ ] videoData , float fps = 15 , [ EnumeratorCancellation ] CancellationToken cancellationToken = default )
205+ private static async IAsyncEnumerable < byte [ ] > CreateFramesInternalAsync ( string fileName , float fps = 15 , [ EnumeratorCancellation ] CancellationToken cancellationToken = default )
156206 {
157- string tempVideoPath = GetTempFilename ( ) ;
158- try
207+ using ( var ffmpegProcess = CreateReader ( fileName , fps ) )
159208 {
160- await File . WriteAllBytesAsync ( tempVideoPath , videoData , cancellationToken ) ;
161- using ( var ffmpegProcess = CreateReader ( tempVideoPath , fps ) )
162- {
163- // Start FFMPEG
164- ffmpegProcess . Start ( ) ;
209+ // Start FFMPEG
210+ ffmpegProcess . Start ( ) ;
165211
166- // FFMPEG output stream
167- var processOutputStream = ffmpegProcess . StandardOutput . BaseStream ;
212+ // FFMPEG output stream
213+ var processOutputStream = ffmpegProcess . StandardOutput . BaseStream ;
168214
169- // Buffer to hold the current image
170- var buffer = new byte [ 20480000 ] ;
215+ // Buffer to hold the current image
216+ var buffer = new byte [ 20480000 ] ;
171217
172- var currentIndex = 0 ;
173- while ( ! cancellationToken . IsCancellationRequested )
174- {
175- // Reset the index new PNG
176- currentIndex = 0 ;
218+ var currentIndex = 0 ;
219+ while ( ! cancellationToken . IsCancellationRequested )
220+ {
221+ // Reset the index new PNG
222+ currentIndex = 0 ;
177223
178- // Read the PNG Header
179- if ( await processOutputStream . ReadAsync ( buffer . AsMemory ( currentIndex , 8 ) , cancellationToken ) <= 0 )
180- break ;
224+ // Read the PNG Header
225+ if ( await processOutputStream . ReadAsync ( buffer . AsMemory ( currentIndex , 8 ) , cancellationToken ) <= 0 )
226+ break ;
181227
182- currentIndex += 8 ; // header length
228+ currentIndex += 8 ; // header length
183229
184- if ( ! IsImageHeader ( buffer ) )
185- throw new Exception ( "Invalid PNG header" ) ;
230+ if ( ! IsImageHeader ( buffer ) )
231+ throw new Exception ( "Invalid PNG header" ) ;
186232
187- // loop through each chunk
188- while ( true )
189- {
190- // Read the chunk header
191- await processOutputStream . ReadAsync ( buffer . AsMemory ( currentIndex , 12 ) , cancellationToken ) ;
233+ // loop through each chunk
234+ while ( true )
235+ {
236+ // Read the chunk header
237+ await processOutputStream . ReadAsync ( buffer . AsMemory ( currentIndex , 12 ) , cancellationToken ) ;
192238
193- var chunkIndex = currentIndex ;
194- currentIndex += 12 ; // Chunk header length
239+ var chunkIndex = currentIndex ;
240+ currentIndex += 12 ; // Chunk header length
195241
196- // Get the chunk's content size in bytes from the header we just read
197- var totalSize = buffer [ chunkIndex ] << 24 | buffer [ chunkIndex + 1 ] << 16 | buffer [ chunkIndex + 2 ] << 8 | buffer [ chunkIndex + 3 ] ;
198- if ( totalSize > 0 )
242+ // Get the chunk's content size in bytes from the header we just read
243+ var totalSize = buffer [ chunkIndex ] << 24 | buffer [ chunkIndex + 1 ] << 16 | buffer [ chunkIndex + 2 ] << 8 | buffer [ chunkIndex + 3 ] ;
244+ if ( totalSize > 0 )
245+ {
246+ var totalRead = 0 ;
247+ while ( totalRead < totalSize )
199248 {
200- var totalRead = 0 ;
201- while ( totalRead < totalSize )
202- {
203- int read = await processOutputStream . ReadAsync ( buffer . AsMemory ( currentIndex , totalSize - totalRead ) , cancellationToken ) ;
204- currentIndex += read ;
205- totalRead += read ;
206- }
207- continue ;
249+ int read = await processOutputStream . ReadAsync ( buffer . AsMemory ( currentIndex , totalSize - totalRead ) , cancellationToken ) ;
250+ currentIndex += read ;
251+ totalRead += read ;
208252 }
209-
210- // If the size is 0 and is the end of the image
211- if ( totalSize == 0 && IsImageEnd ( buffer , chunkIndex ) )
212- break ;
253+ continue ;
213254 }
214255
215- yield return buffer [ ..currentIndex ] ;
256+ // If the size is 0 and is the end of the image
257+ if ( totalSize == 0 && IsImageEnd ( buffer , chunkIndex ) )
258+ break ;
216259 }
217260
218- if ( cancellationToken . IsCancellationRequested )
219- ffmpegProcess . Kill ( ) ;
261+ yield return buffer [ ..currentIndex ] ;
220262 }
221- }
222- finally
223- {
224- DeleteTempFile ( tempVideoPath ) ;
263+
264+ if ( cancellationToken . IsCancellationRequested )
265+ ffmpegProcess . Kill ( ) ;
225266 }
226267 }
227268
0 commit comments