0

I have a problem to be able to read audio using JavaCPP FFMpeg library. I don’t know how to pass it to java sound and I don’t know too if my code is correct.

Let’s see the more important part of my code (video is OK so I drop this) :

The variables :

//==========================================================================
// FFMpeg 4.x - Video and Audio
//==========================================================================

private final AVFormatContext   pFormatCtx = new AVFormatContext(null);
private final AVDictionary      OPTIONS_DICT = null;
private AVPacket                pPacket = new AVPacket();
    
//==========================================================================
// FFMpeg 4.x - Audio
//==========================================================================
    
private AVCodec                 pAudioCodec;
private AVCodecContext          pAudioCodecCtx;
private final List<StreamInfo>  audioStreams = new ArrayList<>();
private int                     audio_data_size;
private final BytePointer       audio_data = new BytePointer(0);
private int                     audio_ret;
private AVFrame                 pAudioDecodedFrame = null;
private AVCodecParserContext    pAudioParser;
private SwrContext              audio_swr_ctx = null;

Then I call prepare functions in this order :

private void prepareFirst() throws Exception{
    oldFile = file;
            
    // Initialize packet and check for error
    pPacket = av_packet_alloc();
    if(pPacket == null){
        throw new Exception("ALL: Couldn't allocate packet");
    }

    // Open video file
    if (avformat_open_input(pFormatCtx, file.getPath(), null, null) != 0) {
        throw new Exception("ALL: Couldn't open file");
    }

    // Retrieve stream information
    if (avformat_find_stream_info(pFormatCtx, (PointerPointer)null) < 0) {
        throw new Exception("ALL: Couldn't find stream information");
    }

    // Dump information about file onto standard error
    av_dump_format(pFormatCtx, 0, file.getPath(), 0);

    // Find the first audio/video stream
    for (int i = 0; i < pFormatCtx.nb_streams(); i++) {
        switch(pFormatCtx.streams(i).codecpar().codec_type()){
            case AVMEDIA_TYPE_VIDEO -> videoStreams.add(new StreamInfo(i, pFormatCtx.streams(i)));
            case AVMEDIA_TYPE_AUDIO -> audioStreams.add(new StreamInfo(i, pFormatCtx.streams(i)));
        }
    }
    
    if(videoStreams.isEmpty() && type != PlayType.AudioOnly){
        throw new Exception("Didn't find an audio stream");
    }
    if(audioStreams.isEmpty() && type != PlayType.VideoOnly){
        throw new Exception("Didn't find a video stream");
    }
}

private void prepareAudio() throws Exception{
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // AUDIO
    //------------------------------------------------------------------

    if(audioStreams.isEmpty() == false){
        //===========================
        //------------
        
//                // Let's search for AVCodec
//                pAudioCodec = avcodec_find_decoder(pFormatCtx.streams(audioStreams.get(0).getStreamIndex()).codecpar().codec_id());
//                if (pAudioCodec == null) {
//                    throw new Exception("AUDIO: Unsupported codec or not found!");
//                }
//
//                // Let's alloc AVCodecContext
//                pAudioCodecCtx = avcodec_alloc_context3(pAudioCodec);
//                if (pAudioCodecCtx == null) {            
//                    throw new Exception("AUDIO: Unallocated codec context or not found!");
//                }
        
        // Get a pointer to the codec context for the video stream
        pAudioCodecCtx = pFormatCtx.streams(audioStreams.get(0).getStreamIndex()).codec();

        // Find the decoder for the video stream
        pAudioCodec = avcodec_find_decoder(pAudioCodecCtx.codec_id());
        if (pAudioCodec == null) {
            throw new Exception("AUDIO: Unsupported codec or not found!");
        }

        //===========================
        //------------

        /* open it */
        if (avcodec_open2(pAudioCodecCtx, pAudioCodec, OPTIONS_DICT) < 0) {
            throw new Exception("AUDIO: Could not open codec");
        }

        pAudioDecodedFrame = av_frame_alloc();
        if (pAudioDecodedFrame == null){
            throw new Exception("AUDIO: DecodedFrame allocation failed");
        }

        audio_swr_ctx = swr_alloc_set_opts(
                null,                           // existing Swr context or NULL
                AV_CH_LAYOUT_STEREO,            // output channel layout (AV_CH_LAYOUT_*)
                AV_SAMPLE_FMT_S16,              // output sample format (AV_SAMPLE_FMT_*).
                44100,                          // output sample rate (frequency in Hz)
                pAudioCodecCtx.channels(),  // input channel layout (AV_CH_LAYOUT_*)
                pAudioCodecCtx.sample_fmt(),    // input sample format (AV_SAMPLE_FMT_*).
                pAudioCodecCtx.sample_rate(),   // input sample rate (frequency in Hz)
                0,                              // logging level offset
                null                            // parent logging context, can be NULL
        );
        
        swr_init(audio_swr_ctx);
        
        av_samples_fill_arrays(
                pAudioDecodedFrame.data(),      // audio_data,
                pAudioDecodedFrame.linesize(),  // linesize
                audio_data,                     // buf
                (int)AV_CH_LAYOUT_STEREO,       // nb_channels
                44100,                          // nb_samples
                AV_SAMPLE_FMT_S16,              // sample_fmt
                0                               // align
        );
        
    }
    
    // Audio treatment end ---------------------------------------------
    //==================================================================
}

