StreamResource Audio

Hello,
I am trying to implement an AudioCaptcha using https://github.com/logicsquad/nanocaptcha and Vaadin 14.

But it seems that the StreamResource is empty. And in firefox console I can see this:
Media resource https://localhost:8060/VAADIN/dynamic/resource/1/6955c70d-929a-4f7e-b3fc-d2fb020a9865/captcha.wav could not be decoded, error: Error Code: NS_ERROR_DOM_MEDIA_METADATA_ERR (0x806e0006)

It works fine server side when using Clip but I don’t understand why this code isn’t working.

Some of the code:
AudioPlayer.java


import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.server.AbstractStreamResource;

@Tag("audio")
public class AudioPlayer  extends Component {

    public AudioPlayer(){
        getElement().setAttribute("controls",true);
    }

    public void setSource(final AbstractStreamResource resource) {
        getElement().setAttribute("src", resource);
    }
}```

NanoCaptchaImpl.java


var stream = new StreamResource(“captcha.wav”, () → audioCaptcha.getAudio().getAudioInputStream()).setContentType(“audio/wav”);

AudioPlayer player = new AudioPlayer();
player.setSource(stream);
captchaLayout.add(player);


The generated html:
```<audio controls="" src="VAADIN/dynamic/resource/4/185b09cc-afaa-40c2-9b73-982103f9bef5/captcha.wav"></audio>```

What happens if you try to load that wav url manually, do you get the file correctly?

Hey @dependable-ferret,

As @secure-leopard said:
Pls, check the file, make sure you can download it properly (proper size, shall be around 70-90kbyte) and open it with any media player.

If you have:

  • a 0 bytes file then the audiInputStream is not consumable before you send it to your frontend,
  • the other problem could be that your generated audioInputStream from your nanocaptcha library is not correct, and you need to do some more transformation, fixing to be able to play it in the

I hope this makes sense!

I have implemented my own solution, give a look and I hope it can help you ```import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.*;
import java.util.Base64;

@Tag(“audio”)
public class NanoAudioPlayer extends Component {

public NanoAudioPlayer(){
    super();
    getElement().setAttribute("controls",true);
}

public void setSrc(AudioInputStream sourceStream){

    try {
        ByteArrayOutputStream  outputStream =  new ByteArrayOutputStream();
        AudioSystem.write(sourceStream, AudioFileFormat.Type.WAVE,outputStream);

        byte[] data  = outputStream.toByteArray();
        String encodedString  = Base64.getEncoder().encodeToString(data);
        getElement().setProperty("src","data:audio/wav;base64,"+encodedString);

    } catch (IOException e) {
        e.getStackTrace();
    }
}

}
Use @Route(value = “/mystuff”, layout = MainView.class)
@PageTitle(“my stuff”)
public class MyStuff extends VerticalLayout {

private boolean barStatus  = false;


public MyStuff() {

    AudioCaptcha audioCaptcha = new AudioCaptcha.Builder().addContent().build();
    NanoAudioPlayer nanoAudioPlayer = new NanoAudioPlayer();
    nanoAudioPlayer.setSrc(audioCaptcha.getAudio().getAudioInputStream());
    add(nanoAudioPlayer);
}```

image.png

Nice! Are you planning to package your solution as an add-on?

I don’t think so, but if someone do it will be great :wink:

Do you have your sources in some GitHub repo?

If you can donate those and put some acceptable license on them, I can look into packaging that as a jar file and pushing it to Vaadin Add-on Directory

The flow html components package could also hold it :nerd_face:

I was about to say I think we shouldn’t have it in the core (Audio captcha), but we don’t have audio element either anymore :open_mouth: (which I assume you meant @quirky-zebra )

Yes that was exactly my thought :sweat_smile:

We had it in the past for ages, but apparently lost also in the V10 :man_facepalming:

Sure, this is the repository please be free to change anything you want https://github.com/GarnicaJR/VCaptcha

Nice Zico, I have a solution as well with not Base64 encoding but with Vaadin generated dynamic content, I think it can make sense in some cases for optimisation purposes. Otherwise its the same: I use ByteArrayOutputStream and WaveFileWriter (under the hood) as well.

Like this:
(outputStreamAudio is ByteArrayOutputStream)

InputStream inputStreamPerfect = new ByteArrayInputStream(outputStreamAudio.toByteArray());
 var stream = new StreamResource("captcha.wav",
 () -> inputStreamPerfect);
 player.setSource(stream);```
Player is a simpler implementation and only adding to the source as your code does:
```  public void setSource(final AbstractStreamResource resource) {
        getElement().setAttribute("src", resource);
    }```

Cool, I will try your solution in my local,:+1:

let me know how it went, if its 0 byte then the inputstream is closed so you need to reset it, or create another one from the bytearray! :slightly_smiling_face:

Yeah, I did some tests and it worked fine, nice catch Peter
image.png

Thank you Zico, I am glad to hear it!