Sto scrivendo un codice C++ in cui viene generata una sequenza di N frame diversi dopo aver eseguito alcune operazioni in esso implementate. Dopo aver completato ogni frame, lo scrivo sul disco come IMG_% d.png e infine li codifico in un video tramite ffmpeg usando il codec x264.Come codificare un video da diverse immagini generate in un programma C++ senza scrivere le immagini del frame separate su disco?
Lo pseudo riepilogativo della parte principale del programma è la seguente:
std::vector<int> B(width*height*3);
for (i=0; i<N; i++)
{
// void generateframe(std::vector<int> &, int)
generateframe(B, i); // Returns different images for different i values.
sprintf(s, "IMG_%d.png", i+1);
WriteToDisk(B, s); // void WriteToDisk(std::vector<int>, char[])
}
Il problema di questa implementazione è che il numero di fotogrammi desiderati, N, è generalmente elevato (N ~ 100000) come così come la risoluzione delle immagini (1920x1080), risultante in un sovraccarico del disco, producendo cicli di scrittura di dozzine di GB dopo ogni esecuzione.
Per evitare questo, ho cercato di trovare la documentazione sull'analisi di ogni immagine memorizzata nel vettore B su un encoder come x264 (senza dover scrivere i file di immagine intermedi sul disco). Anche se sono stati trovati alcuni argomenti interessanti, nessuno di loro ha risolto in modo specifico quello che voglio esattamente, dato che molti riguardano l'esecuzione dell'encoder con i file di immagini esistenti sul disco, mentre altri forniscono soluzioni per altri linguaggi di programmazione come Python (here tu può trovare una soluzione pienamente soddisfacente per quella piattaforma).
Il pseudocodice di quello che vorrei ottenere è qualcosa di simile a questo:
std::vector<int> B(width*height*3);
video_file=open_video("Generated_Video.mp4", ...[encoder options]...);
for (i=0; i<N; i++)
{
generateframe(B, i+1);
add_frame(video_file, B);
}
video_file.close();
Secondo quello che ho letto su argomenti correlati, la x264 C++ API potrebbe essere in grado di fare questo, ma, come detto sopra, non ho trovato una risposta soddisfacente per la mia domanda specifica. Ho provato ad imparare e usare direttamente il codice sorgente ffmpeg, ma sia la sua bassa facilità d'uso che i miei problemi di compilazione mi hanno costretto a scartare questa possibilità come un semplice programmatore non professionista che sono (lo prendo come un semplice hobby e sfortunatamente non posso sprecare che molte volte imparano qualcosa di così impegnativo).
Un'altra possibile soluzione che mi è venuta in mente è trovare un modo per chiamare il file binario ffmpeg nel codice C++ e in qualche modo riuscire a trasferire i dati dell'immagine di ciascuna iterazione (memorizzata in B) all'encoder, lasciando che il aggiunta di ogni frame (cioè, non "chiudendo" il file video da scrivere) fino all'ultimo frame, in modo da poter aggiungere più frame fino a raggiungere l'N-esimo, in cui il file video sarà "chiuso". In altre parole, chiama ffmpeg.exe tramite il programma C++ per scrivere il primo fotogramma su un video, ma fai in modo che l'encoder "aspetti" per più fotogrammi. Quindi chiama di nuovo ffmpeg per aggiungere il secondo frame e fare in modo che l'encoder "attenda" di nuovo per altri frame, e così via fino a raggiungere l'ultimo frame, dove il video sarà finito. Tuttavia, non so come procedere o se è effettivamente possibile.
Edit 1:
Come suggerito nelle risposte, mi sono documentato su named pipe e ha cercato di usarle nel mio codice. Prima di tutto, si dovrebbe notare che sto lavorando con Cygwin, quindi i miei pipe nominati vengono creati così come sarebbero creati sotto Linux. Il pseudocodice modificata ho usato (comprese le librerie di sistema corrispondenti) è la seguente:
FILE *fd;
mkfifo("myfifo", 0666);
for (i=0; i<N; i++)
{
fd=fopen("myfifo", "wb");
generateframe(B, i+1);
WriteToPipe(B, fd); // void WriteToPipe(std::vector<int>, FILE *&fd)
fflush(fd);
fd=fclose("myfifo");
}
unlink("myfifo");
WriteToPipe è una leggera modifica della funzione WriteToFile precedente, dove ho fatto in modo che il buffer di scrittura per inviare i dati di immagine è piccola abbastanza per adattarsi ai limiti del buffering del tubo.
Poi posso compilare e scrivere il seguente comando nel terminale Cygwin:
./myprogram | ffmpeg -i pipe:myfifo -c:v libx264 -preset slow -crf 20 Video.mp4
Tuttavia, rimane bloccato al ciclo quando i = 0 alla linea "fopen" (cioè, la prima chiamata fopen). Se non avessi chiamato ffmpeg sarebbe naturale come il server (il mio programma) sarebbe in attesa di un programma client per connettersi all '"altro lato" del tubo, ma non è il caso. Sembra che non possano essere collegati attraverso il tubo in qualche modo, ma non sono stato in grado di trovare ulteriore documentazione al fine di superare questo problema. Qualche suggerimento?
Hai provato a usare named pipe? Per quanto riguarda FFMPEG, può accettare pipe denominate come input -i pipe: pipe_name [Esempio su msdn.microsoft] (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365592 (v = vs. 85) .aspx) –
Grazie per il suggerimento. Ho imparato a conoscere le pipe con nome e ho cercato di passare attraverso quella strada. I nuovi problemi che sono apparsi dopo aver provato questo metodo sono esposti nella mia nuova modifica. – ksb496