Skip to content

Commit e5579f1

Browse files
authored
Merge pull request #53 from GetStream/chore/sync_1.2.1
chore: sync with flutter_webrtc 1.2.1
2 parents dcc8f3d + f42914c commit e5579f1

File tree

10 files changed

+116
-20
lines changed

10 files changed

+116
-20
lines changed

android/src/main/java/io/getstream/webrtc/flutter/MethodCallHandlerImpl.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import android.graphics.SurfaceTexture;
99
import android.hardware.Camera;
1010
import android.hardware.Camera.CameraInfo;
11+
import android.media.AudioManager;
1112
import android.media.MediaRecorder;
1213
import android.media.AudioAttributes;
1314
import android.media.AudioDeviceInfo;
@@ -202,7 +203,7 @@ void dispose() {
202203
}
203204

204205
private void initialize(boolean bypassVoiceProcessing, int networkIgnoreMask, boolean forceSWCodec, List<String> forceSWCodecList,
205-
@Nullable ConstraintsMap androidAudioConfiguration, Severity logSeverity) {
206+
@Nullable ConstraintsMap androidAudioConfiguration, Severity logSeverity, @Nullable Integer audioSampleRate, @Nullable Integer audioOutputSampleRate) {
206207
if (mFactory != null) {
207208
return;
208209
}
@@ -261,6 +262,39 @@ private void initialize(boolean bypassVoiceProcessing, int networkIgnoreMask, bo
261262
.setUseHardwareNoiseSuppressor(useHardwareAudioProcessing);
262263
}
263264

265+
// Configure audio sample rates if specified
266+
// This allows high-quality audio playback instead of defaulting to WebRtcAudioManager's queried rate
267+
if (audioSampleRate != null) {
268+
Log.i(TAG, "Setting audio sample rate (both input and output) to: " + audioSampleRate + " Hz");
269+
audioDeviceModuleBuilder.setSampleRate(audioSampleRate);
270+
}
271+
272+
// audioOutputSampleRate takes precedence over audioSampleRate for output
273+
if (audioOutputSampleRate != null) {
274+
Log.i(TAG, "Setting audio output sample rate to: " + audioOutputSampleRate + " Hz");
275+
audioDeviceModuleBuilder.setOutputSampleRate(audioOutputSampleRate);
276+
} else if (bypassVoiceProcessing && audioSampleRate == null && audioOutputSampleRate == null) {
277+
// When bypassVoiceProcessing is enabled, use the device's native optimal sample rate
278+
// This prevents the default behavior which may use a low sample rate based on audio mode
279+
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
280+
if (audioManager != null) {
281+
String nativeSampleRateStr = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
282+
int nativeSampleRate = 48000; // fallback default
283+
if (nativeSampleRateStr != null) {
284+
try {
285+
nativeSampleRate = Integer.parseInt(nativeSampleRateStr);
286+
} catch (NumberFormatException e) {
287+
Log.w(TAG, "Failed to parse native sample rate, using default: " + e.getMessage());
288+
}
289+
}
290+
Log.i(TAG, "bypassVoiceProcessing enabled with no explicit sample rate - using device's native optimal rate: " + nativeSampleRate + " Hz");
291+
audioDeviceModuleBuilder.setOutputSampleRate(nativeSampleRate);
292+
} else {
293+
Log.w(TAG, "AudioManager not available, defaulting to 48000 Hz output");
294+
audioDeviceModuleBuilder.setOutputSampleRate(48000);
295+
}
296+
}
297+
264298
audioDeviceModuleBuilder.setSamplesReadyCallback(recordSamplesReadyCallbackAdapter);
265299
audioDeviceModuleBuilder.setPlaybackSamplesReadyCallback(playbackSamplesReadyCallbackAdapter);
266300

@@ -401,7 +435,19 @@ public void onMethodCall(MethodCall call, @NonNull Result notSafeResult) {
401435
logSeverity = str2LogSeverity(logSeverityStr);
402436
}
403437

404-
initialize(enableBypassVoiceProcessing, networkIgnoreMask, forceSWCodec, forceSWCodecList, androidAudioConfiguration, logSeverity);
438+
Integer audioSampleRate = null;
439+
if (constraintsMap.hasKey("audioSampleRate")
440+
&& constraintsMap.getType("audioSampleRate") == ObjectType.Number) {
441+
audioSampleRate = constraintsMap.getInt("audioSampleRate");
442+
}
443+
444+
Integer audioOutputSampleRate = null;
445+
if (constraintsMap.hasKey("audioOutputSampleRate")
446+
&& constraintsMap.getType("audioOutputSampleRate") == ObjectType.Number) {
447+
audioOutputSampleRate = constraintsMap.getInt("audioOutputSampleRate");
448+
}
449+
450+
initialize(enableBypassVoiceProcessing, networkIgnoreMask, forceSWCodec, forceSWCodecList, androidAudioConfiguration, logSeverity, audioSampleRate, audioOutputSampleRate);
405451
result.success(null);
406452
break;
407453
}

