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

Failed to decode audio in .mkv files with flac codec [analysis and solution attached] #799

Open
yuantuo666 opened this issue Jul 8, 2024 · 0 comments

Comments

@yuantuo666
Copy link

TL;DR

Need to specify the correct codec since pydub having some bug handling not pcm encoded audio. E.g.:

from pydub import AudioSegment
audio = AudioSegment.from_file('test.mkv', codec='flac') # specify codec

If this not working, it might because your mkv file is not encoded in flac either! Run ffmpeg -i test.mkv and check the detected codec:

Stream #0:1(jpn): Audio: flac, 48000 Hz, stereo, s32 (24 bit) (default)

For my mkv it is flac. Check your own mkv file for the correct codec. Reference: https://stackoverflow.com/questions/2869281/how-to-determine-video-codec-of-a-file-with-ffmpeg

Steps to reproduce

Run:

from pydub import AudioSegment
audio = AudioSegment.from_file('test.mkv')

Expected behavior

Should load .mkv file successfully.

Actual behavior

pydub.exceptions.CouldntDecodeError: Decoding failed. ffmpeg returned error code: 1

Your System configuration

  • Python version: 3.9
  • Pydub version: pydub==0.25.1
  • ffmpeg or avlib?: ffmpeg
  • ffmpeg/avlib version: 4.2.2

Is there an audio file you can include to help us reproduce?

trimed.mkv.zip

Related Issues

#175
#191
#308

Fix this bug

Test show that remove the -acodec argument, ffmpeg can auto detect the correct format. (Only flac tested, I am not clear is this work for other codecs)
In the pydub/audio_segment.py file:

--- if codec:
+++ if codec and codec != "auto":
            # force audio decoder
            conversion_command += ["-acodec", codec]

And use:

from pydub import AudioSegment
audio = AudioSegment.from_file('test.mkv', codec='auto') # specify codec

Details

I have a mkv file with flac as audio codec.

Running following test code:

from pydub import AudioSegment
file_obj = open('test.mkv', 'rb')
audio = AudioSegment.from_file(file_obj)

Got the following error message:

Traceback (most recent call last):
  File "/test/pydub_mkv.py", line 5, in <module>
    audio = AudioSegment.from_file(file_obj)
  File "/miniconda3/envs/xxx/lib/python3.9/site-packages/pydub/audio_segment.py", line 775, in from_file
    raise CouldntDecodeError(
pydub.exceptions.CouldntDecodeError: Decoding failed. ffmpeg returned error code: 1

Output from ffmpeg/avlib:

ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers
  built with gcc 7.3.0 (crosstool-NG 1.23.0.449-a04d0)
  configuration: --prefix=/tmp/build/80754af9/ffmpeg_1587154242452/_h_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placeho --cc=/tmp/build/80754af9/ffmpeg_1587154242452/_build_env/bin/x86_64-conda_cos6-linux-gnu-cc --disable-doc --enable-avresample --enable-gmp --enable-hardcoded-tables --enable-libfreetype --enable-libvpx --enable-pthreads --enable-libopus --enable-postproc --enable-pic --enable-pthreads --enable-shared --enable-static --enable-version3 --enable-zlib --enable-libmp3lame --disable-nonfree --enable-gpl --enable-gnutls --disable-openssl --enable-libopenh264 --enable-libx264
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libavresample   4.  0.  0 /  4.  0.  0
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
[cache @ 0x557210c9c6c0] Inner protocol failed to seekback end : -38
[cache @ 0x557210c9c6c0] write in cache failed
    Last message repeated 80957 times
[cache @ 0x557210c9c6c0] Failed to perform internal seek
[matroska,webm @ 0x557210c9be40] Read error at pos. 974984906 (0x3a1d16ca)
[cache @ 0x557210c9c6c0] Inner protocol failed to seekback end : -38
    Last message repeated 2 times
Input #0, matroska,webm, from 'cache:pipe:0':
  Metadata:
    encoder         : libebml v1.3.4 + libmatroska v1.4.5
    creation_time   : 2019-08-27T12:20:40.000000Z
  Duration: 00:22:56.38, start: 0.000000, bitrate: 5666 kb/s
    Chapter #0:0: start 0.000000, end 112.028000
    Metadata:
      title           : 
    Chapter #0:1: start 112.028000, end 555.054000
    Metadata:
      title           : 
    Chapter #0:2: start 555.054000, end 1257.047000
    Metadata:
      title           : 
    Chapter #0:3: start 1257.047000, end 1347.054000
    Metadata:
      title           : 
    Chapter #0:4: start 1347.054000, end 1365.072000
    Metadata:
      title           : 
    Chapter #0:5: start 1365.072000, end 1376.375000
    Metadata:
      title           : 
    Stream #0:0(jpn): Video: hevc (Main 10), yuv420p10le(tv, bt709/unknown/unknown), 1920x1080, SAR 1:1 DAR 16:9, 23.98 fps, 23.98 tbr, 1k tbn, 23.98 tbc (default)
    Stream #0:1(jpn): Audio: flac, 48000 Hz, stereo, s32 (24 bit) (default)
Unknown encoder 'pcm_s0le'
[cache @ 0x557210c9c6c0] Statistics, cache hits:7 cache misses:136423

Note the error is Unknown encoder 'pcm_s0le' which did not show in the ffmpeg -encoders, and running ffmpeg -i test.mkv test.wav works file, which indicate the error might from the pcm_s0le, and this should be generated by pydub library.

By checking the pydub code, found following code in pydub/audio_segment.py:

        if codec:
            info = None
        else:
            info = mediainfo_json(orig_file, read_ahead_limit=read_ahead_limit)
        if info:
            audio_streams = [x for x in info['streams']
                             if x['codec_type'] == 'audio']
            # This is a workaround for some ffprobe versions that always say
            # that mp3/mp4/aac/webm/ogg files contain fltp samples
            audio_codec = audio_streams[0].get('codec_name')
            if (audio_streams[0].get('sample_fmt') == 'fltp' and
                    audio_codec in ['mp3', 'mp4', 'aac', 'webm', 'ogg']):
                bits_per_sample = 16
            else:
                bits_per_sample = audio_streams[0]['bits_per_sample'] # some bug here
            if bits_per_sample == 8:
                acodec = 'pcm_u8'
            else:
                acodec = 'pcm_s%dle' % bits_per_sample

            conversion_command += ["-acodec", acodec]

Here, the acodec = 'pcm_s%dle' % bits_per_sample is the problem. When bits_per_sample is 0, the error occurred.

Checking the mediainfo_json function, find out it actually called ffprobe, running the constructed command manually showed that the bits_per_sample is really 0 for my test.mkv file.

By searching my mkv audio codec and the error info, found the following issue:

https://trac.ffmpeg.org/ticket/3047

At the bottom of this page:

Afaict, bits_per_sample is only valid for pcm and closely related codecs. Perhaps sample_fmt is what you are searching for? A flac file (as supported by FFmpeg) can either output s16 or s32 (or s16p or s32p respectively).

Which clearly state that the bits_per_sample should belong to audio that uses pcm codes. So in my case, for flac codec, it should use the flac codec as displayed in ffmpeg -encoders.

A..... flac                 FLAC (Free Lossless Audio Codec)

Change test code to:

from pydub import AudioSegment
file_obj = open('test.mkv', 'rb')
audio = AudioSegment.from_file(file_obj, codec='flac')

This make it works!

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

1 participant