4 tahun selepas memulakan perjalanan membina SaaS yang menarik, inilah masa yang sesuai untuk membina semula salah satu komponen utama apl kami.
Editor video ringkas untuk video media sosial yang ditulis dalam JavaScript.
Inilah timbunan yang saya putuskan untuk digunakan untuk penulisan semula ini, yang kini sedang dalam proses.
Memandangkan bahagian hadapan kami ditulis dalam SvelteKit, ini adalah pilihan terbaik untuk bekas penggunaan kami.
Editor video ialah pustaka npm peribadi yang berasingan yang boleh saya tambahkan pada bahagian hadapan kami. Ia adalah perpustakaan tanpa kepala, jadi UI editor video diasingkan sepenuhnya.
Pustaka editor video bertanggungjawab untuk menyegerakkan elemen video dan audio dengan garis masa, menghasilkan animasi dan peralihan, memaparkan teks HTML ke dalam kanvas dan banyak lagi.
SceneBuilderFactory mengambil pemandangan objek JSON sebagai hujah untuk mencipta pemandangan. StateManager.svelte.ts kemudian mengekalkan keadaan semasa editor video dalam masa nyata.
Ini sangat berguna untuk melukis dan mengemas kini kedudukan kepala main dalam garis masa, dan banyak lagi.
Pixi.js ialah pustaka kanvas JavaScript yang luar biasa.
Pada mulanya, saya mula membina projek ini dengan Pixi v8, tetapi disebabkan beberapa sebab yang akan saya nyatakan kemudian dalam artikel ini, saya memutuskan untuk menggunakan Pixi v7.
Walau bagaimanapun, pustaka editor video tidak berganding rapat dengan mana-mana kebergantungan, jadi mudah untuk menggantikannya jika perlu atau untuk menguji alatan yang berbeza.
Untuk pengurusan garis masa dan animasi yang kompleks, saya memutuskan untuk menggunakan GSAP.
Tiada alat lain dalam ekosistem JavaScript yang saya ketahui yang membenarkan membina garis masa bersarang, animasi gabungan atau animasi teks kompleks dengan cara yang begitu mudah.
Saya mempunyai lesen perniagaan GSAP, jadi saya juga boleh memanfaatkan alatan tambahan untuk memudahkan lagi perkara.
Sebelum kita menyelami bahan yang saya gunakan di bahagian belakang, mari lihat beberapa cabaran yang perlu anda selesaikan semasa membina editor video dalam javascript.
Soalan ini sering ditanya dalam forum GSAP.
Tidak kira anda menggunakan GSAP untuk pengurusan garis masa atau tidak, perkara yang perlu anda lakukan ialah beberapa perkara.
Pada setiap paparan tandakan:
Dapatkan masa relatif video kepada garis masa. Katakan video anda mula dimainkan dari awal pada tanda 10 saat garis masa.
Nah, sebelum 10 saat anda sebenarnya tidak mengambil berat tentang elemen video, tetapi sebaik sahaja ia memasuki garis masa, anda perlu memastikannya sentiasa segerak.
Anda boleh melakukan ini dengan mengira masa relatif video, yang mesti dikira daripada Masa semasa elemen video, berbanding dengan masa adegan semasa dan dalam tempoh "selang" yang boleh diterima.
Jika ketinggalan lebih besar daripada, katakan, 0.3 saat, anda perlu mencari secara automatik elemen video untuk membetulkan penyegerakannya dengan garis masa utama. Ini juga terpakai pada elemen audio juga.
Perkara lain yang perlu anda pertimbangkan:
Main dan jeda adalah mudah untuk dilaksanakan. Untuk mencari, saya menambah id komponen mencari video ke dalam StateManager kami yang ringkas, yang secara automatik akan menukar keadaan kepada "memuatkan".
StateManager mempunyai pergantungan EventManager dan pada setiap perubahan keadaan, ia secara automatik mencetuskan acara "changestate", jadi kami boleh mendengar acara ini tanpa menggunakan $effect.
Perkara yang sama berlaku selepas pencarian selesai dan video sedia untuk dimainkan.
Dengan cara ini kami boleh menunjukkan penunjuk pemuatan dan bukannya butang main / jeda dalam UI kami apabila beberapa komponen sedang dimuatkan.
CSS, GSAP dan TextSplitter GSAP membolehkan saya melakukan perkara yang sangat menakjubkan dengan elemen teks.
Elemen teks kanvas asli adalah terhad dan memandangkan kes penggunaan utama apl kami adalah untuk mencipta video bentuk pendek untuk media sosial, ia tidak sesuai.
Nasib baik, saya menemui cara untuk memaparkan hampir mana-mana teks HTML ke dalam kanvas, yang penting untuk memaparkan output video.
Pixi HTMLTeks
Ini akan menjadi penyelesaian yang paling mudah; malangnya, ia tidak berkesan untuk saya.
Semasa saya menganimasikan teks HTML dengan GSAP, teks itu ketinggalan dengan ketara, dan ia juga tidak menyokong banyak fon Google yang saya cuba dengannya.
Satori
Satori sangat mengagumkan, dan saya boleh bayangkan ia digunakan dalam beberapa kes penggunaan yang lebih mudah. Malangnya, sesetengah animasi GSAP menukar gaya yang tidak serasi dengan Satori, yang mengakibatkan ralat.
SVG dengan objek asing
Akhir sekali, saya membuat penyelesaian tersuai untuk menyelesaikan masalah ini.
Bahagian yang sukar ialah menyokong emoji dan fon tersuai, tetapi saya berjaya menyelesaikannya.
Saya mencipta kelas SVGGenerator yang mempunyai kaedah generateSVG, yang menghasilkan SVG seperti ini:
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" version="1.1">${styleTag}<foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="transform-origin: 0 0;">${html}</div></foreignObject></svg>
StyleTag kemudiannya kelihatan seperti ini:
<style>@font-face { font-family: ${fontFamilyName}; src: url('${fontData}') }</style>
Untuk ini berfungsi, HTML yang kami hantarkan perlu mempunyai set keluarga fon yang betul dalam gaya sebaris. Data fon mestilah rentetan data berkod base64, seperti data:font/ttf;base64,longboringstring
Komposisi atas warisan, kata mereka.
Sebagai latihan untuk mengotorkan tangan saya, saya memfaktorkan semula daripada pendekatan berasaskan warisan kepada sistem berasaskan cangkuk.
Dalam editor video saya, saya memanggil elemen seperti VIDEO, AUDIO, TEKS, SARIKATA, IMEJ, BENTUK, dll. komponen.
Sebelum menulis semula ini, terdapat kelas abstrak BaseComponent, dan setiap kelas komponen memanjangkannya, jadi VideoComponent mempunyai logik untuk video, dsb.
Masalahnya ialah ia menjadi kucar-kacir dengan cepat.
Komponen bertanggungjawab ke atas cara ia dipaparkan, cara mereka mengurus tekstur Pixi mereka, cara ia dianimasikan dan banyak lagi.
Kini, hanya terdapat satu kelas komponen, yang sangat mudah.
Ini kini mempunyai empat peristiwa kitaran hayat:
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" version="1.1">${styleTag}<foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="transform-origin: 0 0;">${html}</div></foreignObject></svg>
Kelas komponen ini mempunyai kaedah yang dipanggil addHook yang mengubah tingkah lakunya.
Cangkuk boleh menyambung ke dalam peristiwa kitaran hayat komponen dan melakukan tindakan.
Sebagai contoh, terdapat MediaHook yang saya gunakan untuk komponen video dan audio.
MediaHook mencipta elemen audio atau video asas dan secara automatik memastikan ia segerak dengan garis masa utama.
Untuk komponen binaan, saya menggunakan corak pembina bersama-sama dengan corak pengarah (lihat rujukan).
Dengan cara ini, apabila membina komponen audio, saya menambah MediaHook padanya, yang juga saya tambahkan pada komponen video. Walau bagaimanapun, video juga memerlukan cangkuk tambahan untuk:
Pendekatan ini menjadikannya sangat mudah untuk menukar, melanjutkan atau mengubah suai logik pemaparan atau cara komponen berkelakuan dalam adegan.
Saya mencuba pelbagai pendekatan berbeza tentang cara untuk memaparkan video dengan cara yang paling pantas dan paling menjimatkan kos.
Pada tahun 2020, saya bermula dengan pendekatan yang paling mudah - memaparkan satu demi satu bingkai, yang merupakan sesuatu yang dilakukan oleh banyak alat.
Selepas beberapa percubaan-dan-ralat, saya beralih kepada pendekatan lapisan pemaparan.
Ini bermakna dokumen SceneData kami mengandungi lapisan yang mengandungi komponen.
Setiap lapisan ini dipaparkan secara berasingan dan kemudian digabungkan dengan ffmpeg untuk mencipta output akhir.
Hadnya ialah lapisan hanya boleh mengandungi komponen daripada jenis yang sama.
Sebagai contoh, lapisan dengan video tidak boleh mengandungi unsur teks; ia hanya boleh mengandungi video lain.
Ini jelas mempunyai kebaikan dan keburukan.
Agak mudah untuk memaparkan teks HTML dengan animasi pada Lambda secara bebas dan mengubahnya menjadi video lutsinar, yang kemudiannya digabungkan dengan potongan lain untuk output akhir.
Sebaliknya, lapisan dengan komponen video hanya diproses dengan ffmpeg.
Walau bagaimanapun, pendekatan ini mempunyai kelemahan yang besar.
Jika saya ingin melaksanakan sistem bingkai utama untuk menskala, memudar atau memutar video, saya perlu membuat port ciri ini dalam fluent-ffmpeg.
Itu pasti boleh dilakukan, tetapi dengan semua tanggungjawab lain yang saya ada, saya tidak berjaya.
Jadi saya memutuskan untuk kembali kepada pendekatan pertama - memaparkan satu demi satu bingkai.
Permintaan pemaparan dihantar ke pelayan bahagian belakang dengan Express.
Laluan ini menyemak sama ada video belum dipaparkan lagi dan jika tidak, ia akan ditambahkan ke dalam baris gilir BullMQ.
Selepas baris gilir mula memproses pemaparan, ia menghasilkan berbilang tika Chrome tanpa kepala.
Nota: pemprosesan ini berlaku pada pelayan Hetzner khusus dengan Pemproses 32 Teras AMD EPYC 7502P dan RAM 128 GB, jadi mesin ini cukup berprestasi.
Perlu diingat Chromium tidak mempunyai codec, jadi saya menggunakan Playwright yang menjadikannya remeh untuk memasang Chrome.
Tetapi tetap, bingkai video menjadi hitam atas sebab tertentu.
Saya pasti saya hanya kehilangan sesuatu; walau bagaimanapun, saya memutuskan untuk memisahkan komponen video kepada bingkai imej individu dan menggunakannya dalam penyemak imbas tanpa pelayan dan bukannya menggunakan video.
Tetapi tetap, bahagian yang paling penting ialah mengelak daripada menggunakan kaedah tangkapan skrin.
Memandangkan kami mempunyai segala-galanya dalam satu kanvas, kami boleh memasukkannya ke dalam imej dengan .getDataURL() pada kanvas, yang jauh lebih pantas.
Untuk menjadikannya lebih mudah, saya membuat halaman statik yang menggabungkan editor video dan menambahkan beberapa fungsi ke dalam tetingkap.
Ini kemudiannya dimuatkan dengan Penulis Drama/Puppeteer, dan pada setiap bingkai, saya hanya memanggil:
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" version="1.1">${styleTag}<foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="transform-origin: 0 0;">${html}</div></foreignObject></svg>
Ini memberi saya data bingkai yang sama ada boleh saya simpan sebagai imej atau tambah ke dalam penimbal untuk menjadikan bahagian video.
Keseluruhan proses ini dibahagikan kepada 5-10 pekerja berbeza, bergantung pada panjang video, yang digabungkan ke dalam output akhir.
Sebaliknya, ia boleh dimuat turun ke sesuatu seperti Lambda juga, tetapi saya cenderung menggunakan RunPod. Satu-satunya kelemahan seni bina tanpa pelayan mereka ialah mereka menggunakan Python, yang saya tidak begitu biasa.
Dengan cara ini, pemaparan mungkin dibahagikan kepada beberapa bahagian yang diproses di awan, malah pemaparan video 60 minit boleh dilakukan dalam satu atau dua minit. Bagus untuk dimiliki, tetapi itu bukan matlamat utama atau kes penggunaan kami.
Sebab saya menurunkan taraf daripada Pixi 8 kepada Pixi 7 adalah kerana Pixi 7 juga mempunyai versi "warisan" yang menyokong kanvas 2D. Ini JAUH lebih pantas untuk pemaparan. Video 60 saat mengambil masa kira-kira 80 saat untuk dipaparkan pada pelayan, tetapi jika kanvas mempunyai konteks WebGL atau WebGPU, saya hanya dapat memaparkan 1-2 bingkai sesaat.
Menariknya, Chrome tanpa pelayan jauh lebih perlahan berbanding Firefox yang penuh perhatian apabila memaparkan kanvas WebGL, menurut ujian saya.
Malah menggunakan GPU khusus tidak membantu mempercepatkan pemaparan dengan sebarang margin yang ketara. Sama ada saya melakukan sesuatu yang salah atau Chrome tanpa kepala tidak begitu berprestasi dengan WebGL.
WebGL dalam kes penggunaan kami bagus untuk peralihan, yang biasanya agak pendek.
Salah satu cara yang saya rancang untuk menguji tentang perkara ini ialah dengan memaparkan bahagian WebGL dan bukan WebGL secara berasingan.
Terdapat banyak bahagian yang terlibat dalam projek.
Data adegan disimpan pada MongoDB, kerana struktur dokumen paling masuk akal untuk disimpan dalam pangkalan data tanpa skema.
Halaman hadapan, ditulis dalam SvelteKit, menggunakan urql sebagai klien GraphQL.
Pelayan GraphQL menggunakan PHP Laravel dengan MongoDB dan Lighthouse GraphQL yang menakjubkan.
Tetapi ini adalah tema mungkin untuk kali seterusnya.
Jadi itu sahaja buat masa ini! Terdapat banyak kerja yang perlu dilakukan sebelum memasukkannya ke dalam pengeluaran dan menggantikan editor video semasa, yang agak bermasalah dan mengingatkan saya sedikit tentang Frankenstein.
Beri tahu saya apa yang anda fikirkan dan teruskan goncang!
Atas ialah kandungan terperinci Membina Editor Video TypeScript sebagai Pembangun Solo. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!