android/src/main/java/io/getstream/webrtc/flutter/record/MediaRecorderImpl.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,6 @@ public void stopRecording(Runnable onStopped) {
8383
audioFileRenderer.release();
8484
audioFileRenderer = null;
8585
}
86-
if (audioFileRenderer != null) {
87-
audioFileRenderer.release();
88-
audioFileRenderer = null;
89-
}
9086
}
9187

9288
private static final String TAG = "MediaRecorderImpl";

android/src/main/java/io/getstream/webrtc/flutter/record/VideoFileRenderer.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,18 @@ private boolean tryConfigureEncoder(EncoderConfig config) {
9292
// Use YUV420 semi-planar size (1.5 bytes per pixel) to reduce memory usage
9393
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, config.width * config.height * 3 / 2);
9494
format.setInteger(MediaFormat.KEY_PRIORITY, 0);
95-
format.setInteger(MediaFormat.KEY_PROFILE, config.profile);
9695

9796
Log.d(TAG, "Trying encoder config: " + config);
9897

9998
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
10099
String codecName = encoder.getName();
101100
Log.d(TAG, "Codec name: " + codecName);
101+
if ("OMX.hisi.video.encoder.avc".equals(codecName)) {
102+
Log.w(TAG, "hisi h264 encoder does not set 'MediaFormat.KEY_PROFILE'.");
103+
//format.setInteger(MediaFormat.KEY_PROFILE, config.profile);
104+
}else{
105+
format.setInteger(MediaFormat.KEY_PROFILE, config.profile);
106+
}
102107

103108
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
104109
// Create input surface *before* starting the encoder

