Skip to content

Commit

Permalink
feat(*): add better parameters management.
Browse files Browse the repository at this point in the history
Also fix a mismatch between iOS and Android video resolutions
  • Loading branch information
ThibaultBee committed Feb 12, 2024
1 parent 0bbd9b2 commit 0417737
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ fun Map<String, Any>.toAudioConfig(): AudioConfig {

fun String.toResolution(): Size {
return when (this) {
"240p" -> Size(352, 240)
"240p" -> Size(426, 240)
"360p" -> Size(640, 360)
"480p" -> Size(858, 480)
"480p" -> Size(854, 480)
"720p" -> Size(1280, 720)
"1080p" -> Size(1920, 1080)
"2160p" -> Size(4096, 2160)
else -> throw IllegalArgumentException("Unknown resolution: $this")
}
}
Expand Down
23 changes: 1 addition & 22 deletions example/lib/types/resolution.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,6 @@ Map<Resolution, String> getResolutionsMap() {

extension ResolutionExtension on Resolution {
String toPrettyString() {
var result = "";
switch (this) {
case Resolution.RESOLUTION_240:
result = "352x240";
break;
case Resolution.RESOLUTION_360:
result = "640x360";
break;
case Resolution.RESOLUTION_480:
result = "858x480";
break;
case Resolution.RESOLUTION_720:
result = "1280x720";
break;
case Resolution.RESOLUTION_1080:
result = "1920x1080";
break;
default:
result = "1280x720";
break;
}
return result;
return "${this.size.width.toInt()}x${this.size.height.toInt()}";
}
}
14 changes: 1 addition & 13 deletions example/lib/types/sample_rate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,6 @@ Map<SampleRate, String> getSampleRatesMap() {

extension SampleRateExtension on SampleRate {
String toPrettyString() {
var result = "";
switch (this) {
case SampleRate.kHz_11:
result = "11 kHz";
break;
case SampleRate.kHz_22:
result = "22 kHz";
break;
case SampleRate.kHz_44_1:
result = "44.1 kHz";
break;
}
return result;
return "${this.value / 1000} KHz";
}
}
2 changes: 1 addition & 1 deletion ios/Classes/FlutterLiveStreamView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class FlutterLiveStreamView: NSObject {
liveStream.videoConfig
}
set {
sendEvent(["type": "videoSizeChanged", "width": Double(newValue.resolution.size.width), "height": Double(newValue.resolution.size.height)])
sendEvent(["type": "videoSizeChanged", "width": Double(newValue.resolution.width), "height": Double(newValue.resolution.height)])

liveStream.videoConfig = newValue
}
Expand Down
56 changes: 32 additions & 24 deletions ios/Classes/SwiftApiVideoLiveStreamPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ public class SwiftApiVideoLiveStreamPlugin: NSObject, FlutterPlugin {
result(FlutterError(code: "invalid_parameter", message: "Invalid video config", details: nil))
return
}
flutterView.videoConfig = videoParameters.toVideoConfig()
result(nil)
applyVideoConfig(config: videoParameters, flutterView: flutterView, result: result)
case "setAudioConfig":
guard let flutterView = flutterView else {
result(FlutterError(code: "missing_live_stream", message: "Live stream must exist at this point", details: nil))
Expand All @@ -65,8 +64,7 @@ public class SwiftApiVideoLiveStreamPlugin: NSObject, FlutterPlugin {
result(FlutterError(code: "invalid_parameter", message: "Invalid audio config", details: nil))
return
}
flutterView.audioConfig = audioParameters.toAudioConfig()
result(nil)
applyAudioConfig(config: audioParameters, flutterView: flutterView, result: result)
case "startPreview":
guard let flutterView = flutterView else {
result(FlutterError(code: "missing_live_stream", message: "Live stream must exist at this point", details: nil))
Expand Down Expand Up @@ -158,40 +156,50 @@ public class SwiftApiVideoLiveStreamPlugin: NSObject, FlutterPlugin {
result(FlutterError(code: "missing_live_stream", message: "Live stream must exist at this point", details: nil))
return
}
result(["width": flutterView.videoConfig.resolution.size.width, "height": flutterView.videoConfig.resolution.size.height])
result(["width": flutterView.videoConfig.resolution.width, "height": flutterView.videoConfig.resolution.height])
default:
result(FlutterMethodNotImplemented)
}
}

private func applyVideoConfig(config: Dictionary<String, Any>, flutterView: FlutterLiveStreamView, result: @escaping FlutterResult) {
let resolutionString = config["resolution"] as! String?
guard let resolutionString else {
result(FlutterError(code: "missing_parameter", message: "Resolution is missing", details: nil))
return
}
let resolution = resolutionString.toResolution()
guard let resolution else {
result(FlutterError(code: "invalid_parameter", message: "Invalid resolution \(resolutionString)", details: nil))
return
}
flutterView.videoConfig = VideoConfig(bitrate: config["bitrate"] as! Int,
resolution: resolution.rawValue,
fps: config["fps"] as! Float64)
result(nil)
}

private func applyAudioConfig(config: Dictionary<String, Any>, flutterView: FlutterLiveStreamView, result: @escaping FlutterResult) {
flutterView.audioConfig = AudioConfig(bitrate: config["bitrate"] as! Int)
result(nil)
}
}

extension String {
func toResolution() -> Resolution {
func toResolution() -> Resolution? {
switch self {
case "240p":
return Resolution.RESOLUTION_240
return Resolution.RESOLUTION_16_9_240P
case "360p":
return Resolution.RESOLUTION_360
return Resolution.RESOLUTION_16_9_360P
case "480p":
return Resolution.RESOLUTION_480
return Resolution.RESOLUTION_16_9_480P
case "720p":
return Resolution.RESOLUTION_720
return Resolution.RESOLUTION_16_9_720P
case "1080p":
return Resolution.RESOLUTION_1080
return Resolution.RESOLUTION_16_9_1080P
default:
return Resolution.RESOLUTION_720
return nil
}
}
}

extension Dictionary where Key == String {
func toAudioConfig() -> AudioConfig {
return AudioConfig(bitrate: self["bitrate"] as! Int)
}

func toVideoConfig() -> VideoConfig {
return VideoConfig(bitrate: self["bitrate"] as! Int,
resolution: (self["resolution"] as! String).toResolution(),
fps: self["fps"] as! Float64)
}
}
2 changes: 1 addition & 1 deletion ios/apivideo_live_stream.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ A new flutter plugin project.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'Flutter'
s.dependency 'ApiVideoLiveStream', "1.3.6"
s.dependency 'ApiVideoLiveStream', "1.4.0"
s.platform = :ios, '12.0'

# Flutter.framework does not contain a i386 slice.
Expand Down
3 changes: 2 additions & 1 deletion lib/src/types/audio_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class AudioConfig {
this.channel = Channel.stereo,
this.sampleRate = SampleRate.kHz_44_1,
this.enableEchoCanceler = true,
this.enableNoiseSuppressor = true});
this.enableNoiseSuppressor = true})
: assert(bitrate > 0);