And then when I launch the thread :

private void doPlay() throws Exception{
    av_init_packet(pPacket);

    // Read frames
    while (av_read_frame(pFormatCtx, pPacket) >= 0) {
        if (type != PlayType.AudioOnly && pPacket.stream_index() == videoStreams.get(0).getStreamIndex()) {
            // Is this a packet from the video stream?
            decodeVideo();
            renewPacket();
        }

        if (type != PlayType.VideoOnly && pPacket.stream_index() == audioStreams.get(0).getStreamIndex()) {
            // Is this a packet from the audio stream?
            if(pPacket.size() > 0){
                decodeAudio();
                renewPacket();
            }
        }
    }
}

private void renewPacket(){
    // Free the packet that was allocated by av_read_frame
    av_packet_unref(pPacket);

    pPacket.data(null);
    pPacket.size(0);
    av_init_packet(pPacket);
}

And again, this is where I don’t read audio :

private void decodeAudio() throws Exception{

    do {
        audio_ret = avcodec_send_packet(pAudioCodecCtx, pPacket);
    } while(audio_ret == AVERROR_EAGAIN());
    System.out.println("packet sent return value: " + audio_ret);

    if(audio_ret == AVERROR_EOF || audio_ret == AVERROR_EINVAL()) {
        StringBuilder sb = new StringBuilder();
        Formatter formatter = new Formatter(sb, Locale.US);
        formatter.format("AVERROR(EAGAIN): %d, AVERROR_EOF: %d, AVERROR(EINVAL): %d\n", AVERROR_EAGAIN(), AVERROR_EOF, AVERROR_EINVAL());
        formatter.format("Audio frame getting error (%d)!\n", audio_ret);
        throw new Exception(sb.toString());
    }

    audio_ret = avcodec_receive_frame(pAudioCodecCtx, pAudioDecodedFrame);
    System.out.println("frame received return value: " + audio_ret);

    audio_data_size = av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);

    if (audio_data_size < 0) {
        /* This should not occur, checking just for paranoia */
        throw new Exception("Failed to calculate data size");
    }
    
    double frame_nb = 44100d / pAudioCodecCtx.sample_rate() * pAudioDecodedFrame.nb_samples();
    long out_count = Math.round(Math.floor(frame_nb));

    int out_samples = swr_convert(
            audio_swr_ctx,
            audio_data, 
            (int)out_count,
            pAudioDecodedFrame.data(0),
            pAudioDecodedFrame.nb_samples()
    );
    
    if (out_samples < 0) {
        throw new Exception("AUDIO: Error while converting");
    }
    
    int dst_bufsize = av_samples_get_buffer_size(
        pAudioDecodedFrame.linesize(), 
        (int)AV_CH_LAYOUT_STEREO,  
        out_samples,
        AV_SAMPLE_FMT_S16,
        1
    );
    
    AudioFormat audioFormat = new AudioFormat(
            pAudioDecodedFrame.sample_rate(),
            16,
            2, 
            true, 
            false
    );
    
    BytePointer bytePointer = pAudioDecodedFrame.data(0);
    ByteBuffer byteBuffer = bytePointer.asBuffer();

    byte[] bytes = new byte[byteBuffer.remaining()];
    byteBuffer.get(bytes);
    
    try (SourceDataLine sdl = AudioSystem.getSourceDataLine(audioFormat)) {
        sdl.open(audioFormat);                
        sdl.start();
        sdl.write(bytes, 0, bytes.length);
        sdl.drain();
        sdl.stop();
    } catch (LineUnavailableException ex) {
        Logger.getLogger(AVEntry.class.getName()).log(Level.SEVERE, null, ex);
    }    
}

Do you have an idea ?

greg-449
  • 109,219
  • 232
  • 102
  • 145
TW2
  • 33
  • 8
  • Try this: https://github.com/bytedeco/javacv/blob/master/samples/WebcamAndMicrophoneCapture.java – Samuel Audet Aug 08 '20 at 10:20
  • Not a real solution, but using a library like https://www.tagtraum.com/ffsampledsp/index.html might be easier. – Hendrik Aug 09 '20 at 19:07
  • Hi and thanks to you two. The javacv FFmpeg grabber do the job by its simplicity of ready-to-use working. I can't put here the solve code cause my application is too fat. For the big picture, I use video playing and audio playing of whole video or audio files or piece of them. The other solution doesn't fit to me but I imagine it works. – TW2 Jan 14 '21 at 08:23

0 Answers0