kkroening / ffmpeg-python

Python bindings for FFmpeg - with complex filtering support
Apache License 2.0
9.64k stars 875 forks source link

How to convert mp4 to multi-segment and multi-quality hls? #842

Open AmirhBrt opened 1 week ago

AmirhBrt commented 1 week ago

I want to convert a mp4 file and I am using this command to convert my file to multiple qualities:

ffmpeg -hide_banner -y -i test.mp4 \ 
-f hls -master_pl_name test_video_hls.m3u8 \
-filter_complex "[0:v]split=4[v1][v2][v3][v4];[v1]scale=w=1920:h=1080:force_original_aspect_ratio=increase[v1out];[v2]scale=w=1280:h=720:force_original_aspect_ratio=increase[v2out];[v3]scale=w=854:h=480:force_original_aspect_ratio=increase[v3out];[v4]scale=w=640:h=360:force_original_aspect_ratio=increase[v4out];[0:a]asplit=4[a1][a2][a3][a4]" \                                                              
-map "[v1out]" -map "[a1]" -b:v:0 5000k -maxrate:v:0 5350k -bufsize:v:0 7500k -b:a:0 192k \
-map "[v2out]" -map "[a2]" -b:v:1 2800k -maxrate:v:1 2996k -bufsize:v:1 4200k -b:a:1 128k \
-map "[v3out]" -map "[a3]" -b:v:2 1400k -maxrate:v:2 1498k -bufsize:v:2 2100k -b:a:2 128k \
-map "[v4out]" -map "[a4]" -b:v:3 800k -maxrate:v:3 856k -bufsize:v:3 1200k -b:a:3 96k \
-c:a aac -ac 2 -ar 48000 \          
-var_stream_map "v:0,a:0,name:1080p v:1,a:1,name:720p v:2,a:2,name:480p v:3,a:3,name:360p" \
-profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 \
-hls_time 2 -hls_playlist_type vod -hls_flags independent_segments \
-hls_segment_filename /tmp/downloaded-videos/test_video_hls_%v_%03d.ts /tmp/downloaded-videos/test_video_hls_%v.m3u8

How can I convert it to ffmpeg-python?

AmirhBrt commented 5 days ago

I finally came up with this solution, but it still can not be run due to empty characters as file names. Anyone has any idea how to fix it properly without using subprocess library commands?

ffmpeg_input = ffmpeg.input(source_file_path)
video_splits = ffmpeg_input.video.split()
audio_splits = ffmpeg_input.audio.asplit()
ffmpeg_outputs = []
for i, quality in enumerate(HLS_QUALITIES):
    ffmpeg_outputs.append(
        ffmpeg.output(
            video_splits[i].filter_(
                "scale",
                w=quality.width,
                h=quality.height,
                force_original_aspect_ratio="increase",
            ),
            # Attach the corresponding audio stream
            audio_splits[i],
            filename="",
            **{
                # Set the video bitrate
                f"b:v:{i}": f"{quality.video_bitrate}k",
                # Set the maximum video bitrate
                f"maxrate:v:{i}": f"{quality.video_max_rate}k",
                # Set the buffer size for video
                f"bufsize:v:{i}": f"{quality.buffer_size}k",
                # Set the audio bitrate
                f"b:a:{i}": f"{quality.audio_bitrate}k",
            },
        )
    )

var_stream_map = " ".join([f"v:{i},a:{i},name:{quality.height}" for i, quality in enumerate(HLS_QUALITIES)])

stream = ffmpeg.merge_outputs(
    *ffmpeg_outputs,
    ffmpeg_input.output(
        f"{destination_file_path}_%v.m3u8",
        f="hls",
        master_pl_name=f"{output_file_prefix}.m3u8",
        ar=48000,  
        vprofile="main",  
        crf=20,  
        sc_threshold=0,  
        g=48,  
        keyint_min=48,  
        hls_time=2,  
        hls_playlist_type="vod",  
        hls_flags="independent_segments",  
        hls_segment_filename=f"{destination_file_path}_%v_%03d.ts",  
        var_stream_map=f"{var_stream_map}"  
    ),
).overwrite_output()