Skip to content

Commit

Permalink
feat(linux): Add startStream function for linux
Browse files Browse the repository at this point in the history
  • Loading branch information
mauricelos committed Dec 4, 2024
1 parent 57951e2 commit 467f1c1
Showing 1 changed file with 104 additions and 0 deletions.
104 changes: 104 additions & 0 deletions record_linux/lib/record_linux.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class RecordLinux extends RecordPlatform {
RecordState _state = RecordState.stop;
String? _path;
StreamController<RecordState>? _stateStreamCtrl;
Process? _fmediaStreamProcess;

@override
Future<void> create(String recorderId) async {}
Expand Down Expand Up @@ -129,13 +130,79 @@ class RecordLinux extends RecordPlatform {
);
}

@override
Future<Stream<Uint8List>> startStream(
String recorderId, RecordConfig config) async {
final supported = await isEncoderSupported(recorderId, config.encoder);
if (!supported) {
throw Exception('${config.encoder} is not supported.');
}

String numChannels;
if (config.numChannels == 6) {
numChannels = '5.1';
} else if (config.numChannels == 8) {
numChannels = '7.1';
} else if (config.numChannels == 1 || config.numChannels == 2) {
numChannels = config.numChannels.toString();
} else {
throw Exception('${config.numChannels} config is not supported.');
}

final streamController = StreamController<List<int>>();
var fileExtension = '';

switch (config.encoder) {
case AudioEncoder.flac:
fileExtension = '.flac';
break;
case AudioEncoder.opus:
fileExtension = '.opus';
break;
case AudioEncoder.wav:
fileExtension = '.wav';
break;
default:
throw Exception('${config.encoder} is not supported for streaming.');
}

await _callFMediaStream(
[
'--notui',
'--globcmd.pipe-name=$_pipeProcName$recorderId',
'--record',
'--out=@stdout$fileExtension',
'--rate=${config.sampleRate}',
'--channels=$numChannels',
'--globcmd=listen',
'--gain=6.0',
if (config.device != null) '--dev-capture=${config.device!.id}',
..._getEncoderSettings(config.encoder, config.bitRate),
],
recorderId: recorderId,
outStreamCtrl: streamController,
onStarted: () {
_updateState(RecordState.record);
},
).catchError((error) {
print("Error in _callFMediaStream: $error");
streamController.addError(error);
streamController.close();
});

return streamController.stream.map((data) => Uint8List.fromList(data));
}

@override
Future<String?> stop(String recorderId) async {
final path = _path;

await _callFMedia(['--globcmd=stop'], recorderId: recorderId);
await _callFMedia(['--globcmd=quit'], recorderId: recorderId);

await _fmediaStreamProcess?.exitCode;
_fmediaStreamProcess = null;

_updateState(RecordState.stop);

return path;
Expand Down Expand Up @@ -251,6 +318,43 @@ class RecordLinux extends RecordPlatform {
}
}

Future<void> _callFMediaStream(
List<String> arguments, {
required String recorderId,
StreamController<List<int>>? outStreamCtrl,
VoidCallback? onStarted,
bool consumeOutput = true,
}) async {
_fmediaStreamProcess = await Process.start(_fmediaBin, arguments);

if (onStarted != null) {
onStarted();
}

// Listen to both stdout & stderr to not leak system resources.
if (consumeOutput) {
final out = outStreamCtrl ?? StreamController<List<int>>();
if (outStreamCtrl == null) out.stream.listen((event) {});
final err = StreamController<List<int>>();
err.stream.listen((event) {});

_fmediaStreamProcess!.stdout.listen(
(data) {
if (!out.isClosed) {
out.add(data);
}
},
onDone: () async {
if (outStreamCtrl != null && !outStreamCtrl.isClosed) {
outStreamCtrl.close();
} else if (!out.isClosed) {
out.close();
}
},
);
}
}

// Playback/Loopback:
// device #1: FOO (High Definition Audio) - Default
// Default Format: 2 channel, 48000 Hz
Expand Down

0 comments on commit 467f1c1

Please sign in to comment.