From 4501e333f0e528e45b21c0395735ce6fe9368ccf Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sun, 20 Aug 2023 12:20:33 -0700 Subject: [PATCH] fucking works can transfer files from desktop to mobile and save them there. --- examples/messages.rs | 37 +++++++- mobile/lib/bridge_definitions.dart | 14 ++- mobile/lib/bridge_generated.dart | 97 ++++++++++++++----- mobile/lib/ffi.dart | 1 - mobile/lib/main.dart | 116 +++++++++++------------ mobile/native/src/api.rs | 31 ++++-- mobile/native/src/bridge_generated.io.rs | 60 +++++++++++- mobile/native/src/bridge_generated.rs | 13 +-- src/lib.rs | 45 ++++----- src/qr_utils.rs | 2 +- 10 files changed, 290 insertions(+), 126 deletions(-) diff --git a/examples/messages.rs b/examples/messages.rs index b63fa40..85aa018 100644 --- a/examples/messages.rs +++ b/examples/messages.rs @@ -1,3 +1,38 @@ +use rand::{seq::SliceRandom, Rng}; +use raptorq::{Decoder, Encoder, EncodingPacket, ObjectTransmissionInformation}; + fn main() { - // + let rng = &mut rand::thread_rng(); + let len = 20_000; + let mut v = Vec::with_capacity(len); + + for _ in 0..len { + v.push(rng.gen::()); + } + let config = ObjectTransmissionInformation::with_defaults(len as u64, 1200); + let encoder = Encoder::new(&v, config); + let mut packets = encoder + .get_encoded_packets(10) + .iter() + .map(|p| p.serialize()) + .collect::>(); + + packets.shuffle(rng); + + let mut decoder = Decoder::new(config); + + let mut v2 = None; + for (i, p) in packets.iter().enumerate() { + v2 = decoder.decode(EncodingPacket::deserialize(p)); + if v2.is_some() { + println!( + "recovered after {i} packets received, out of {} total", + packets.len() + ); + break; + } + } + + assert!(v2.is_some()); + assert_eq!(v2.unwrap(), v); } diff --git a/mobile/lib/bridge_definitions.dart b/mobile/lib/bridge_definitions.dart index 6c2789a..d762a39 100644 --- a/mobile/lib/bridge_definitions.dart +++ b/mobile/lib/bridge_definitions.dart @@ -13,19 +13,29 @@ abstract class Native { FlutterRustBridgeTaskConstMeta get kGetTxConfigConstMeta; - Future checkRaptor({required Uint8List buffer, required TxConfig txconf, dynamic hint}); + Future decodePackets({required List packets, required TxConfig txconf, dynamic hint}); - FlutterRustBridgeTaskConstMeta get kCheckRaptorConstMeta; + FlutterRustBridgeTaskConstMeta get kDecodePacketsConstMeta; +} + +class RaptorPacket { + final Uint8List field0; + + const RaptorPacket({ + required this.field0, + }); } class TxConfig { final int len; final int mtu; final String description; + final String? filename; const TxConfig({ required this.len, required this.mtu, required this.description, + this.filename, }); } diff --git a/mobile/lib/bridge_generated.dart b/mobile/lib/bridge_generated.dart index 140944b..9ab4f8b 100644 --- a/mobile/lib/bridge_generated.dart +++ b/mobile/lib/bridge_generated.dart @@ -40,21 +40,21 @@ class NativeImpl implements Native { argNames: ["bytes"], ); - Future checkRaptor({required Uint8List buffer, required TxConfig txconf, dynamic hint}) { - var arg0 = _platform.api2wire_uint_8_list(buffer); + Future decodePackets({required List packets, required TxConfig txconf, dynamic hint}) { + var arg0 = _platform.api2wire_list_raptor_packet(packets); var arg1 = _platform.api2wire_box_autoadd_tx_config(txconf); return _platform.executeNormal(FlutterRustBridgeTask( - callFfi: (port_) => _platform.inner.wire_check_raptor(port_, arg0, arg1), - parseSuccessData: _wire2api_bool, - constMeta: kCheckRaptorConstMeta, - argValues: [buffer, txconf], + callFfi: (port_) => _platform.inner.wire_decode_packets(port_, arg0, arg1), + parseSuccessData: _wire2api_opt_uint_8_list, + constMeta: kDecodePacketsConstMeta, + argValues: [packets, txconf], hint: hint, )); } - FlutterRustBridgeTaskConstMeta get kCheckRaptorConstMeta => const FlutterRustBridgeTaskConstMeta( - debugName: "check_raptor", - argNames: ["buffer", "txconf"], + FlutterRustBridgeTaskConstMeta get kDecodePacketsConstMeta => const FlutterRustBridgeTaskConstMeta( + debugName: "decode_packets", + argNames: ["packets", "txconf"], ); void dispose() { @@ -66,25 +66,30 @@ class NativeImpl implements Native { return raw as String; } - bool _wire2api_bool(dynamic raw) { - return raw as bool; - } - TxConfig _wire2api_box_autoadd_tx_config(dynamic raw) { return _wire2api_tx_config(raw); } + String? _wire2api_opt_String(dynamic raw) { + return raw == null ? null : _wire2api_String(raw); + } + TxConfig? _wire2api_opt_box_autoadd_tx_config(dynamic raw) { return raw == null ? null : _wire2api_box_autoadd_tx_config(raw); } + Uint8List? _wire2api_opt_uint_8_list(dynamic raw) { + return raw == null ? null : _wire2api_uint_8_list(raw); + } + TxConfig _wire2api_tx_config(dynamic raw) { final arr = raw as List; - if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + if (arr.length != 4) throw Exception('unexpected arr length: expect 4 but see ${arr.length}'); return TxConfig( len: _wire2api_u64(arr[0]), mtu: _wire2api_u16(arr[1]), description: _wire2api_String(arr[2]), + filename: _wire2api_opt_String(arr[3]), ); } @@ -136,6 +141,20 @@ class NativePlatform extends FlutterRustBridgeBase { return ptr; } + @protected + ffi.Pointer api2wire_list_raptor_packet(List raw) { + final ans = inner.new_list_raptor_packet_0(raw.length); + for (var i = 0; i < raw.length; ++i) { + _api_fill_to_wire_raptor_packet(raw[i], ans.ref.ptr[i]); + } + return ans; + } + + @protected + ffi.Pointer api2wire_opt_String(String? raw) { + return raw == null ? ffi.nullptr : api2wire_String(raw); + } + @protected int api2wire_u64(int raw) { return raw; @@ -155,10 +174,15 @@ class NativePlatform extends FlutterRustBridgeBase { _api_fill_to_wire_tx_config(apiObj, wireObj.ref); } + void _api_fill_to_wire_raptor_packet(RaptorPacket apiObj, wire_RaptorPacket wireObj) { + wireObj.field0 = api2wire_uint_8_list(apiObj.field0); + } + void _api_fill_to_wire_tx_config(TxConfig apiObj, wire_TxConfig wireObj) { wireObj.len = api2wire_u64(apiObj.len); wireObj.mtu = api2wire_u16(apiObj.mtu); wireObj.description = api2wire_String(apiObj.description); + wireObj.filename = api2wire_opt_String(apiObj.filename); } } @@ -255,23 +279,24 @@ class NativeWire implements FlutterRustBridgeWireBase { late final _wire_get_tx_config = _wire_get_tx_configPtr.asFunction)>(); - void wire_check_raptor( + void wire_decode_packets( int port_, - ffi.Pointer buffer, + ffi.Pointer packets, ffi.Pointer txconf, ) { - return _wire_check_raptor( + return _wire_decode_packets( port_, - buffer, + packets, txconf, ); } - late final _wire_check_raptorPtr = _lookup< - ffi.NativeFunction, ffi.Pointer)>>( - 'wire_check_raptor'); - late final _wire_check_raptor = - _wire_check_raptorPtr.asFunction, ffi.Pointer)>(); + late final _wire_decode_packetsPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, ffi.Pointer, ffi.Pointer)>>('wire_decode_packets'); + late final _wire_decode_packets = _wire_decode_packetsPtr + .asFunction, ffi.Pointer)>(); ffi.Pointer new_box_autoadd_tx_config_0() { return _new_box_autoadd_tx_config_0(); @@ -282,6 +307,19 @@ class NativeWire implements FlutterRustBridgeWireBase { late final _new_box_autoadd_tx_config_0 = _new_box_autoadd_tx_config_0Ptr.asFunction Function()>(); + ffi.Pointer new_list_raptor_packet_0( + int len, + ) { + return _new_list_raptor_packet_0( + len, + ); + } + + late final _new_list_raptor_packet_0Ptr = + _lookup Function(ffi.Int32)>>('new_list_raptor_packet_0'); + late final _new_list_raptor_packet_0 = + _new_list_raptor_packet_0Ptr.asFunction Function(int)>(); + ffi.Pointer new_uint_8_list_0( int len, ) { @@ -316,6 +354,17 @@ final class wire_uint_8_list extends ffi.Struct { external int len; } +final class wire_RaptorPacket extends ffi.Struct { + external ffi.Pointer field0; +} + +final class wire_list_raptor_packet extends ffi.Struct { + external ffi.Pointer ptr; + + @ffi.Int32() + external int len; +} + final class wire_TxConfig extends ffi.Struct { @ffi.Uint64() external int len; @@ -324,6 +373,8 @@ final class wire_TxConfig extends ffi.Struct { external int mtu; external ffi.Pointer description; + + external ffi.Pointer filename; } typedef DartPostCObjectFnType diff --git a/mobile/lib/ffi.dart b/mobile/lib/ffi.dart index 5e469a7..93b66c6 100644 --- a/mobile/lib/ffi.dart +++ b/mobile/lib/ffi.dart @@ -9,7 +9,6 @@ export 'bridge_definitions.dart'; // Re-export the bridge so it is only necessary to import this file. export 'bridge_generated.dart'; -import 'dart:io' as io; const _base = 'native'; diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index b3aad83..4bcb396 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'dart:ffi'; +import 'dart:io'; import 'dart:typed_data'; import 'package:camerawesome/camerawesome_plugin.dart'; @@ -25,7 +25,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'camerAwesome App', + title: 'Cuttle', theme: ThemeData( primarySwatch: Colors.blue, ), @@ -43,20 +43,18 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { final _barcodeScanner = BarcodeScanner(formats: [BarcodeFormat.qrCode]); - - final _buffer = []; - final _barcodesController = BehaviorSubject>(); - late final Stream> _barcodesStream = _barcodesController.stream; + final _rxTextController = BehaviorSubject(); + late final Stream _rxTextStream = _rxTextController.stream; final _scrollController = ScrollController(); TxConfig? _txConfig; - String? _rxText; var _cuttleState = CuttleState.unitialized; - final _rxData = []; + final List _rxData = []; + String _rxText = ''; @override void dispose() { - _barcodesController.close(); + _rxTextController.close(); super.dispose(); } @@ -69,12 +67,12 @@ class _MyHomePageState extends State { androidOptions: const AndroidAnalysisOptions.nv21( width: 1024, ), - maxFramesPerSecond: null, + maxFramesPerSecond: 30, autoStart: false, ), builder: (cameraModeState, previewSize, previewRect) { - return _BarcodeDisplayWidget( - barcodesStream: _barcodesStream, + return _RxTextDisplayWidget( + rxTextStream: _rxTextStream, scrollController: _scrollController, analysisController: cameraModeState.analysisController!, ); @@ -93,7 +91,7 @@ class _MyHomePageState extends State { if (bytes == null) { continue; } - final dbytes = bytes; + final Uint8List dbytes = bytes; switch (_cuttleState) { case CuttleState.unitialized: { @@ -101,8 +99,13 @@ class _MyHomePageState extends State { if (txconf != null) { _txConfig = txconf; _cuttleState = CuttleState.receiving; - debugPrint( - "txconf: ${txconf.len} bytes, \"${txconf.description}\""); + final fname = + _txConfig!.filename ?? "large text on the command line"; + + final desc = _txConfig!.description; + final text = 'Receiving $fname, ${_txConfig!.len} bytes: $desc'; + _rxText = text; + _rxTextController.add(text); continue; } // implicit else here; txconf was null @@ -110,69 +113,73 @@ class _MyHomePageState extends State { if (text != null) { // it's not a txconfig, and it's not a raptor packet, so it must be a regular qr code _rxText = text; + _rxTextController.add(text); _cuttleState = CuttleState.received; } } case CuttleState.receiving: { - final check = await api.checkRaptor( - buffer: Uint8List.fromList(_rxData), txconf: _txConfig!); - if (check) { - _cuttleState = CuttleState.received; + var txconf = await api.getTxConfig(bytes: dbytes); + if (txconf != null || barcode.rawValue != null) { + continue; } + final packet = RaptorPacket(field0: dbytes); + _rxData.add(packet); + final content = + await api.decodePackets(packets: _rxData, txconf: _txConfig!); + if (content != null) { + _rxData.clear(); + _barcodeScanner.close(); + _cuttleState = CuttleState.received; + _rxTextController.add("DONE RECEIVING $_rxText"); + final f = await _saveReceivedFile(_txConfig!.filename, content); + _rxTextController.add("Saved content to $f"); + continue; + } + _rxTextController + .add("$_rxText -- ${_rxData.length} bytes so far"); } case CuttleState.received: continue; } - - _addBarcode("[${barcode.format.name}]: ${barcode.rawValue}"); } } catch (error) { - debugPrint("...sending image resulted error $error"); + debugPrint("sending image resulted error $error"); } } - void _addBarcode(String value) { - try { - if (_buffer.length > 300) { - _buffer.removeRange(_buffer.length - 300, _buffer.length); - } - if (_buffer.isEmpty || value != _buffer[0]) { - _buffer.insert(0, value); - _barcodesController.add(_buffer); - _scrollController.animateTo( - 0, - duration: const Duration(milliseconds: 400), - curve: Curves.fastLinearToSlowEaseIn, - ); - } - } catch (err) { - debugPrint("...logging error $err"); - } + Future _saveReceivedFile(String? filename, Uint8List bytes) async { + final Directory downloadDir = Directory('/storage/emulated/0/Download'); + final String fname = + filename ?? "cuttle_${DateTime.now().millisecondsSinceEpoch}.txt"; + final String path = "${downloadDir.path}/$fname"; + final file = await File(path).create(); + await file.writeAsBytes(bytes, flush: true); + return path; } } -class _BarcodeDisplayWidget extends StatefulWidget { - final Stream> barcodesStream; +class _RxTextDisplayWidget extends StatefulWidget { + final Stream rxTextStream; final ScrollController scrollController; final AnalysisController analysisController; - const _BarcodeDisplayWidget({ + const _RxTextDisplayWidget({ // ignore: unused_element super.key, - required this.barcodesStream, + required this.rxTextStream, required this.scrollController, required this.analysisController, }); @override - State<_BarcodeDisplayWidget> createState() => _BarcodeDisplayWidgetState(); + State<_RxTextDisplayWidget> createState() => _RxTextDisplayWidgetState(); } -class _BarcodeDisplayWidgetState extends State<_BarcodeDisplayWidget> { +class _RxTextDisplayWidgetState extends State<_RxTextDisplayWidget> { @override Widget build(BuildContext context) { return Align( @@ -203,19 +210,12 @@ class _BarcodeDisplayWidgetState extends State<_BarcodeDisplayWidget> { Container( height: 120, padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamBuilder>( - stream: widget.barcodesStream, - builder: (context, value) => !value.hasData - ? const SizedBox.expand() - : ListView.separated( - padding: const EdgeInsets.only(top: 8), - controller: widget.scrollController, - itemCount: value.data!.length, - separatorBuilder: (context, index) => - const SizedBox(height: 4), - itemBuilder: (context, index) => Text(value.data![index]), - ), - ), + child: SelectionArea( + child: StreamBuilder( + stream: widget.rxTextStream, + builder: (context, value) => + !value.hasData ? const SizedBox.expand() : Text(value.data!), + )), ), ]), ), diff --git a/mobile/native/src/api.rs b/mobile/native/src/api.rs index 7075e68..08484ce 100644 --- a/mobile/native/src/api.rs +++ b/mobile/native/src/api.rs @@ -1,33 +1,46 @@ -use rkyv::Deserialize; +use raptorq::{Decoder, EncodingPacket, ObjectTransmissionInformation}; #[derive(Debug, Clone)] pub struct TxConfig { pub len: u64, pub mtu: u16, pub description: String, + pub filename: Option, } +#[derive(Debug, Clone)] +pub struct RaptorPacket(pub Vec); + impl From for TxConfig { fn from(value: cuttle::TxConfig) -> Self { TxConfig { len: value.len, mtu: value.mtu, description: value.description, + filename: value.filename, } } } pub fn get_tx_config(bytes: Vec) -> Option { if let Ok(archive) = rkyv::check_archived_root::(&bytes) { - if let Ok::(conf) = archive.deserialize(&mut rkyv::Infallible) { - return Some(conf.into()); - } else { - return None; - } + >::deserialize(archive, &mut rkyv::Infallible) + .ok().map(Into::into) + } else { + None } - None } -pub fn check_raptor(buffer: Vec, txconf: TxConfig) -> bool { - false +pub fn decode_packets(packets: Vec, txconf: TxConfig) -> Option> { + let conf = ObjectTransmissionInformation::with_defaults(txconf.len, txconf.mtu); + let mut decoder = Decoder::new(conf); + + let mut res = None; + for RaptorPacket(p) in &packets { + res = decoder.decode(EncodingPacket::deserialize(p)); + if res.is_some() { + break; + } + } + res } diff --git a/mobile/native/src/bridge_generated.io.rs b/mobile/native/src/bridge_generated.io.rs index 0870c5d..94e19c9 100644 --- a/mobile/native/src/bridge_generated.io.rs +++ b/mobile/native/src/bridge_generated.io.rs @@ -7,12 +7,12 @@ pub extern "C" fn wire_get_tx_config(port_: i64, bytes: *mut wire_uint_8_list) { } #[no_mangle] -pub extern "C" fn wire_check_raptor( +pub extern "C" fn wire_decode_packets( port_: i64, - buffer: *mut wire_uint_8_list, + packets: *mut wire_list_raptor_packet, txconf: *mut wire_TxConfig, ) { - wire_check_raptor_impl(port_, buffer, txconf) + wire_decode_packets_impl(port_, packets, txconf) } // Section: allocate functions @@ -22,6 +22,15 @@ pub extern "C" fn new_box_autoadd_tx_config_0() -> *mut wire_TxConfig { support::new_leak_box_ptr(wire_TxConfig::new_with_null_ptr()) } +#[no_mangle] +pub extern "C" fn new_list_raptor_packet_0(len: i32) -> *mut wire_list_raptor_packet { + let wrap = wire_list_raptor_packet { + ptr: support::new_leak_vec_ptr(::new_with_null_ptr(), len), + len, + }; + support::new_leak_box_ptr(wrap) +} + #[no_mangle] pub extern "C" fn new_uint_8_list_0(len: i32) -> *mut wire_uint_8_list { let ans = wire_uint_8_list { @@ -47,12 +56,28 @@ impl Wire2Api for *mut wire_TxConfig { Wire2Api::::wire2api(*wrap).into() } } +impl Wire2Api> for *mut wire_list_raptor_packet { + fn wire2api(self) -> Vec { + let vec = unsafe { + let wrap = support::box_from_leak_ptr(self); + support::vec_from_leak_ptr(wrap.ptr, wrap.len) + }; + vec.into_iter().map(Wire2Api::wire2api).collect() + } +} + +impl Wire2Api for wire_RaptorPacket { + fn wire2api(self) -> RaptorPacket { + RaptorPacket(self.field0.wire2api()) + } +} impl Wire2Api for wire_TxConfig { fn wire2api(self) -> TxConfig { TxConfig { len: self.len.wire2api(), mtu: self.mtu.wire2api(), description: self.description.wire2api(), + filename: self.filename.wire2api(), } } } @@ -67,12 +92,26 @@ impl Wire2Api> for *mut wire_uint_8_list { } // Section: wire structs +#[repr(C)] +#[derive(Clone)] +pub struct wire_list_raptor_packet { + ptr: *mut wire_RaptorPacket, + len: i32, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wire_RaptorPacket { + field0: *mut wire_uint_8_list, +} + #[repr(C)] #[derive(Clone)] pub struct wire_TxConfig { len: u64, mtu: u16, description: *mut wire_uint_8_list, + filename: *mut wire_uint_8_list, } #[repr(C)] @@ -94,12 +133,27 @@ impl NewWithNullPtr for *mut T { } } +impl NewWithNullPtr for wire_RaptorPacket { + fn new_with_null_ptr() -> Self { + Self { + field0: core::ptr::null_mut(), + } + } +} + +impl Default for wire_RaptorPacket { + fn default() -> Self { + Self::new_with_null_ptr() + } +} + impl NewWithNullPtr for wire_TxConfig { fn new_with_null_ptr() -> Self { Self { len: Default::default(), mtu: Default::default(), description: core::ptr::null_mut(), + filename: core::ptr::null_mut(), } } } diff --git a/mobile/native/src/bridge_generated.rs b/mobile/native/src/bridge_generated.rs index 6c63a24..46fc436 100644 --- a/mobile/native/src/bridge_generated.rs +++ b/mobile/native/src/bridge_generated.rs @@ -35,21 +35,21 @@ fn wire_get_tx_config_impl(port_: MessagePort, bytes: impl Wire2Api> + U }, ) } -fn wire_check_raptor_impl( +fn wire_decode_packets_impl( port_: MessagePort, - buffer: impl Wire2Api> + UnwindSafe, + packets: impl Wire2Api> + UnwindSafe, txconf: impl Wire2Api + UnwindSafe, ) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, bool>( + FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, Option>>( WrapInfo { - debug_name: "check_raptor", + debug_name: "decode_packets", port: Some(port_), mode: FfiCallMode::Normal, }, move || { - let api_buffer = buffer.wire2api(); + let api_packets = packets.wire2api(); let api_txconf = txconf.wire2api(); - move |task_callback| Ok(check_raptor(api_buffer, api_txconf)) + move |task_callback| Ok(decode_packets(api_packets, api_txconf)) }, ) } @@ -100,6 +100,7 @@ impl support::IntoDart for TxConfig { self.len.into_into_dart().into_dart(), self.mtu.into_into_dart().into_dart(), self.description.into_into_dart().into_dart(), + self.filename.into_dart(), ] .into_dart() } diff --git a/src/lib.rs b/src/lib.rs index ce1be62..048448c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ pub use qr_utils::{get_content, mk_qr_bytes}; pub type CuttleSender = std::sync::mpsc::SyncSender>; pub type CuttleReceiver = std::sync::mpsc::Receiver>; -pub const STREAMING_MTU: u16 = 2326; +pub const STREAMING_MTU: u16 = 1200; /// The application state #[derive(Debug)] @@ -88,32 +88,33 @@ pub fn stream_bytes( filename: Option<&str>, ) -> Vec { let len = bytes.len() as u64; + + let rng = &mut rand::thread_rng(); + + let config = ObjectTransmissionInformation::with_defaults(len, STREAMING_MTU); + let encoder = Encoder::new(&bytes, config); + + let mut packets = encoder + .get_encoded_packets(10) + .iter() + .map(|p| p.serialize()) + .collect::>(); + + packets.shuffle(rng); + + std::thread::spawn(move || { + for packet in packets.iter().cycle() { + tx.send(packet.clone()).unwrap_or_default(); + } + }); + let txconfig = TxConfig { len, mtu: STREAMING_MTU, description: desc.to_string(), filename: filename.map(|f| f.to_string()), }; - let txconfig = rkyv::to_bytes::<_, 256>(&txconfig) + rkyv::to_bytes::<_, 256>(&txconfig) .expect("tried to serialize the txconfig") - .to_vec(); - - std::thread::spawn(move || { - let rng = &mut rand::thread_rng(); - - let config = ObjectTransmissionInformation::with_defaults(len, STREAMING_MTU); - let encoder = Encoder::new(&bytes, config); - let mut packets = encoder - .get_encoded_packets(10) - .iter() - .map(|p| p.serialize()) - .collect::>(); - - packets.shuffle(rng); - - for packet in packets.iter().cycle() { - tx.send(packet.clone()).unwrap_or_default(); - } - }); - txconfig + .to_vec() } diff --git a/src/qr_utils.rs b/src/qr_utils.rs index 1b087b7..0ad1511 100644 --- a/src/qr_utils.rs +++ b/src/qr_utils.rs @@ -5,7 +5,7 @@ use crate::{stream_bytes, Content, StreamedContent}; /// Makes a PNG of a QR code for the given bytes, returns the bytes of the PNG. pub fn mk_qr_bytes(bytes: &[u8], height: f32) -> Vec { let qr = fast_qr::QRBuilder::new(bytes) - .ecl(fast_qr::ECL::M) + .ecl(fast_qr::ECL::L) .build() .unwrap();