Failed to decode audio in `.mkv` files with `flac` codec [analysis and solution attached]
yuantuo666 opened this issue · 0 comments
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?
Related Issues
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!