Newest 'ffmpeg' Questions - Stack Overflow

http://stackoverflow.com/questions/tagged/ffmpeg

Les articles publiés sur le site

  • Can ffmpeg show a progress bar ?

    19 mai, par Pawan Rao

    I am converting a .avi file to .flv file using ffmpeg. As it takes a long time to convert a file I would like to display a progress bar. Can someone please guide me on how to go about the same.

    I know that ffmpeg somehow has to output the progress in a text file and I have to read it using ajax calls. But how do I get ffmpeg to output the progress to the text file?

  • I'm trying to hide information in a H264 video. When I stitch the video up, split it into frames again and try to read it, the information is lost

    18 mai, par Wer Wer

    I'm trying to create a video steganography python script. The algorithm for hiding will be...

    1. convert any video codec into h264 lossless
    2. save the audio of the video and split the h264 video into frames
    3. hide my txt secret into frame0 using LSB replacement method
    4. stitch the video back up and put in the audio

    ...and when I want to recover the text, I'll

    1. save the audio of the video and split the encoded h264 video into frames
    2. retrieve my hidden text from frame0 and print the text

    So, this is what I can do:

    1. split the video
    2. hide the text in frame0
    3. retrieve the text from frame0
    4. stitch the video

    But after stitching the video, when I tried to retrieve the text by splitting that encrypted video, it appears that the text has been lost. This is because i got the error

    UnicodeEncodeError: 'charmap' codec can't encode character '\x82' in position 21: character maps to 
    

    I'm not sure if my LSB replacement algorithm was lost, which results in my not being able to retrieve my frame 0 information, or if the H264 conversion command I used was a converted my video into H264 lossy version instead of lossless (which I don't believe so because I specified -qp 0) This was the command I used to convert my video

    ffmpeg -i video.mp4 -t 12 -c:v libx264 -preset veryslow -qp 0 output.mp4
    

    These are my codes

    import json
    import os
    import magic
    import ffmpeg
    import cv2
    import numpy as np
    
    import subprocess
    
    # Path to the file you want to check
    here = os.path.dirname(os.path.abspath(__file__))
    file_path = os.path.join(here, "output.mp4")
    raw_video = cv2.VideoCapture(file_path)
    audio_output_path = os.path.join(here, "audio.aac")
    final_video_file = os.path.join(here, "output.mp4")
    
    # create a folder to save the frames.
    frames_directory = os.path.join(here, "data1")
    try:
        if not os.path.exists(frames_directory):
            os.makedirs(frames_directory)
    except OSError:
        print("Error: Creating directory of data")
    
    file_path_txt = os.path.join(here, "hiddentext.txt")
    # Read the content of the file in binary mode
    with open(file_path_txt, "r") as f:
        file_content = f.read()
    # txt_binary_representation = "".join(format(byte, "08b") for byte in file_content)
    # print(file_content)
    
    """
    use this cmd to convert any video to h264 lossless. original vid in 10 bit depth format
    ffmpeg -i video.mp4 -c:v libx264 -preset veryslow -qp 0 output.mp4
    
    use this cmd to convert any video to h264 lossless. original vid in 8 bit depth format
    ffmpeg -i video.mp4 -c:v libx264 -preset veryslow -crf 0 output.mp4
    
    i used this command to only get first 12 sec of video because the h264 vid is too large 
    ffmpeg -i video.mp4 -t 12 -c:v libx264 -preset veryslow -qp 0 output.mp4
    
    check for multiple values to ensure its h264 lossless:
    1. CRF = 0
    2. qp = 0
    3. High 4:4:4 Predictive
    """
    
    
    # region --codec checking. ensure video is h264 lossless--
    def check_h264_lossless(file_path):
        try:
            # Use ffprobe to get detailed codec information, including tags
            result = subprocess.run(
                [
                    "ffprobe",
                    "-v",
                    "error",
                    "-show_entries",
                    "stream=codec_name,codec_long_name,profile,level,bit_rate,avg_frame_rate,nb_frames,tags",
                    "-of",
                    "json",
                    file_path,
                ],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
            )
            # Check if the file is lossless
            metadata = check_h264_lossless(file_path)
            print(json.dumps(metadata, indent=4))
    
            # Check if the CRF value is available in the tags
            for stream in metadata.get("streams", []):
                if stream.get("codec_name") == "h264":
                    tags = stream.get("tags", {})
                    crf_value = tags.get("crf")
                    encoder = tags.get("encoder")
                    print(f"CRF value: {crf_value}")
                    print(f"Encoder: {encoder}")
            return json.loads(result.stdout)
        except Exception as e:
            return f"An error occurred: {e}"
    
    
    # endregion
    
    
    # region --splitting video into frames--
    def extract_audio(input_video_path, audio_output_path):
        if os.path.exists(audio_output_path):
            print(f"Audio file {audio_output_path} already exists. Skipping extraction.")
            return
        command = [
            "ffmpeg",
            "-i",
            input_video_path,
            "-q:a",
            "0",
            "-map",
            "a",
            audio_output_path,
        ]
        try:
            subprocess.run(command, check=True)
            print(f"Audio successfully extracted to {audio_output_path}")
        except subprocess.CalledProcessError as e:
            print(f"An error occurred: {e}")
    
    
    def split_into_frames():
        extract_audio(file_path, audio_output_path)
        currentframe = 0
        print("Splitting...")
        while True:
            ret, frame = raw_video.read()
            if ret:
                name = os.path.join(here, "data1", f"frame{currentframe}.png")
                # print("Creating..." + name)
                cv2.imwrite(name, frame)
                currentframe += 1
            else:
                print("Complete")
                break
    
    
    # endregion
    
    
    # region --merge all back into h264 lossless--
    # output_video_file = "output1111.mp4"
    
    
    def stitch_frames_to_video(frames_dir, output_video_path, framerate=60):
        command = [
            "ffmpeg",
            "-y",
            "-framerate",
            str(framerate),
            "-i",
            os.path.join(frames_dir, "frame%d.png"),
            "-c:v",
            "libx264",
            "-preset",
            "veryslow",
            "-qp",
            "0",
            output_video_path,
        ]
    
        try:
            subprocess.run(command, check=True)
            print(f"Video successfully created at {output_video_path}")
        except subprocess.CalledProcessError as e:
            print(f"An error occurred: {e}")
    
    
    def add_audio_to_video(video_path, audio_path, final_output_path):
        command = [
            "ffmpeg",
            "-i",
            video_path,
            "-i",
            audio_path,
            "-c:v",
            "copy",
            "-c:a",
            "aac",
            "-strict",
            "experimental",
            final_output_path,
        ]
        try:
            subprocess.run(command, check=True)
            print(f"Final video with audio created at {final_output_path}")
        except subprocess.CalledProcessError as e:
            print(f"An error occurred: {e}")
    
    
    # endregion
    
    
    def to_bin(data):
        if isinstance(data, str):
            return "".join([format(ord(i), "08b") for i in data])
        elif isinstance(data, bytes) or isinstance(data, np.ndarray):
            return [format(i, "08b") for i in data]
        elif isinstance(data, int) or isinstance(data, np.uint8):
            return format(data, "08b")
        else:
            raise TypeError("Type not supported")
    
    
    def encode(image_name, secret_data):
        image = cv2.imread(image_name)
        n_bytes = image.shape[0] * image.shape[1] * 3 // 8
        print("[*] Maximum bytes to encode:", n_bytes)
        secret_data += "====="
        if len(secret_data) > n_bytes:
            raise ValueError("[!] Insufficient bytes, need bigger image or less data")
        print("[*] Encoding Data")
    
        data_index = 0
        binary_secret_data = to_bin(secret_data)
        data_len = len(binary_secret_data)
        for row in image:
            for pixel in row:
                r, g, b = to_bin(pixel)
                if data_index < data_len:
                    pixel[0] = int(r[:-1] + binary_secret_data[data_index], 2)
                    data_index += 1
                if data_index < data_len:
                    pixel[1] = int(g[:-1] + binary_secret_data[data_index], 2)
                    data_index += 1
                if data_index < data_len:
                    pixel[2] = int(b[:-1] + binary_secret_data[data_index], 2)
                    data_index += 1
                if data_index >= data_len:
                    break
        return image
    
    
    def decode(image_name):
        print("[+] Decoding")
        image = cv2.imread(image_name)
        binary_data = ""
        for row in image:
            for pixel in row:
                r, g, b = to_bin(pixel)
                binary_data += r[-1]
                binary_data += g[-1]
                binary_data += b[-1]
        all_bytes = [binary_data[i : i + 8] for i in range(0, len(binary_data), 8)]
        decoded_data = ""
        for byte in all_bytes:
            decoded_data += chr(int(byte, 2))
            if decoded_data[-5:] == "=====":
                break
        return decoded_data[:-5]
    
    
    frame0_path = os.path.join(here, "data1", "frame0.png")
    encoded_image_path = os.path.join(here, "data1", "frame0.png")
    
    
    def encoding_function():
        split_into_frames()
    
        encoded_image = encode(frame0_path, file_content)
        cv2.imwrite(encoded_image_path, encoded_image)
    
        stitch_frames_to_video(frames_directory, file_path)
        add_audio_to_video(file_path, audio_output_path, final_video_file)
    
    
    def decoding_function():
        split_into_frames()
        decoded_message = decode(encoded_image_path)
        print(f"[+] Decoded message: {decoded_message}")
    
    
    # encoding_function()
    decoding_function()
    
    

    So I tried to put my decoding function into my encoding function like this

    def encoding_function():
        split_into_frames()
    
        encoded_image = encode(frame0_path, file_content)
        cv2.imwrite(encoded_image_path, encoded_image)
    
    #immediately get frame0 and decode without stitching to check if the data is there
        decoded_message = decode(encoded_image_path)
        print(f"[+] Decoded message: {decoded_message}")
    
        stitch_frames_to_video(frames_directory, file_path)
        add_audio_to_video(file_path, audio_output_path, final_video_file)
    
    

    This returns my secret text from frame0. But splitting it after stitching does not return my hidden text. The hidden text was lost

    def decoding_function():
        split_into_frames()
    #this function is after the encoding_function(). the secret text is lost, resulting in charmap codec #can't encode error
        decoded_message = decode(encoded_image_path)
        print(f"[+] Decoded message: {decoded_message}")
    

    EDIT: So i ran the encoding function first, copied frame0.png out and placed it some where. Then I ran the decoding function, and got another frame0.png.

    I ran both frame0.png into this python function

    frame0_data1_path = os.path.join(here, "data1", "frame0.png")
    frame0_data2_path = os.path.join(here, "data2", "frame0.png")
    frame0_data1 = cv2.imread(frame0_data1_path)
    frame0_data2 = cv2.imread(frame0_data2_path)
    
    if frame0_data1 is None:
        print(f"Error: Could not load image from {frame0_data1_path}")
    elif frame0_data2 is None:
        print(f"Error: Could not load image from {frame0_data2_path}")
    else:
    
        if np.array_equal(frame0_data1, frame0_data2):
            print("The frames are identical.")
        else:
            print("The frames are different.")
    

    ...and apparently both are different. This means my frame0 binary got changed when I stitch back into the video after encoding. Is there a way to make it not change? Or will h264 or any video codec change a little bit when you stitch the frames back up?

  • Edge of text clipped a few pixels in ffmpeg

    18 mai, par THEMOUNTAINSANDTHESKIES

    Trying to make a short video of a background image with text and some background audio. It's functional, but the right side of each line of my text is always clipped a few pixels. What am I missing?

    Here's my python code (which includes the ffmpeg command):

    font_size = 100
    text='Example Text Example Text \n Example Text'
    font_path = 'Bangers-Regular.ttf'
    image_path = 'background.jpg'
    audio_path = 'audio.wav'
    duration = 15 #or whatever the audio's length is
    output_path = 'output.mp4'
    
    font_path_escaped = font_path.replace("\\", "\\\\\\\\").replace("C:", "C\\:")
    
    lines = text.split('\n')
    num_lines = len(lines)
    line_height = font_size + 10
    start_y = (1080 - line_height * num_lines) // 2
    
    filter_complex = []
    for i, line in enumerate(lines):
        y_position = start_y + i * line_height
        filter_complex.append(
            f"drawtext=text='{line}':fontfile='{font_path_escaped}':fontsize={font_size}:"
            f"x=((w-text_w)/2):y={y_position}:"
            "fontcolor=white:borderw=6:bordercolor=black"
        )
    
    filter_complex_string = ','.join(filter_complex)
    
    command = [
        'ffmpeg',
        '-loop', '1',
        '-i', image_path,
        '-i', audio_path,
        '-filter_complex', filter_complex_string,
        '-map', '[v]',
        '-map', '1:a',
        '-c:v', 'hevc_nvenc',
        '-c:a', 'aac',
        '-pix_fmt', 'yuv420p',
        '-t', str(duration),
        '-shortest',
        '-loglevel', 'debug',
        '-y',
        output_path
    ]
    
    
    subprocess.run(command, check=True)
    print(f"Video created successfully: {output_path}")
    

    and a frame from the outputted video:

    enter image description here

  • FFMPEG merge continous input stream with intermittent input stream without waiting

    18 mai, par gian848396

    My goal is to take two audio inputs and merge them. The below works for this, however, I want a continuous output stream even when only one of the two inputs are being streamed into FFmpeg. The below only produces an output when the stdin pipe input is receiving audio.

    Here I have an FFmpeg child process where one input is a stream from the device's microphone, the other comes from wav audio buffers sent to the server in clips by a client.

    @Injectable()
    export class AppService {
      ffmpegProcess: ChildProcessWithoutNullStreams;
    
      constructor() {
        this.start();
      }
    
      start() {
        this.ffmpegProcess = spawn(ffmpegPath, [
          '-f', 'avfoundation', // mac os media devices
          '-i', ':1', // mac os microphone input
          '-f', 'wav',
          '-i', 'pipe:', // stdin input source
          '-codec:a', 'aac',
          '-b:a', '128k',
          // '-ab', '32k',
          '-f', 'hls',
          '-hls_time', '4', // Segment duration (in seconds)
          '-hls_list_size', '3', // Number of HLS segments to keep in playlist
          '-hls_flags', 'delete_segments', // Automatically delete old segments
          '-filter_complex', 'amerge=inputs=2',
          'public/output.m3u8' // HLS playlist file name
        ]);
      }
    }
    
    

    Here I'm piping wav audio buffers posted to the server and streamed as the stdin source to the FFMepg process, to be merged into the output.

    @Controller()
    export class AppController {
      constructor(
        private readonly appService: AppService,
      ) { }
    
      @Post('broadcast')
      @UseInterceptors(FileInterceptor('audio_data'))
      uploadFile(@UploadedFile() file: Express.Multer.File) {
        console.log(file);
        Readable.from(file.buffer).pipe(this.appService.ffmpegProcess.stdin);
      }
    }
    

    I've considered trying to pass in a null stream for the stdin when no submitted wav audio is present though I'm spinning my wheels. How can I produce a continuous output while only one of the two inputs are streaming, and while both are streaming?

  • ffmpeg.wasm DOMException : Failed to construct 'Worker' : Script at '/814.ffmpeg.js' cannot be accessed from origin ''

    18 mai, par Quantum

    I have written a google chrome extension using local ffmpegwasm files (no need to run a server or download any ffmpegwasm files), which allows users to click an injected 'download' button under Reddit videos (via content_scripts). This was working great on ffmpegwasm v11, but since migrating to v12 (latest version) I have been unable to solve the following exception which is triggered during ffmpeg.load(): as seen in chrome's console log:

    DOMException: Failed to construct 'Worker': Script at 'https://www.redditstatic.com/814.ffmpeg.js' cannot be accessed from origin 'https://reddit.com'. At i.load (chrome-extension://ljejfmmefhnpbpdhkhbbajpihfhnbdnm/lib/ffmpeg/umd/ffmpeg.js:1:2918)
    

    I'm a programming beginner and I've been stuck trying to fix this for many, many hours. This exception does not happen and the video downloads if I instead use a feature of my extension which allows entering a Reddit video URL directly into a field in the extensions popup or options window. Also, I have no idea why the 814.ffmpeg.js file is being referenced along with redditstatic.com (from exception to exception, this url seems to randomly change to other reddit url's too), though I imagine this is part of the problem?

    I have seen there are a couple of mentions of this error on the ffmpegwasm github repository, with some fixes involving using the worker814URL and/or classWorkerURL params within ffmpeg.load() and pointing them to the 814.ffmpeg.js file, which I have tried without any success (although I did notice that using classWorkerURL changed https://www.redditstatic.com/814.ffmpeg.js to chrome-extension://ljejfmmefhnpbpdhkhbbajpihfhnbdnm/lib/ffmpeg/umd/814.ffmpeg.js). Though, It seems that previous discussions around this error might not be too helpful for me as those appear geared towards ffmpegwasm running with a server, as apposed to entirely locally within a chrome extension.

    Any help would be super appreciated - many thanks