8000 Fixed video size, cleanup, small fixes · Boumour/flutter-webrtc@20f009c · GitHub
[go: up one dir, main page]

Skip to content

Commit 20f009c

Browse files
committed
Fixed video size, cleanup, small fixes
1 parent 2f67335 commit 20f009c

File tree

3 files changed

+48
-52
lines changed

3 files changed

+48
-52
lines changed

android/src/main/java/com/cloudwebrtc/webrtc/record/MediaRecorderImpl.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ public void startRecording(File file) throws IOException {
3131
//noinspection ResultOfMethodCallIgnored
3232
file.getParentFile().mkdirs();
3333
if (videoTrack != null) {
34-
videoFileRenderer = new VideoFileRenderer(file.getAbsolutePath(), 1280, 720, EglUtils.getRootEglBaseContext());
34+
videoFileRenderer = new VideoFileRenderer(
35+
file.getAbsolutePath(),
36+
EglUtils.getRootEglBaseContext(),
37+
audioInterceptor != null
38+
);
3539
videoTrack.addSink(videoFileRenderer);
3640
if (audioInterceptor != null)
3741
audioInterceptor.attachCallback(id, videoFileRenderer);

android/src/main/java/com/cloudwebrtc/webrtc/record/VideoFileRenderer.java

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.cloudwebrtc.webrtc.record;
22

3-
import android.media.AudioFormat;
43
import android.media.MediaCodec;
54
import android.media.MediaCodecInfo;
65
import android.media.MediaFormat;
@@ -34,12 +33,14 @@ class VideoFileRenderer implements VideoSink, SamplesReadyCallback {
3433
private final Handler audioThreadHandler;
3534
private final FileOutputStream videoOutFile;
3635
private final String outputFileName;
37-
private final int outputFileWidth;
38-
private final int outputFileHeight;
36+
private int outputFileWidth = -1;
37+
private int outputFileHeight = -1;
3938
private ByteBuffer[] encoderOutputBuffers;
4039
private ByteBuffer[] audioInputBuffers;
4140
private ByteBuffer[] audioOutputBuffers;
4241
private EglBase eglBase;
42+
private EglBase.Context sharedContext;
43+
private VideoFrameDrawer frameDrawer;
4344

4445
// TODO: these ought to be configurable as well
4546
private static final String MIME_TYPE = "video/avc"; // H.264 Advanced Video Coding
@@ -49,23 +50,18 @@ class VideoFileRenderer implements VideoSink, SamplesReadyCallback {
4950
private MediaMuxer mediaMuxer;
5051
private MediaCodec encoder;
5152
private MediaCodec.BufferInfo bufferInfo;
52-
private int trackIndex;
53-
private int audioTrackIndex = -1;
53+
private int trackIndex = -1;
54+
private int audioTrackIndex;
5455
private boolean isRunning = true;
5556
private GlRectDrawer drawer;
5657
private Surface surface;
5758
private MediaCodec audioEncoder;
58-
private long framesCount = 0;
59-
private long samplesCount = 0;
6059

61-
VideoFileRenderer(String outputFile, int outputFileWidth, int outputFileHeight,
62-
final EglBase.Context sharedContext) throws IOException {
60+
VideoFileRenderer(String outputFile, final EglBase.Context sharedContext, boolean withAudio) throws IOException {
6361
if ((outputFileWidth % 2) == 1 || (outputFileHeight % 2) == 1) {
6462
throw new IllegalArgumentException("Does not support uneven width or height");
6563
}
6664
this.outputFileName = outputFile;
67-
this.outputFileWidth = outputFileWidth;
68-
this.outputFileHeight = outputFileHeight;
6965
videoOutFile = new FileOutputStream(outputFile);
7066
renderThread = new HandlerThread(TAG + "RenderThread");
7167
renderThread.start();
@@ -77,7 +73,18 @@ class VideoFileRenderer implements VideoSink, SamplesReadyCallback {
7773
audioThreadHandler = new Handler(audioThread.getLooper());
7874
fileThreadHandler = new Handler(fileThread.getLooper());
7975
bufferInfo = new MediaCodec.BufferInfo();
76+
this.sharedContext = sharedContext;
77+
78+
// Create a MediaMuxer. We can't add the video track and start() the muxer here,
79+
// because our MediaFormat doesn't have the Magic Goodies. These can only be
80+
57AE // obtained from the encoder after it has started processing data.
81+
mediaMuxer = new MediaMuxer(outputFile,
82+
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
8083

84+
audioTrackIndex = withAudio ? 0 : -1;
85+
}
86+
87+
private void initVideoEncoder() {
8188
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, outputFileWidth, outputFileHeight);
8289

8390
// Set some properties. Failing to specify some of these can cause the MediaCodec
@@ -90,71 +97,59 @@ class VideoFileRenderer implements VideoSink, SamplesReadyCallback {
9097

9198
// Create a MediaCodec encoder, and configure it with our format. Get a Surface
9299
// we can use for input and wrap it with a class that handles the EGL work.
93-
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
94-
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
95-
ThreadUtils.invokeAtFrontUninterruptibly(renderThreadHandler, new Runnable() {
96-
@Override
97-
public void run() {
100+
try {
101+
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
102+
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
103+
renderThreadHandler.post(() -> {
98104
eglBase = EglBase.create(sharedContext, EglBase.CONFIG_RECORDABLE);
99105
surface = encoder.createInputSurface();
100106
eglBase.createSurface(surface);
101107
eglBase.makeCurrent();
102108
drawer = new GlRectDrawer();
103-
}
104-
});
105-
106-
// Create a MediaMuxer. We can't add the video track and start() the muxer here,
107-
// because our MediaFormat doesn't have the Magic Goodies. These can only be
108-
// obtained from the encoder after it has started processing data.
109-
//
110-
// We're not actually interested in multiplexing audio. We just want to convert
111-
// the raw H.264 elementary stream we get from MediaCodec into a .mp4 file.
112-
mediaMuxer = new MediaMuxer(outputFile,
113-
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
114-
115-
trackIndex = -1;
109+
});
110+
} catch (Exception e) {
111+
Log.wtf(TAG, e);
112+
}
116113
}
117114

118115
@Override
119116
public void onFrame(VideoFrame frame) {
120117
frame.retain();
118+
if (outputFileWidth == -1) {
119+
outputFileWidth = frame.getRotatedWidth();
120+
outputFileHeight = frame.getRotatedHeight();
121+
initVideoEncoder();
122+
}
121123
renderThreadHandler.post(() -> renderFrameOnRenderThread(frame));
122124
}
123125

124-
private VideoFrameDrawer frameDrawer;
125-
126126
private void renderFrameOnRenderThread(VideoFrame frame) {
127127
if (frameDrawer == null) {
128128
frameDrawer = new VideoFrameDrawer();
129129
}
130-
framesCount++;
131130
frameDrawer.drawFrame(frame, drawer, null, 0, 0, outputFileWidth, outputFileHeight);
132131
frame.release();
133-
drainEncoder(false);
132+
drainEncoder();
134133
eglBase.swapBuffers();
135134
}
136135

137136
/**
138137
* Release all resources. All already posted frames will be rendered first.
139138
*/
140139
void release() {
141-
Log.e(TAG, "Frames " + String.valueOf(framesCount) + " samples " + String.valueOf(samplesCount));
142140
final CountDownLatch cleanupBarrier = new CountDownLatch(2);
143141
isRunning = false;
144142
renderThreadHandler.post(() -> {
145-
// encoder.flush();
146-
//drainEncoder(false);
147143
encoder.stop();
148144
encoder.release();
149145
eglBase.release();
150146
renderThread.quit();
151147
cleanupBarrier.countDown();
152148
});
153149
audioThreadHandler.post(() -> {
154-
// audioEncoder.flush();
155-
//drainAudio();
156150
audioEncoder.stop();
157151
audioEncoder.release();
152+
audioThread.quit();
158153
cleanupBarrier.countDown();
159154
});
160155
ThreadUtils.awaitUninterruptibly(cleanupBarrier);
@@ -181,7 +176,7 @@ void release() {
181176
private volatile boolean muxerStarted = false;
182177
private long videoFrameStart = 0;
183178

184-
private void drainEncoder(boolean endOfStream) {
179+
private void drainEncoder() {
185180
if (!encoderStarted) {
186181
encoder.start();
187182
encoderOutputBuffers = encoder.getOutputBuffers();
@@ -191,9 +186,7 @@ private void drainEncoder(boolean endOfStream) {
191186
while (true) {
192187
int encoderStatus = encoder.dequeueOutputBuffer(bufferInfo, 10000);
193188
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
194-
Log.i(TAG, "no output from encoder available");
195-
if (!endOfStream)
196-
break;
189+
break;
197190
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
198191
// not expected for an encoder
199192
encoderOutputBuffers = encoder.getOutputBuffers();
@@ -217,6 +210,7 @@ private void drainEncoder(boolean endOfStream) {
217210
ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
218211
if (encodedData == null) {
219212
Log.e(TAG, "encoderOutputBuffer " + encoderStatus + " was null");
213+
break;
220214
}
221215
// It's usually necessary to adjust the ByteBuffer values to match BufferInfo.
222216
encodedData.position(bufferInfo.offset);
@@ -248,17 +242,16 @@ private void drainAudio() {
248242
while (true) {
249243
int encoderStatus = audioEncoder.dequeueOutputBuffer(bufferInfo, 10000);
250244
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
251-
Log.i(TAG, "no output from audio encoder available");
252245
break;
253246
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
254247
// not expected for an encoder
255248
audioOutputBuffers = audioEncoder.getOutputBuffers();
256-
Log.e(TAG, "encoder output buffers changed");
249+
Log.w(TAG, "encoder output buffers changed");
257250
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
258251
// not expected for an encoder
259252
MediaFormat newFormat = audioEncoder.getOutputFormat();
260253

261-
Log.e(TAG, "encoder output format changed: " + newFormat);
254+
Log.w(TAG, "encoder output format changed: " + newFormat);
262255
audioTrackIndex = mediaMuxer.addTrack(newFormat);
263256
if (trackIndex != -1 && !muxerStarted) {
264257
mediaMuxer.start();
@@ -299,7 +292,6 @@ private void drainAudio() {
299292
public void onWebRtcAudioRecordSamplesReady(JavaAudioDeviceModule.AudioSamples audioSamples) {
300293
if (!isRunning)
301294
return;
302-
samplesCount++;
303295
audioThreadHandler.post(() -> {
304296
if (audioEncoder == null) try {
305297
audioEncoder = MediaCodec.createEncoderByType("audio/mp4a-latm");

example/lib/src/get_user_media_sample.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,18 @@ class _GetUserMediaSampleState extends State<GetUserMediaSample> {
9393
_mediaRecorder = MediaRecorder();
9494
setState((){});
9595
await _localStream.getMediaTracks();
96-
final audioTracks = _localStream.getAudioTracks();
97-
final videoTracks = _localStream.getVideoTracks();
96+
final videoTrack = _localStream.getVideoTracks().firstWhere((track) => track.kind == "video");
9897
await _mediaRecorder.start(
9998
path: filePath,
100-
audioTrack: audioTracks.length == 0 ? null : audioTracks.first,
101-
videoTrack: videoTracks.length == 0 ? null : videoTracks.first,
99+
videoTrack: videoTrack,
102100
);
103101
}
104102

105103
_stopRecording() async {
106104
await _mediaRecorder?.stop();
107-
_mediaRecorder = null;
105+
setState((){
106+
_mediaRecorder = 2851 null;
107+
});
108108
}
109109

110110
@override

0 commit comments

Comments
 (0)
0