common/cpp/src/flutter_webrtc_base.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ FlutterWebRTCBase::FlutterWebRTCBase(BinaryMessenger* messenger,
1515
: messenger_(messenger), task_runner_(task_runner), textures_(textures) {
1616
LibWebRTC::Initialize();
1717
factory_ = LibWebRTC::CreateRTCPeerConnectionFactory();
18+
factory_->Initialize();
1819
audio_device_ = factory_->GetAudioDevice();
1920
video_device_ = factory_->GetVideoDevice();
2021
desktop_device_ = factory_->GetDesktopDevice();

common/darwin/Classes/FlutterRTCMediaRecorder.m

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,18 @@ - (void)renderFrame:(nullable RTCVideoFrame*)frame {
113113
pixelBufferRef = [FlutterRTCFrameCapturer convertToCVPixelBuffer:frame];
114114
shouldRelease = true;
115115
}
116-
CMVideoFormatDescriptionRef formatDescription;
116+
CMVideoFormatDescriptionRef formatDescription = NULL;
117117
OSStatus status = CMVideoFormatDescriptionCreateForImageBuffer(
118118
kCFAllocatorDefault, pixelBufferRef, &formatDescription);
119119

120+
if (status != noErr || formatDescription == NULL) {
121+
NSLog(@"Failed to create format description: %d", (int)status);
122+
if (shouldRelease) {
123+
CVPixelBufferRelease(pixelBufferRef);
124+
}
125+
return;
126+
}
127+
120128
CMSampleTimingInfo timingInfo;
121129

122130
timingInfo.decodeTimeStamp = kCMTimeInvalid;
@@ -127,19 +135,30 @@ - (void)renderFrame:(nullable RTCVideoFrame*)frame {
127135
timingInfo.presentationTimeStamp = CMTimeMake(frameTime, 1000000);
128136
framesCount++;
129137

130-
CMSampleBufferRef outBuffer;
138+
CMSampleBufferRef outBuffer = NULL;
131139

132140
status = CMSampleBufferCreateReadyWithImageBuffer(kCFAllocatorDefault, pixelBufferRef,
133141
formatDescription, &timingInfo, &outBuffer);
134142

135-
if (![self.writerInput appendSampleBuffer:outBuffer]) {
136-
NSLog(@"Frame not appended %@", self.assetWriter.error);
143+
if (status == noErr && outBuffer != NULL) {
144+
if (![self.writerInput appendSampleBuffer:outBuffer]) {
145+
NSLog(@"Frame not appended %@", self.assetWriter.error);
146+
}
147+
} else {
148+
NSLog(@"Failed to create sample buffer: %d", (int)status);
137149
}
138-
#if TARGET_OS_IPHONE
150+
151+
// Release Core Foundation objects to prevent memory leaks
152+
if (outBuffer != NULL) {
153+
CFRelease(outBuffer);
154+
}
155+
if (formatDescription != NULL) {
156+
CFRelease(formatDescription);
157+
}
158+
139159
if (shouldRelease) {
140160
CVPixelBufferRelease(pixelBufferRef);
141161
}
142-
#endif
143162
}
144163

145164
- (void)stop:(FlutterResult _Nonnull)result {

ios/stream_webrtc_flutter/Sources/stream_webrtc_flutter/FlutterRTCMediaRecorder.m

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,18 @@ - (void)renderFrame:(nullable RTCVideoFrame*)frame {
109109
pixelBufferRef = [FlutterRTCFrameCapturer convertToCVPixelBuffer:frame];
110110
shouldRelease = true;
111111
}
112-
CMVideoFormatDescriptionRef formatDescription;
112+
CMVideoFormatDescriptionRef formatDescription = NULL;
113113
OSStatus status = CMVideoFormatDescriptionCreateForImageBuffer(
114114
kCFAllocatorDefault, pixelBufferRef, &formatDescription);
115115

116+
if (status != noErr || formatDescription == NULL) {
117+
NSLog(@"Failed to create format description: %d", (int)status);
118+
if (shouldRelease) {
119+
CVPixelBufferRelease(pixelBufferRef);
120+
}
121+
return;
122+
}
123+
116124
CMSampleTimingInfo timingInfo;
117125

118126
timingInfo.decodeTimeStamp = kCMTimeInvalid;
@@ -123,19 +131,30 @@ - (void)renderFrame:(nullable RTCVideoFrame*)frame {
123131
timingInfo.presentationTimeStamp = CMTimeMake(frameTime, 1000000);
124132
framesCount++;
125133

126-
CMSampleBufferRef outBuffer;
134+
CMSampleBufferRef outBuffer = NULL;
127135

128136
status = CMSampleBufferCreateReadyWithImageBuffer(kCFAllocatorDefault, pixelBufferRef,
129137
formatDescription, &timingInfo, &outBuffer);
130138

131-
if (![self.writerInput appendSampleBuffer:outBuffer]) {
132-
NSLog(@"Frame not appended %@", self.assetWriter.error);
139+
if (status == noErr && outBuffer != NULL) {
140+
if (![self.writerInput appendSampleBuffer:outBuffer]) {
141+
NSLog(@"Frame not appended %@", self.assetWriter.error);
142+
}
143+
} else {
144+
NSLog(@"Failed to create sample buffer: %d", (int)status);
145+
}
146+
147+
// Release Core Foundation objects to prevent memory leaks
148+
if (outBuffer != NULL) {
149+
CFRelease(outBuffer);
133150
}
134-
#if TARGET_OS_IPHONE
151+
if (formatDescription != NULL) {
152+
CFRelease(formatDescription);
153+
}
154+
135155
if (shouldRelease) {
136156
CVPixelBufferRelease(pixelBufferRef);
137157
}
138-
#endif
139158
}
140159

141160
- (void)stop:(FlutterResult _Nonnull)result {

lib/src/native/utils.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ class WebRTC {
5454
/// "androidAudioConfiguration": an AndroidAudioConfiguration object mapped with toMap()
5555
///
5656
/// "bypassVoiceProcessing": a boolean that bypasses the audio processing for the audio device.
57+
///
58+
/// "audioSampleRate": (Android only) Sets both input and output sample rate in Hz (e.g., 48000).
59+
/// If not specified, uses the native device's default sample rate.
60+
///
61+
/// "audioOutputSampleRate": (Android only) Sets only output sample rate in Hz (e.g., 48000).
62+
/// Takes precedence over audioSampleRate for output.
63+
/// If not specified, uses audioSampleRate or native default.
5764
static Future<void> initialize({Map<String, dynamic>? options}) async {
5865
if (!initialized) {
5966
await _channel.invokeMethod<void>('initialize', <String, dynamic>{

lib/src/web/rtc_video_renderer_impl.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,8 @@ class RTCVideoRenderer extends ValueNotifier<RTCVideoValue>
347347
element.style.pointerEvents = "none";
348348
element.style.opacity = "0";
349349
element.style.position = "absolute";
350+
element.style.left = "0px";
351+
element.style.top = "0px";
350352
}
351353
}
352354

lib/src/web/rtc_video_view_impl.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ class RTCVideoViewState extends State<RTCVideoView> {
172172
RTCVideoViewObjectFit.RTCVideoViewObjectFitCover =>
173173
BoxFit.cover,
174174
},
175+
clipBehavior: Clip.hardEdge,
175176
child: SizedBox(
176177
width: capturedFrame!.width.toDouble(),
177178
height: capturedFrame!.height.toDouble(),

third_party/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
include(ExternalProject)
22

33
set(ZIPFILE "${CMAKE_CURRENT_LIST_DIR}/downloads/libwebrtc.zip")
4-
set(DOWNLOAD_URL "https://github.com/flutter-webrtc/flutter-webrtc/releases/download/v1.1.0/libwebrtc.zip")
4+
set(DOWNLOAD_URL "https://github.com/flutter-webrtc/flutter-webrtc/releases/download/v1.2.1/libwebrtc.zip")
55

66
if(NOT EXISTS "${ZIPFILE}")
77
message(NOTICE "download: ${DOWNLOAD_URL}")

0 commit comments

Comments
 (0)