/// Creates a [AudioConfig] from a [json] map.
factory AudioConfig.fromJson(Map<String, dynamic> json) =>
Expand Down
48 changes: 13 additions & 35 deletions lib/src/types/resolution.dart
Original file line number Diff line number Diff line change
@@ -1,53 +1,31 @@
import 'dart:ui';

import 'package:json_annotation/json_annotation.dart';

/// Enumeration for camera resolution
/// Only 16/9 resolutions are supported.
enum Resolution {
/// 352x240
/// 426x240
@JsonValue("240p")
RESOLUTION_240,
RESOLUTION_240(size: Size(426, 240)),

/// 640x360
@JsonValue("360p")
RESOLUTION_360,
RESOLUTION_360(size: Size(640, 360)),

/// 858x480
/// 854x480
@JsonValue("480p")
RESOLUTION_480,
RESOLUTION_480(size: Size(854, 480)),

/// 1280x720
@JsonValue("720p")
RESOLUTION_720,
RESOLUTION_720(size: Size(1280, 720)),

/// 1920x1080
@JsonValue("1080p")
RESOLUTION_1080
}
RESOLUTION_1080(size: Size(1920, 1080));

const Resolution({required this.size});

/// Extension of [Resolution]
extension ResolutionExtension on Resolution {
/// Returns the aspect ratio from a [Resolution]
double getAspectRatio() {
var result = 0.0;
switch (this) {
case Resolution.RESOLUTION_240:
result = 352 / 240;
break;
case Resolution.RESOLUTION_360:
result = 640 / 360;
break;
case Resolution.RESOLUTION_480:
result = 858 / 480;
break;
case Resolution.RESOLUTION_720:
result = 1280 / 720;
break;
case Resolution.RESOLUTION_1080:
result = 1920 / 1080;
break;
default:
result = 16 / 9;
break;
}
return result;
}
final Size size;
}
14 changes: 8 additions & 6 deletions lib/src/types/sample_rate.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import 'package:json_annotation/json_annotation.dart';

/// Enumeration for supported RTMP sample rate
@JsonEnum(valueField: 'value')
enum SampleRate {
/// 11025 Hz
@JsonValue(11025)
kHz_11,
kHz_11(value: 11025),

/// 22050 Hz
@JsonValue(22050)
kHz_22,
kHz_22(value: 22050),

/// 44100 Hz
@JsonValue(44100)
kHz_44_1,
kHz_44_1(value: 44100);

const SampleRate({required this.value});

final int value;
}
9 changes: 6 additions & 3 deletions lib/src/types/video_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,22 @@ class VideoConfig {
/// The live streaming video resolution
Resolution resolution;

/// The video framerate in fps
/// The video frame rate in fps
int fps;

/// Creates a [VideoConfig] instance
VideoConfig(
{required this.bitrate,
this.resolution = Resolution.RESOLUTION_720,
this.fps = 30});
this.fps = 30})
: assert(bitrate > 0),
assert(fps > 0);

/// Creates a [VideoConfig] instance where bitrate is set according to the given [resolution].
VideoConfig.withDefaultBitrate(
{this.resolution = Resolution.RESOLUTION_720, this.fps = 30})
: bitrate = _getDefaultBitrate(resolution);
: assert(fps > 0),
bitrate = _getDefaultBitrate(resolution);

/// Creates a [VideoConfig] from a [json] map.
factory VideoConfig.fromJson(Map<String, dynamic> json) =>
Expand Down

0 comments on commit 0417737

Please sign in to comment.