Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

segfault when transcoding and frame sizes match exactly #71

Open
bavardage opened this issue Apr 28, 2023 · 2 comments
Open

segfault when transcoding and frame sizes match exactly #71

bavardage opened this issue Apr 28, 2023 · 2 comments

Comments

@bavardage
Copy link
Contributor

In a slightly degenerate case, I'm transcoding a 44.1khz stereo mp3 to a 44.1khz stereo mp3.

When flushing we pass in a null frame to swr_convert_frame in resampler.c:256

This then ends up returning a frame with nb_samples of 0, because everything matched up exactly (I think)

image

This 'empty' frame gets send along to avcodec_send_frame, which then segfaults :/

I think a potential fix is to only push non-empty frames to the Encoder, but curious for ideas on other fixes

e.g. in mod.rs making this change fixes the segfault

   fn try_push(&mut self, frame: AudioFrame) -> Result<(), CodecError> {
        if frame.samples() == 0 {
            println!("skipping empty frame");
            return Ok(()); 
        }

I'm not entirely sure of exactly where the segfault happens - I need to figure out a local build of ffmpeg with debug symbols

@operutka
Copy link
Member

Interesting. I'm not sure if it's a bug in our crate or if the bug is somewhere in the FFmpeg. In any case, if discarding empty frames by the encoder helps, it's probably worth implementing. Though I'm also not sure if encoding empty frames may be intentional in some cases.

Could you post here a code example that can be used to reproduce the bug? I can help with debugging.

@bavardage
Copy link
Contributor Author

bavardage commented May 2, 2023

    #[test]
     fn empty_frames() {
        // load sample mp3
        let input_path = concat!(env!("CARGO_MANIFEST_DIR"), "/resources/test/broken.mp3");
        let input_file = File::open(input_path).unwrap();

        // output to a vec
        let output = Vec::new();

        // wrap input and output in io
        let input = IO::from_seekable_read_stream(input_file);
        let output = IO::from_write_stream(output);

        // create demuxer
        let mut demuxer = Demuxer::builder()
            .build(input)
            .unwrap()
            .find_stream_info(None)
            .map_err(|_| "could not find stream info")
            .unwrap();

        // find audio stream
        let (input_stream_index, _, input_codec_parameters) = demuxer
            .streams()
            .iter()
            .enumerate()
            .map(|(stream_index, stream)| {
                (
                    stream_index,
                    stream,
                    stream.codec_parameters().into_audio_codec_parameters(),
                )
            })
            .find(|(_, _, params)| params.is_some())
            .map(|(stream_index, stream, params)| (stream_index, stream, params.unwrap()))
            .expect("should find audio stream");

        // create output/muxer
        let channel_layout = ChannelLayout::from_channels(2).unwrap();
        let sample_format = SampleFormat::from_str("fltp").unwrap();
        let output_format = OutputFormat::find_by_name("mp3").unwrap();

        let output_codec_parameters = AudioCodecParameters::builder("mp3")
            .unwrap()
            .channel_layout(&channel_layout)
            .sample_format(sample_format)
            .sample_rate(44100)
            .build();

        let mut muxer = Muxer::builder();
        let output_stream_index = muxer
            .add_stream(&CodecParameters::from(output_codec_parameters.clone()))
            .unwrap();
        let mut muxer = muxer.build(output, output_format).unwrap();

        // create transcoder
        let mut transcoder =
            AudioTranscoder::builder(input_codec_parameters, output_codec_parameters)
                .unwrap()
                .build()
                .unwrap();

        // do transcode
        while let Some(packet) = demuxer.take().unwrap() {
            if packet.stream_index() != input_stream_index {
                continue;
            }

            transcoder.push(packet).unwrap();

            while let Some(packet) = transcoder.take().unwrap() {
                let packet = packet.with_stream_index(output_stream_index);
                muxer.push(packet).unwrap();
            }
        }

        println!("flushing transcoder");
        transcoder.flush().unwrap();

        // drain any remaining packets
        while let Some(packet) = transcoder.take().unwrap() {
            let packet = packet.with_stream_index(output_stream_index);
            muxer.push(packet).unwrap()
        }

        println!("flushing muxer");
        muxer.flush().unwrap();

        println!("closing muxer");
        muxer.close().unwrap();
    }

zip file containing the mp3:
broken.mp3.zip

this fails with a segfault, unless I skip the empty frames

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants