EPNW / opus_flutter

Repository for the federal organized opus_flutter plugin, which is needed to easily obtain a DynamicLibrary of opus for the use in opus_dart on flutter platforms.
https://pub.dev/packages/opus_flutter
9 stars 10 forks source link

OpusDecoder error when using the StreamOpusDecoder #5

Closed jeffmikels closed 3 years ago

jeffmikels commented 3 years ago

Unpredictably, we are getting an OpusException (-1) invalid argument error when using the StreamOpusDecoder.

Here's the relevant code:

Future<void> onAudioReceived(Stream<AudioFrame> voiceData, AudioCodec codec, User speaker, TalkMode talkMode) async {
      Stream<List<int>> pcm;
      try {
        List<int> channelIds;
        double volume = 1;
        pcm = voiceData
            .map((AudioFrame frame) {
              // compute volume from channel volumes encoded in frame's position
              if (firstFrame) {
                channelIds = channelIdsFromPosition(frame.positionalInformation);
                for (int id in channelIds) volume *= channelVolumes.putIfAbsent(id, () => 1.0);
                firstFrame = false;
              }
              return frame.frame;
            })
            .transform(new StreamOpusDecoder.bytes(
              floatOutput: false,
              sampleRate: MUMBLE_SAMPLE_RATE,
              channels: 2,
              forwardErrorCorrection: true,
            ))
            .cast<List<int>>();
        await for (List<int> bytes in pcm) {
          if (audioStream == null) {
            volume = volume * userVolumes.putIfAbsent(speaker.session, () => 1);
            print('Volume set to: $volume');
            audioStream = AudioStream(volume: volume);
          }
          var u8 = Uint8List.fromList(bytes);
          var samples = u8.buffer.asInt16List();
          audioStream.mix(samples);
        }
      } on OpusException catch (e) {
        print(e);
      }
      ...

NOTES:

  1. we are using PositionalInformation to encode extra metadata in our voice packets.
  2. we are also letting the client determine separate volumes for each user and for each channel
  3. AudioStream is the class we are using to create and mix multiple audio streams together

Bottom line, it appears that StreamOpusDecoder is not correctly buffering the packets to wait for full-sized opus packets before attempting to decode.

EP-u-NW commented 3 years ago

The Stream Decoder assumes (according to its documentation) that every frame given to it is a whole frame. But if I remember correctly, mumble only sends whole frames when using opus. So I dont think the problem is with buffring...

You are using 2 channels, could you test if this problem occurs too if you use 1 channel?

Edit: you could also try after the map call: add something like .retainWhere (frame=>frame.isNotEmpty). We once got an error were mumble sendet empty frames.