Rumah >pembangunan bahagian belakang >Tutorial Python >IO data video melalui subproses ffmpeg

IO data video melalui subproses ffmpeg

Susan Sarandon
Susan Sarandonasal
2024-12-27 21:30:16762semak imbas

Semasa saya memulakan semula carian kerja saya (ya, saya masih #OpenToWork, ping saya!), dalam salah satu permohonan kerja, saya diminta untuk melaksanakan prototaip yang memproses data video. Semasa menjalankan projek itu, secara tidak disangka-sangka saya mendapat banyak bantuan daripada chatbot AI generatif kerana saya kurang pengalaman di kawasan itu.

Video data IO through ffmpeg subprocess

Seperti yang dinyatakan dalam tajuk, ffmpeg telah digunakan untuk melaksanakan beberapa kerja prapemprosesan. Salah satu matlamat projek itu adalah untuk dapat memainkan berbilang fail video satu demi satu. Walaupun terdapat pelbagai cara untuk mencapainya, saya memutuskan untuk menggunakan penyelesaian yang paling jelas, menggabungkannya bersama-sama.

$ cat video1 video2 video3 | python further-work.py

Untuk mencapainya, pertama sekali saya perlu mengekod semula fail ke dalam format yang membolehkannya. Selepas "berbincang" dengan Google Gemini mengenai perkara ini, chatbot mengesyorkan saya menggunakan MPEG-TS untuk tujuan tersebut.

Strim Pengangkutan MPEG (MPEG-TS) berfungsi dengan merangkum aliran asas berpaket. Strim ini termasuk audio, video dan data PSIP, yang dikemaskan ke dalam segmen kecil. Setiap aliran dicincang kepada bahagian 188-bait dan dijalin bersama. Proses ini memastikan kurang kependaman dan daya tahan ralat yang lebih besar, menjadikannya sesuai untuk persidangan video yang bingkai besar boleh menyebabkan kelewatan audio.

Dipetik daripada https://castr.com/blog/mpeg-transport-stream-mpeg-ts/

Terdapat format fail lain yang boleh digunakan untuk tujuan tersebut, tetapi ia tidak berkaitan dengan perbincangan. Selepas saya mendapatkan video yang dikodkan semula ke dalam format ini, data video akan dihantar ke baris gilir, untuk digunakan oleh modul lain, berjalan dalam proses lain.

Selepas menentukan kedua-dua input (senarai fail video untuk diambil dalam talian) dan output (kandungan fail video yang dikod semula), tiba masanya untuk memikirkan cara melakukannya. Malangnya, ffmpeg ialah utiliti rumit yang melakukan banyak perkara. Terdapat/berbilang percubaan untuk menyediakan beberapa antara muka untuk membantu pengguna dengannya (saya benar-benar mahu mencuba ini, tetapi ia sudah mati sekarang, nampaknya). Walau bagaimanapun, dengan betapa bergunanya AI generatif hari ini, mendapatkan arahan yang betul hanya beberapa langkah sahaja lagi.

ffmpeg -hwaccel cuda -i pipe:0 -c:v h264_nvenc -b:v 1.5M -c:a aac -b:a 128k -f mpegts -y pipe:1

Ia malah memberikan penjelasan tentang maksud setiap hujah tersebut, seperti yang ditunjukkan dalam tangkapan skrin di bawah.

Video data IO through ffmpeg subprocess
Percubaan Gemini untuk menerangkan arahan ffmpeg

Ringkasnya, arahan menerima kandungan fail video melalui stdin dan mengeluarkan kandungan fail video yang dikod semula sebagai stdout.

Sekarang masa untuk mengodkan pelaksanaan, kerana saya mahu membaca dan menulis ke ffmpeg secara serentak, jadi ini akan menjadi aplikasi asyncio. Pustaka klien http yang kami gunakan kali ini ialah httpx, yang mempunyai kaedah untuk mengambil muat turun dalam kelompok yang lebih kecil:

$ cat video1 video2 video3 | python further-work.py

Kami bimbang tentang pemprosesan sebenar nanti, buat masa ini kami hanya akan mendapatkan kod untuk mencetak ketulan ke skrin.

Seterusnya kami menulis fungsi untuk memanggil ffmpeg, melalui asyncio.create_subprocess_exec

ffmpeg -hwaccel cuda -i pipe:0 -c:v h264_nvenc -b:v 1.5M -c:a aac -b:a 128k -f mpegts -y pipe:1

Sebaik-baiknya, kami akan menggunakan process.communicate(file_content) di sini seperti yang dinasihatkan dalam dokumentasi, malangnya jika kami berbuat demikian, kami perlu memuat turun keseluruhan fail terlebih dahulu, yang pasti akan melambatkan respons, yang tidak sesuai.

Sebaliknya, kita boleh menggunakan process.stdin.write(), mari kemas kini fungsi write_input asal:

import httpx

client = httpx.AsyncClient()

async def write_input(
    client: httpx.AsyncClient, video_link: str, process: asyncio.subprocess.Process
) -> None:
    async with client.stream("GET", video_link) as response:
        async for chunk in response.aiter_raw(1024):
            print(chunk) # this is the downloaded video file, in chunks

Dengan setiap bahagian yang dimuat turun,

  1. kami menyuapnya kepada proses melalui process.stdin.write(chunk).
  2. Setelah selesai, kami akan menulis EOF (process.stdin.write_eof()) untuk menandakan penghujung input fail,
  3. diikuti dengan .close() (dan sepadan menanti .wait_closed())

Kembali ke fungsi video_send, kami meneruskan fungsi dengan membaca process.stdout. Mampu melakukan kedua-dua membaca dan menulis adalah tepat sebab kami melakukan ini melalui asyncio. Sebelum ini dalam tetapan segerak, kami hanya boleh melakukan satu demi satu dalam susunan tetap, tetapi kini kami boleh membiarkan penjadual bimbang tentang pesanan itu. Kini fungsi itu mempunyai kod berikut ditambah untuk membaca kandungan fail yang dikod semula, dan hantarkannya ke baris gilir:

async def video_send(client: httpx.AsyncClient, video_link: str) -> None:
    logger.info("DATA: Fetching video from link", link=video_link)
    process = await asyncio.create_subprocess_exec(
        "ffmpeg",
        "-hwaccel",
        "cuda",
        "-i",
        "pipe:0",
        "-c:v",
        "h264_nvenc",
        # "libx264",
        "-b:v",
        "1.5M",
        "-c:a",
        "aac",
        "-b:a",
        "128k",
        "-f",
        "mpegts",
        "-y",
        "pipe:1",
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
    )

    asyncio.create_task(write_input(client, video_link, process))

Dalam satu gelung, kita

  1. Ambil sebahagian daripada data daripada ffmpeg stdout
  2. Jika bongkah ialah rentetan kosong, putuskan daripada gelung
  3. Jika tidak, tolak bongkah ke baris gilir (melalui asyncio.to_thread, kerana kami menggunakan versi selamat proses di sini)
  4. Kemudian kita tunggu arahan untuk keluar dengan anggun, melalui process.wait()

Nampaknya sangat mudah sekarang, tetapi saya mengambil masa sepanjang malam untuk benar-benar menyelesaikan perkara ini dengan betul (dan saya masih menyemak kod semasa menulis ini). Separuh masa saya menyemak dokumentasi untuk memastikan saya tidak terlepas apa-apa, lain kali saya akan meminta Gemini menyemak kod saya.

Semoga anda mendapati ini berguna, dan itu sahaja untuk hari ini, semoga kita akan kembali kepada kandungan Kod Kemunculan yang dijanjikan sebelum ini pada minggu hadapan.

Atas ialah kandungan terperinci IO data video melalui subproses ffmpeg. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn