Hoje já é possível criar aplicativos PWA que funcionam dentro do browser e sem acesso à Internet. Entretanto costumava ser difícil realizar tarefas pesadas dentro do contexto do browser. Por exemplo, editar vídeos é mais fácil acessando alguma API que esteja rodando em um servidor remoto. O WebAssembly veio para suprir essa e outras necessidades, pois com ele é possível executar binários através do JavaScript direto do browser.
Com o WebAssembly é possível usar o pacote ffmpeg para converter vídeos em gifs sem a necessidade de qualquer servidor remoto. Incrível, né?
Vamos lá! Comecei criando um novo projeto via Angular CLI e depois instalei via NPM os pacotes do ffmpeg.
ng new wasm-video-to-gif cd wasm-video-to-gif npm install @ffmpeg/ffmpeg @ffmpeg/core --save
Também adicionei “node” na lista types dentro de compilerOptions do arquivo tsconfig.app.json.
{ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/app", "types": [ "node" ] }, "files": [ "src/main.ts", "src/polyfills.ts" ], "include": [ "src/**/*.d.ts" ] }
Para utilizar o ffmpeg é necessário primeiro carregar ele na memória.
import { Component, OnInit } from '@angular/core'; import { createFFmpeg, FFmpeg } from '@ffmpeg/ffmpeg'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit { ffmpeg: FFmpeg; isReady = false; async ngOnInit() { this.ffmpeg = createFFmpeg({ log: true }); await this.ffmpeg.load(); // Flag que indica que está pronto. this.isReady = true; } }
A primeira parte é permitir que o usuário selecione um arquivo de vídeo. Para isso eu utilizei o elemento <input type=”file”> e o evento change apontando para uma função chamada selectedFile.
<input *ngIf="isReady" type="file" (change)="selectedFile($event)">
// Atualizei os imports adicionando o fetchFile import { createFFmpeg, fetchFile, FFmpeg } from '@ffmpeg/ffmpeg'; gifUrlData: string; async selectedFile(event){ // Pegando arquivo do evento 'change' const videoFile: File = event.target.files?.item(0); // Carregando o arquivo de vídeo na memória em 'video.mp4' this.ffmpeg.FS('writeFile', 'video.mp4', await fetchFile(videoFile)); // Executando o comando do FFMpeg para converter 'video.mp4' para 'video.gif' (2,5 segundos de duração) await this.ffmpeg.run('-i', 'video.mp4', '-t', '2.5', '-ss', '2.0', '-f', 'gif', 'video.gif'); // Lendo resultado de 'video.gif' const gifData = this.ffmpeg.FS('readFile', 'video.gif'); // Criando uma URL com dados do gif this.gifUrlData = URL.createObjectURL(new Blob([gifData.buffer], { type: 'image/gif' })); }
O progresso da conversão aparece no console do browser.
Ao executar a função, a propriedade gifUrlData ficou com a URL do blob do gif. Para renderizar o gif utilizei o elemento img, mas, para poder usar essa URL do gif no Angular , eu tive que “higienizar” (sanatize) ela antes.
// ... import { DomSanitizer } from '@angular/platform-browser'; // ... export class AppComponent implements OnInit { // ... constructor( private domSanitizer: DomSanitizer ) { } // ... sanitize(url: string): SafeUrl { return this.domSanitizer.bypassSecurityTrustUrl(url); } }
<img *ngIf="gifUrlData" [src]="sanitize(gifUrlData)" style="max-width: 500px;">
Para que a aplicação funcione como um PWA eu tive que adicionar via Angular CLI a lib @angular/pwa.
ng add @angular/pwa --project wasm-video-to-gif
Ficou pronto! Agora falta só publicar em algum ambiente HTTPS.
Com WebAssembly é possível realizar processamentos pesados direto do browser. É possível, por exemplo, utilizar ffmpeg para criar um editor de vídeos mais completo que funcione no navegador e sem servidor remoto. Ao transformar a aplicação em PWA é possível que o usuário utilize mesmo off-line.
O repositório da aplicação está disponível no GitHub.
Veja funcionando você mesmo clicando aqui.
Espero ter ajudado! 😉
Texto objetivo e didático ao mesmo tempo, parabéns pela iniciativa em dispensar um tempo para passar conhecimento adiante e pela clareza e competência com que o faz 🙂
Muito interessante. Não sabia de todas essas possibilidades assim… Pode ajudar mto em diversos serviços