3232import java .io .IOException ;
3333import java .nio .file .Files ;
3434import java .nio .file .Paths ;
35+ import java .util .ArrayList ;
36+ import java .util .List ;
3537import java .lang .foreign .*;
3638import libffmpeg .AVCodecContext ;
3739import libffmpeg .AVFormatContext ;
5153public class LibffmpegMain {
5254 private static int NUM_FRAMES_TO_CAPTURE = 5 ;
5355
54- static class ExitException extends RuntimeException {
55- final int exitCode ;
56- ExitException (int exitCode , String msg ) {
57- super (msg );
58- this .exitCode = exitCode ;
56+ record Exit (String message , int exitCode ) {}
57+
58+ public static void main (String [] args ) {
59+ var exit = run (args );
60+ System .err .println (exit .message ());
61+ System .exit (exit .exitCode ());
62+ }
63+
64+ private static class ArenaCleanup implements AutoCloseable {
65+ private Arena arena = Arena .openConfined ();
66+ private final List <Runnable > preCloseActions = new ArrayList <>();
67+
68+ void addCleanup (Runnable runnable ) {
69+ preCloseActions .add (runnable );
70+ }
71+
72+ Arena arena () {
73+ return arena ;
74+ }
75+
76+ @ Override
77+ public void close () {
78+ preCloseActions .forEach (Runnable ::run );
79+ System .out .println ("cleanup done" );
80+ arena .close ();
5981 }
6082 }
6183
62- public static void main (String [] args ) {
84+ private static Exit run (String [] args ) {
6385 if (args .length != 1 ) {
64- System .err .println ("please pass a .mp4 file" );
65- System .exit (1 );
86+ return new Exit ("please pass a .mp4 file" , 1 );
6687 }
6788
6889 av_register_all ();
6990
70- int exitCode = 0 ;
7191 var pCodecCtxOrig = NULL ;
7292 var pCodecCtx = NULL ;
7393 var pFrame = NULL ;
7494 var pFrameRGB = NULL ;
7595 var buffer = NULL ;
7696
77- try (var session = MemorySession .openConfined ()) {
97+ try (var arenaCleanup = new ArenaCleanup ()) {
98+ var arena = arenaCleanup .arena ();
7899 // AVFormatContext *ppFormatCtx;
79- var ppFormatCtx = session .allocate (C_POINTER );
100+ var ppFormatCtx = arena .allocate (C_POINTER );
80101 // char* fileName;
81102 var fileName = arena .allocateUtf8String (args [0 ]);
82103
83104 // open video file
84105 if (avformat_open_input (ppFormatCtx , fileName , NULL , NULL ) != 0 ) {
85- throw new ExitException ( 1 , "Cannot open " + args [0 ]);
106+ return new Exit ( "Cannot open " + args [0 ], 1 );
86107 }
87108 System .out .println ("opened " + args [0 ]);
88109 // AVFormatContext *pFormatCtx;
89110 var pFormatCtx = ppFormatCtx .get (C_POINTER , 0 );
90111
91112 // Retrieve stream info
92113 if (avformat_find_stream_info (pFormatCtx , NULL ) < 0 ) {
93- throw new ExitException (1 , "Could not find stream information" );
114+ return new Exit ("Could not find stream information" , 1 );
115+
94116 }
95117
96- arena . addCloseAction (()-> {
118+ arenaCleanup . addCleanup (() -> {
97119 // Close the video file
98120 avformat_close_input (ppFormatCtx );
99121 });
@@ -129,13 +151,13 @@ public static void main(String[] args) {
129151 }
130152
131153 if (videoStream == -1 ) {
132- throw new ExitException ( 1 , "Didn't find a video stream" );
154+ return new Exit ( "Didn't find a video stream" , 1 );
133155 } else {
134156 System .out .println ("Found video stream (index: " + videoStream + ")" );
135157 }
136158
137159 if (pCodec .equals (NULL )) {
138- throw new ExitException ( 1 , "Unsupported codec" );
160+ return new Exit ( "Unsupported codec" , 1 );
139161 }
140162
141163 // Copy context
@@ -144,12 +166,12 @@ public static void main(String[] args) {
144166 // AVCodecContext *pCodecCtx;
145167 pCodecCtx = avcodec_alloc_context3 (pCodec );
146168 if (avcodec_copy_context (pCodecCtx , pCodecCtxOrig ) != 0 ) {
147- throw new ExitException ( 1 , "Cannot copy context" );
169+ return new Exit ( "Cannot copy context" , 1 );
148170 }
149171
150172 // Open codec
151173 if (avcodec_open2 (pCodecCtx , pCodec , NULL ) < 0 ) {
152- throw new ExitException ( 1 , "Cannot open codec" );
174+ return new Exit ( "Cannot open codec" , 1 );
153175 }
154176
155177 // Allocate video frame
@@ -167,13 +189,13 @@ public static void main(String[] args) {
167189
168190
169191 if (pFrame .equals (NULL )) {
170- throw new ExitException ( 1 , "Cannot allocate frame" );
192+ return new Exit ( "Cannot allocate frame" , 1 );
171193 }
172194 if (pFrameRGB .equals (NULL )) {
173- throw new ExitException ( 1 , "Cannot allocate RGB frame" );
195+ return new Exit ( "Cannot allocate RGB frame" , 1 );
174196 }
175197 if (buffer .equals (NULL )) {
176- throw new ExitException ( 1 , "cannot allocate buffer" );
198+ return new Exit ( "cannot allocate buffer" , 1 );
177199 }
178200
179201 // Assign appropriate parts of buffer to image planes in pFrameRGB
@@ -188,9 +210,9 @@ public static void main(String[] args) {
188210
189211 int i = 0 ;
190212 // ACPacket packet;
191- var packet = AVPacket .allocate (session );
213+ var packet = AVPacket .allocate (arena );
192214 // int* pFrameFinished;
193- var pFrameFinished = session .allocate (C_INT );
215+ var pFrameFinished = arena .allocate (C_INT );
194216
195217 while (av_read_frame (pFormatCtx , packet ) >= 0 ) {
196218 // Is this a packet from the video stream?
@@ -210,10 +232,10 @@ public static void main(String[] args) {
210232 // Save the frame to disk
211233 if (++i <= NUM_FRAMES_TO_CAPTURE ) {
212234 try {
213- saveFrame (pFrameRGB , session , width , height , i );
235+ saveFrame (pFrameRGB , arena . scope () , width , height , i );
214236 } catch (Exception exp ) {
215237 exp .printStackTrace ();
216- throw new ExitException ( 1 , "save frame failed for frame " + i );
238+ return new Exit ( "save frame failed for frame " + i , 1 );
217239 }
218240 }
219241 }
@@ -222,11 +244,6 @@ public static void main(String[] args) {
222244 // Free the packet that was allocated by av_read_frame
223245 av_free_packet (packet );
224246 }
225-
226- throw new ExitException (0 , "Goodbye!" );
227- } catch (ExitException ee ) {
228- System .err .println (ee .getMessage ());
229- exitCode = ee .exitCode ;
230247 } finally {
231248 // clean-up everything
232249
@@ -254,10 +271,10 @@ public static void main(String[] args) {
254271 }
255272 }
256273
257- System . exit ( exitCode );
274+ return new Exit ( "Goodbye!" , 0 );
258275 }
259276
260- private static void saveFrame (MemorySegment frameRGB , MemorySession session ,
277+ private static void saveFrame (MemorySegment frameRGB , SegmentScope scope ,
261278 int width , int height , int iFrame )
262279 throws IOException {
263280 var header = String .format ("P6\n %d %d\n 255\n " , width , height );
@@ -273,7 +290,7 @@ private static void saveFrame(MemorySegment frameRGB, MemorySession session,
273290 // Write pixel data
274291 for (int y = 0 ; y < height ; y ++) {
275292 // frameRGB.data[0] + y*frameRGB.linesize[0] is the pointer. And 3*width size of data
276- var pixelArray = MemorySegment .ofAddress (pdata .address () + y *linesize , 3 *width , session );
293+ var pixelArray = MemorySegment .ofAddress (pdata .address () + y *linesize , 3 *width , scope );
277294 // dump the pixel byte buffer to file
278295 os .write (pixelArray .toArray (C_CHAR ));
279296 }
0 commit comments