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.
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.
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,
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
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!