1+ package com .cloudwebrtc .webrtc .record ;
2+
3+ import android .graphics .ImageFormat ;
4+ import android .graphics .Rect ;
5+ import android .graphics .YuvImage ;
6+ import android .os .Handler ;
7+ import android .os .Looper ;
8+
9+ import org .webrtc .VideoFrame ;
10+ import org .webrtc .VideoSink ;
11+ import org .webrtc .VideoTrack ;
12+ import org .webrtc .YuvHelper ;
13+
14+ import java .io .File ;
15+ import java .io .FileOutputStream ;
16+ import java .io .IOException ;
17+ import java .nio .ByteBuffer ;
18+
19+ import io .flutter .plugin .common .MethodChannel ;
20+
21+ public class FrameCapturer implements VideoSink {
22+ private VideoTrack videoTrack ;
23+ private File file ;
24+ private final MethodChannel .Result callback ;
25+ private boolean gotFrame = false ;
26+
27+ public FrameCapturer (VideoTrack track , File file , MethodChannel .Result callback ) {
28+ videoTrack = track ;
29+ this .file = file ;
30+ this .callback = callback ;
31+ track .addSink (this );
32+ }
33+
34+ @ Override
35+ public void onFrame (VideoFrame videoFrame ) {
36+ if (gotFrame )
37+ return ;
38+ gotFrame = true ;
39+ videoFrame .retain ();
40+ VideoFrame .Buffer buffer = videoFrame .getBuffer ();
41+ VideoFrame .I420Buffer i420Buffer = buffer .toI420 ();
42+ ByteBuffer y = i420Buffer .getDataY ();
43+ ByteBuffer u = i420Buffer .getDataU ();
44+ ByteBuffer v = i420Buffer .getDataV ();
45+ int width = i420Buffer .getWidth ();
46+ int height = i420Buffer .getHeight ();
47+ int [] strides = new int [] {
48+ i420Buffer .getStrideY (),
49+ i420Buffer .getStrideU (),
50+ i420Buffer .getStrideV ()
51+ };
52+ final int chromaWidth = (width + 1 ) / 2 ;
53+ final int chromaHeight = (height + 1 ) / 2 ;
54+ final int minSize = width * height + chromaWidth * chromaHeight * 2 ;
55+ ByteBuffer yuvBuffer = ByteBuffer .allocateDirect (minSize );
56+ YuvHelper .I420ToNV12 (y , strides [0 ], v , strides [2 ], u , strides [1 ], yuvBuffer , width , height );
57+ YuvImage yuvImage = new YuvImage (
58+ yuvBuffer .array (),
59+ ImageFormat .NV21 ,
60+ width ,
61+ height ,
62+ strides
63+ );
64+ videoFrame .release ();
65+ new Handler (Looper .getMainLooper ()).post (() -> {
66+ videoTrack .removeSink (this );
67+ });
68+ try {
69+ if (!file .exists ())
70+ //noinspection ResultOfMethodCallIgnored
71+ file .createNewFile ();
72+ } catch (IOException io ) {
73+ callback .error ("IOException" , io .getLocalizedMessage (), io );
74+ return ;
75+ }
76+ try (FileOutputStream outputStream = new FileOutputStream (file )) {
77+ yuvImage .compressToJpeg (
78+ new Rect (0 , 0 , width , height ),
79+ 100 ,
80+ outputStream
81+ );
82+ callback .success (null );
83+ } catch (IOException io ) {
84+ callback .error ("IOException" , io .getLocalizedMessage (), io );
85+ } catch (IllegalArgumentException iae ) {
86+ callback .error ("IllegalArgumentException" , iae .getLocalizedMessage (), iae );
87+ } finally {
88+ file = null ;
89+ }
90+ }
91+
92+ }
0 commit comments