Wie zum codieren eines video aus mehreren Bildern generiert, die in einem C++ - Programm ohne separaten Rahmen, Bilder auf die Festplatte?
Ich Schreibe ein C++ - code, in dem eine Sequenz von N verschiedenen frames generiert wird, nach der Durchführung einige Operationen implementiert darin. Nach jedem frame abgeschlossen ist, Schreibe ich es auf die Festplatte als IMG_%d....png, und schließlich ich encodieren Sie ein video durch ffmpeg mit dem x264 codec.
Den pseudocode zusammengefasst der wichtigste Teil des Programms ist das folgende:
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[])
}
Das problem dieser Implementierung ist, dass die Nummer des gewünschten frames, N, ist in der Regel hoch (N~100000) sowie die Auflösung der Bilder (1920 x 1080), was in einer überlastung der Festplatte, Herstellung von schreib-Zyklen von Dutzenden von GB nach jeder Ausführung.
Um dies zu vermeiden, habe ich versucht zu finden-Dokumentation über die Analyse direkt jedes Bild gespeichert, im Vektor B an einen encoder wie x264 (ohne zu schreiben, die intermediate-image-Dateien auf der Festplatte). Obgleich einige interessante Themen gefunden wurden, keiner von Ihnen gelöst konkret, was ich genau will, wie viele von Ihnen betreffen die Ausführung der encoder mit den bestehenden Bilder-Dateien auf der Festplatte, während andere Lösungen für andere Programmiersprachen wie z.B. Python (hier finden Sie eine voll befriedigende Lösung für diese Plattform).
Den pseudocode von dem, was ich möchte, zu erhalten, ist etwas ähnlich wie diese:
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();
Je nach dem was ich gelesen habe, auf Verwandte Themen, die x264-C++ - API in der Lage sein könnte, dies zu tun, aber, wie oben gesagt, ich habe nicht gefunden, eine befriedigende Antwort für meine spezielle Frage. Ich habe versucht, zu lernen und direkt mit dem ffmpeg-Quellcode, aber sowohl seine geringe Benutzerfreundlichkeit und Zusammenstellung der Themen hat mich gezwungen Sie zu verwerfen, diese Möglichkeit als eine bloße nicht-Profi-Programmierer bin ich (ich nehme es als nur ein hobby und leider kann ich nicht Abfälle, die viele Zeit, etwas zu lernen so anspruchsvoll).
Andere mögliche Lösung, die mir in den Sinn gekommen ist einen Weg zu finden, rufen Sie die ffmpeg-binary Datei in den C++ - code, und es irgendwie schaffen, zur übertragung der Bild Daten jeder iteration (gespeichert in B) auf den encoder, so dass der Zusatz von jedem frame (das heißt, nicht "schließen" die video-Datei zu schreiben), bis Sie den letzten frame, so dass frames können Hinzugefügt werden, bis das erreichen der N-TEN ein, wo Sie die video-Datei wird "geschlossen". In anderen Worten, rufen Sie ffmpeg.exe durch das C++ - Programm zu schreiben, das erste Bild zu einem video, aber das machen die encoder "warten" für mehr frames. Dann wieder anrufen, ffmpeg, um das zweite Bild und das encoder "warten" wieder für mehr frames und so weiter, bis Sie den letzten frame, wo das video fertig sein wird. Allerdings, ich weiß nicht, wie es weitergehen soll oder ob es eigentlich möglich ist.
Edit 1:
Wie vorgeschlagen-in die Antworten, ich habe bereits dokumentiert über named pipes und versucht, Sie in meinem code. Zuerst von allen, es sollte angemerkt werden, dass ich arbeite mit Cygwin, so dass meine named pipes werden erzeugt, wie Sie entstehen würden, unter Linux. Die geänderten pseudocode I verwendet werden (einschließlich der entsprechenden system-Bibliotheken) ist die folgende:
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 ist eine leichte Modifikation der früheren WriteToFile-Funktion, wo ich sicher, dass der schreib-Puffer zum senden der Bilddaten ist klein genug, um das Rohr Pufferung Einschränkungen.
Ich dann kompilieren und schreiben Sie den folgenden Befehl in die Cygwin-terminal:
./myprogram | ffmpeg -i pipe:myfifo -c:v libx264 -preset slow -crf 20 Video.mp4
Jedoch, bleibt Sie fest an der Schleife, wenn i=0 an die "fopen" - Zeile (also die erste fopen-Aufruf). Hätte ich nicht angerufen ffmpeg wäre es natürlich, wenn die server (mein Programm) auf Sie wartet ein client-Programm eine Verbindung zu der "anderen Seite" der Leitung, aber es ist nicht der Fall. Es sieht aus wie Sie können nicht angeschlossen werden, durch die Leitung irgendwie, aber ich habe nicht in der Lage zu finden, eine weitere Dokumentation, um zu überwinden dieses Problem. Jede Anregung?
- Haben Sie versucht, verwendet named pipe? Für FFMPEG kann es akzeptieren, named pipes als input-i pipe:von pipe_name Beispiel auf der msdn-Website.microsoft
- Danke für die Anregung. Ich lernte über named pipes und versuchte zu gehen auf diesem Weg. Die neuen Themen, die erschien nach dem Versuch diese Methode ausgesetzt sind, in meinem neuen zu Bearbeiten.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Nach einigen intensiven Kampf habe ich es endlich geschafft, es zu machen, arbeiten nach dem lernen ein bisschen wie die FFmpeg und libx264 C-APIs für meinen spezifischen Zweck, Dank der nützlichen Informationen, die einige Benutzer in dieser Website und einige andere, sowie einige, die FFmpeg Dokumentation Beispiele. Für die illustration, die details werden im folgenden vorgestellt.
Zunächst die libx264-C-Bibliothek kompiliert wurde, und, danach, die FFmpeg mit den configure-Optionen --enable-gpl --enable-libx264. Jetzt lassen Sie uns gehen, um die Codierung. Der relevante Teil des Codes erreicht werden, dass die angeforderten Zweck ist das folgende:
Umfasst:
LDFLAGS auf Makefile:
Inner code (der Einfachheit halber, wird der Fehler checkings wird weggelassen werden, und die Deklarationen von Variablen erfolgt dann, wenn notwendig, anstelle der Anfang zum besseren Verständnis):
Den oben kommentiert Vektor hat die gleiche Struktur als die, die ich ausgesetzt in meine Frage; jedoch die RGB-Daten werden auf der AVFrames in einer bestimmten Weise. Daher, im Interesse der Ausstellung, lassen Sie uns annehmen, wir haben statt dessen einen Zeiger auf eine Struktur der form uint8_t[3] Matrix(int, int), dessen Zugriff auf die Farbwerte der Pixel für eine bestimmte Koordinate (x, y) Matrix(x, y)->Rot, Matrix(x, y)->Green und Matrix(x, y)->Blau, um zu bekommen, bzw. auf der roten, grün-und blau-Werte der Koordinate (x, y). Das erste argument steht für die horizontale position, von Links nach rechts als x erhöht und der zweite für die vertikale position, von oben nach unten als y erhöht.
Wesen, das sagte, die für Schleife um die Daten zu übertragen, codieren und schreiben Sie jeden frame, wäre die folgende:
Seite Hinweise:
Für zukünftige Referenz, da die verfügbaren Informationen auf dem Netz über den Zeitstempel (PTS/DTS) sieht so verwirrend, werde ich weiter erklären, so gut wie ich Tat, zu verwalten, um die Probleme zu lösen, durch die Einstellung der richtigen Werte. Setzt man diese Werte fälschlicherweise verursacht, dass die Ausgabe-Größe war viel größer als die, die durch die ffmpeg gebaut binäre Kommandozeilen-tool, da die frame-Daten werden Redundant geschrieben durch kleinere Zeitintervalle als die tatsächlich durch die FPS.
Zuerst von all, es sollte angemerkt werden, dass bei der Codierung gibt es zwei Arten von Zeitmarken: ein Zusammenhang zu dem Rahmen (PTS) (pre-encoding-Stufe) und zwei zugeordnet, wird das Paket (PTS und DTS) (nach der Kodierung der Bühne). Im ersten Fall sieht es so aus das frame PTS-Werte zugeordnet werden können, mithilfe einer benutzerdefinierten Einheit der Referenz (mit der einzigen Einschränkung, dass Sie werden müssen, in gleichem Abstand, wenn man will dem Konstanten FPS), so kann man zum Beispiel die Nummer des Bildes, wie wir es im obigen code. In der zweiten, wir haben berücksichtigt die folgenden Parameter:
Der Schlüssel hier ist, zum Glück ist es nicht notwendig, zu kämpfen mit der Berechnung dieser Mengen, als libav enthält eine Funktion zum berechnen der korrekten Zeitstempel zugeordnet, um das Paket durch die Kenntnis der oben genannten Daten:
Danke für diese überlegungen, ich war schließlich in der Lage, erzeugen ein vernünftiger output-container und im wesentlichen die gleiche Kompressionsrate als die, die mit dem commandline-tool, das waren die zwei verbleibenden Fragen vor der Untersuchung noch tiefer, wie das format der header-und trailer-und wie die Zeit, die Stempel sind richtig eingestellt.
Vielen Dank für Ihre hervorragende Arbeit, @ksb496 !
Eine kleine Verbesserung:
sollte besser so geschrieben:
um Speicherlecks zu vermeiden.
Wenn Euch das nichts ausmacht, ich habe hochgeladen, die komplette ready-to-deploy-Bibliothek auf GitHub: https://github.com/apc-llc/moviemaker-cpp.git
Dank ksb496 habe ich es geschafft, das zu tun diese Aufgabe, aber in meinem Fall muss ich einiges ändern-codes wie erwartet arbeiten. Ich dachte, vielleicht könnte es anderen helfen, also habe ich beschlossen, zu teilen (mit zwei Jahren Verzögerung
:D
).Hatte ich eine
RGB
- Puffer gefüllt, die von directshow sample grabber, die ich brauchte, um ein video aus.RGB
zuYUV
Umwandlung von gegebenen Antwort nicht die Arbeit zu tun für mich. Ich habe es wie folgt aus :data
variable hier ist meinRGB
- Puffer (einfachBYTE*
) undsize
istdata
Puffergröße in bytes. Es ist start-FüllungRGB
AVFrame
von Links unten nach rechts oben.Die andere Sache ist, dass meine version von FFMPEG nicht
av_packet_rescale_ts
Funktion. Es ist die Letzte version, aber FFMPEG-docs nicht sagen, diese Funktion ist veraltet und überall, ich denke, dies könnte der Fall sein, nur für windows. Jedenfalls habe ich verwendetav_rescale_q
statt, dass macht die gleiche Arbeit. wie diese :Und die Letzte Sache, mit diesem format-Konvertierung, die ich brauchte, um zu ändern, mein
swsContext
zuBGR24
stattRGB24
wie diese :