FFmpeg ist ein großartiges Open Source-Projekt, eine Art Multimedia-Enzyklopädie. Mit FFmpeg können Sie eine Vielzahl von Computer-Multimedia-Aufgaben lösen. Trotzdem besteht manchmal die Notwendigkeit, FFmpeg zu erweitern. Die Standardmethode besteht darin, Änderungen am Projektcode vorzunehmen und anschließend die neue Version zu kompilieren. Der Artikel beschreibt, wie ein neuer Codec hinzugefügt wird. Einige Funktionen zum Anschließen externer Funktionen an FFmpeg werden ebenfalls berücksichtigt. Wenn Sie keinen Codec hinzufügen müssen, kann der Artikel hilfreich sein, um die Architektur von FFmpeg-Codecs und ihre Einstellungen besser zu verstehen. Es wird davon ausgegangen, dass der Leser mit der Architektur von FFmpeg, dem Kompilierungsprozess von FFmpeg, vertraut ist und über Programmiererfahrung mit der FFmpeg-API verfügt. Die Beschreibung gilt für FFmpeg 4.2 "Ada", August 2019.
Inhaltsverzeichnis
Einleitung
Der Codec (Codec, stammt aus der Kombination der Begriffe COder und DECoder) ist ein sehr gebräuchlicher Begriff und seine Bedeutung variiert, wie in solchen Fällen häufig, je nach Kontext etwas. Die primäre Bedeutung ist Software oder Hardware zum Komprimieren / Dekomprimieren von Mediendaten. Anstelle der Begriffe Komprimierung / Dekomprimierung werden häufig die Begriffe Kodierung / Dekodierung verwendet. In einigen Fällen wird unter einem Codec jedoch normalerweise einfach ein Komprimierungsformat verstanden (sie bezeichnen auch das Codec-Format), unabhängig von den für die Komprimierung / Dekomprimierung verwendeten Mitteln. Mal sehen, wie der Begriff Codec in FFmpeg verwendet wird.
1. Codec-Identifikation
FFmpeg-Codecs werden in der libavcodec- Bibliothek kompiliert.
1.1. Codec-ID
Die enum AVCodecID
in der enum AVCodecID
libavcodec/avcodec.h
definiert. Jedes Element dieser Aufzählung gibt das Komprimierungsformat an. Elemente dieser Aufzählung müssen die Form AV_CODEC_ID_XXX
, wobei XXX
eindeutige Codec- AV_CODEC_ID_XXX
Name in Großbuchstaben ist. Hier sind Beispiele für Codec- AV_CODEC_ID_H264
: AV_CODEC_ID_H264
, AV_CODEC_ID_AAC
. Für eine detailliertere Beschreibung der Codec- AVCodecDescriptor
verwenden Sie die AVCodecDescriptor
Struktur (deklariert in libavcodec/avcodec.h
, abgekürzt):
typedef struct AVCodecDescriptor { enum AVCodecID id; enum AVMediaType type; const char *name; const char *long_name;
Das Schlüsselelement dieser Struktur ist id
. Die übrigen Elemente enthalten zusätzliche Informationen zur Codec- id
. Jeder Codec-Bezeichner ist eindeutig einem Medientyp ( type
) zugeordnet und verfügt über einen eindeutigen Namen ( name
), der in Kleinbuchstaben geschrieben ist. Ein Array vom Typ AVCodecDescriptor
in der Datei libavcodec/codec_desc.c
AVCodecDescriptor
. Für jede Codec-ID gibt es ein entsprechendes Array-Element. Elemente dieses Arrays sollten nach id
Werten sortiert werden, da die binäre Suche für die Suche nach Elementen verwendet wird. Um Informationen über die Codec-ID zu erhalten, können Sie die folgenden Funktionen verwenden:
const AVCodecDescriptor* avcodec_descriptor_get(enum AVCodecID id); const AVCodecDescriptor* avcodec_descriptor_get_by_name(const char *name); enum AVMediaType avcodec_get_type(enum AVCodecID codec_id); const char* avcodec_get_name(enum AVCodecID id);
1.2. Codec
Der Codec selbst - eine Reihe von Tools, die zum Codieren / Decodieren von Mediendaten erforderlich sind, kombiniert die AVCodec
Struktur (deklariert in libavcodec/avcodec.h
). Hier ist seine gekürzte Version, die weiter unten noch ausführlicher besprochen wird.
typedef struct AVCodec { const char *name; const char *long_name; enum AVMediaType type; enum AVCodecID id;
Das wichtigste Mitglied dieser Struktur ist id
, die Codec- id
Es gibt auch ein Mitglied, das den Medientyp ( type
) definiert, dessen Wert jedoch mit dem Wert desselben Mitglieds aus AVCodecDescriptor
. Codecs werden in zwei Kategorien unterteilt: Codierer, die Medien komprimieren oder codieren, und Decodierer, die den umgekehrten Vorgang ausführen - Dekomprimieren oder Decodieren. (In russischen Texten verwendet der Encoder manchmal anstelle des Begriffs AVCodec
aus dem Englischen - dem Encoder.) In AVCodec
gibt es kein spezielles AVCodec
, das die Codec-Kategorie definiert (obwohl die Kategorie indirekt mit den Funktionen av_codec_is_encoder()
und av_codec_is_decoder()
, wird diese Kategorie bei der Registrierung festgelegt. Wie dies gemacht wird, wird im Folgenden gezeigt: Mehrere Codecs können dieselbe Codec-ID haben. Wenn sie dieselbe Kategorie haben, müssen sie sich nach dem Namen (Mitgliedsnamen) unterscheiden. Ein Codierer und ein Decodierer, die dieselbe Codec-ID haben, können dies einen haben Derselbe Name, der auch mit dem Namen der Codec-ID übereinstimmen kann (diese Übereinstimmungen sind jedoch optional.) In einer solchen Situation kann es zu Verwirrung kommen, aber es ist nichts zu tun, Sie müssen klar verstehen, zu welcher Entität der Name gehört Der Codec muss eindeutig sein. Um nach registrierten Codecs zu suchen, gibt es Funktionen:
AVCodec* avcodec_find_encoder_by_name(const char *name); AVCodec* avcodec_find_decoder_by_name(const char *name); AVCodec* avcodec_find_encoder(enum AVCodecID id); AVCodec* avcodec_find_decoder(enum AVCodecID id);
Da mehrere Codecs denselben Bezeichner haben können, geben die letzten beiden Funktionen einen von ihnen zurück, der als Standard-Codec für einen bestimmten Codec-Bezeichner angesehen werden kann.
Mit dem Befehl kann eine Liste aller registrierten Codecs angefordert werden
ffmpeg -codecs >codecs.txt
Nach dem Ausführen des Befehls enthält die Datei codecs.txt
diese Liste. Jede Codec-ID wird durch einen separaten Datensatz (Zeile) dargestellt. Hier zum Beispiel der Eintrag für die Codec-Kennung AV_CODEC_ID_H264
:
DEV.LS
h264
H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
(decoders: h264 h264_qsv h264_cuvid)
(encoders: libx264 libx264rgb h264_amf h264_nvenc h264_qsv nvenc nvenc_h264)
Zu Beginn der Aufnahme stehen spezielle Zeichen zur Verfügung, die die gemeinsamen Merkmale dieser Codec-Kennung bestimmen: D
- registrierte Decoder, E
- registrierte Encoder, V
- für Video verwendet, L
- Möglichkeit einer verlustbehafteten Komprimierung, S
- Möglichkeit einer verlustfreien Komprimierung. Als nächstes folgt der Name der Codec- h264
( h264
), gefolgt von einem langen Namen der Codec- h264
( H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
) und einer Liste der Namen registrierter Decoder und Encoder.
2. Hinzufügen eines neuen Codecs zu FFmpeg
Wir werden die Prozedur zum Hinzufügen eines neuen Codecs zu FFmpeg am Beispiel eines Audio-Codecs betrachten, den wir FROX
nennen.
Schritt 1. Fügen Sie der enum AVCodecID
ein neues Element enum AVCodecID
.
Diese Auflistung befindet sich in der libavcodec/avcodec.h
. Beim Hinzufügen müssen Sie die folgenden Regeln beachten:
- Der Wert eines Elements darf nicht mit den Werten vorhandener Aufzählungselemente übereinstimmen.
- Ändern Sie nicht die Werte vorhandener Aufzählungselemente.
- Veröffentlichen Sie einen neuen Wert in einer Gruppe ähnlicher Codecs.
Gemäß der Vorlage sollte der Bezeichner dieses Elements AV_CODEC_ID_FROX
. AV_CODEC_ID_PCM_S64LE
es vor AV_CODEC_ID_PCM_S64LE
und geben Sie den Wert 0x10700
.
Schritt 2. Fügen Sie das Element dem Array codec_descriptors
(Datei libavcodec/codec_desc.c
).
static const AVCodecDescriptor codec_descriptors[] = {
Sie müssen das Element an der richtigen Stelle einfügen. Die Monotonie der Array-Elemente durch den id
Wert sollte nicht verletzt werden.
Schritt 3. Definieren Sie die Instanzen von AVCodec
separat für den Encoder und den Decoder.
Dazu müssen Sie zunächst die Struktur für den Kontext des Codecs und mehrere Funktionen festlegen, mit denen die eigentliche Codierung / Decodierung und einige andere erforderliche Vorgänge ausgeführt werden. In diesem Abschnitt werden diese Definitionen äußerst schematisch vorgenommen, eine detailliertere Beschreibung erfolgt später. Wir werden den Code in die Datei libavcodec/frox.c
#include "avcodec.h"
Der Einfachheit halber haben in diesem Beispiel der Codierer und der Decodierer denselben Kontext - FroxContext
, aber meistens haben der Codierer und der Decodierer unterschiedliche Kontexte. Beachten Sie auch, dass AVCodec
einem speziellen Muster folgen müssen.
Schritt 4. Fügen AVCodec
der Registrierungsliste Instanzen von AVCodec
.
Gehen Sie zur Datei libavcodec/allcodecs.c
. Am Anfang dieser Datei befindet sich eine Liste der Deklarationen aller registrierten Codecs. Fügen Sie unsere Codecs zu dieser Liste hinzu:
extern AVCodec ff_frox_decoder; extern AVCodec ff_frox_encoder;
Während der Ausführung findet das configure
Skript alle derartigen Deklarationen und generiert die libavcodec/codec_list.c
, die ein Array von Zeigern auf die in libavcodec/allcodecs.c
deklarierten Codecs libavcodec/allcodecs.c
. Nach dem Ausführen des Skripts in der Datei libavcodec/codec_list.c
wir:
static const AVCodec * const codec_list[] = {
Während der Ausführung des configure
Skripts wird auch die config.h
Datei config.h
, in der sich die Deklarationen befinden
#define CONFIG_FROX_DECODER 1 #define CONFIG_FROX_ENCODER 1
Schritt 5. Bearbeiten Sie libavcodec/Makefile
Öffnen Sie libavcodec/Makefile
. Wir finden den Abschnitt # decoders/encoders
und fügen dort hinzu
OBJS-$(CONFIG_FROX_DECODER) += frox.o OBJS-$(CONFIG_FROX_ENCODER) += frox.o
Schritt 6. Bearbeiten Sie den Code des Multiplexers und Demultiplexers.
Der Multiplexer (Muxer) und der Demultiplexer (Demuxer) müssen den neuen Codec „kennen“. Beim Aufzeichnen ist es erforderlich, die Identifizierungsinformationen für diesen Codec aufzuzeichnen, während beim Lesen die Kennung des Codec aus den Identifizierungsinformationen ermittelt wird. matroska
müssen Sie für das matroska
Format ( *.mkv
) tun.
1. libavformat/matroska.c
in der Datei libavformat/matroska.c
dem Array libavformat/matroska.c
ein Element für den neuen Codec libavformat/matroska.c
:
const CodecTags ff_mkv_codec_tags[] = {
String "A_FROX"
und wird vom Multiplexer als Identifikationsinformation in die Datei geschrieben. In diesem Array ist es der Codec-Kennung zugeordnet, daher kann der Demultiplexer sie beim Lesen leicht ermitteln. Der Demultiplexer schreibt die Codec- codec_id
Mitglied codec_id
Struktur codec_id
. Ein Zeiger auf diese Struktur ist ein Mitglied der AVStream
Struktur.
2. libavformat/matroskaenc.c
in der Datei libavformat/matroskaenc.c
das Element zum Array additional_audio_tags
:
static const AVCodecTag additional_audio_tags[] = {
Also ist alles fertig. Führen Sie zuerst das configure
. Danach müssen Sie sicherstellen, dass die oben beschriebenen Änderungen in den Dateien libavcodec/codec_list.c
und config.h
vorgenommen werden. Dann können Sie die Kompilierung ausführen:
make clean
make
Wenn die Kompilierung ffmpeg.exe
, wird die ausführbare Datei ffmpeg
(oder ffmpeg.exe
, wenn das ffmpeg.exe
Windows ist) angezeigt. Führen Sie den Befehl aus
./ffmpeg -codecs >codecs.txt
und stellen Sie sicher, dass FFmpeg unsere neuen Codecs "sieht", wir finden den Eintrag in der Datei codecs.txt
DEA..S frox FROX audio (decoders: frox_dec) (encoders: frox_enc)
3. Detaillierte Beschreibung des Kontexts und der erforderlichen Funktionen
In diesem Abschnitt beschreiben wir detaillierter, wie die Struktur des Codec-Kontexts und die erforderlichen Funktionen aussehen können.
3.1. Codec-Kontext
Der Codec-Kontext unterstützt möglicherweise die Installation von Optionen. Bei Encodern wird diese Unterstützung häufig genug verwendet, bei Decodern seltener. Die Struktur, die die Installation von Optionen unterstützt, sollte als erstes Mitglied einen Zeiger auf die AVClass
Struktur und dann auf die Optionen selbst haben.
#include "libavutil/opt.h" typedef struct FroxContext { const AVClass *av_class; int frox_int; char *frox_str; uint8_t *frox_bin; int bin_size; } FroxContext;
Als nächstes müssen Sie ein Array vom Typ AVOption
, von dem jedes Element eine bestimmte Option beschreibt.
static const AVOption frox_options[] = { { "frox_int", "This is a demo option of int type.", offsetof(FroxContext, frox_int), AV_OPT_TYPE_INT, { .i64 = -1 }, 1, SHRT_MAX }, { "frox_str", "This is a demo option of string type.", offsetof(FroxContext, frox_str), AV_OPT_TYPE_STRING }, { "frox_bin", "This is a demo option of binary type.", offsetof(FroxContext, frox_bin), AV_OPT_TYPE_BINARY }, { NULL }, };
Für jede Option müssen Sie einen Namen, eine Beschreibung, einen Versatz in der Struktur und einen Typ definieren. Sie können auch einen Standardwert und für Ganzzahloptionen einen Bereich gültiger Werte definieren.
Als Nächstes müssen Sie eine Instanz vom Typ AVClass
.
static const AVClass frox_class = { .class_name = "FroxContext", .item_name = av_default_item_name, .option = frox_options, .version = LIBAVUTIL_VERSION_INT, };
Ein Zeiger auf diese Instanz muss verwendet werden, um das entsprechende AVCodec
Mitglied zu initialisieren.
AVCodec ff_frox_decoder = {
Jetzt beim Ausführen der Funktion
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
Eine Instanz der AVCodecContext
Struktur wird AVCodecContext
und das codec
Mitglied wird initialisiert. Als nächstes wird basierend auf dem Wert codec->priv_data_size
der erforderliche Speicher für die FroxContext
Instanz zugewiesen, wobei der Wert codec->priv_class
erste Mitglied dieser Instanz wird initialisiert und anschließend die Funktion av_opt_set_defaults()
aufgerufen, mit der die Standardwerte für die Optionen festgelegt werden. Ein Zeiger auf eine Instanz von FroxContext
ist über das Mitglied priv_data
Struktur FroxContext
verfügbar.
Bei der Arbeit mit der FFmpeg-API können Werte für Optionen direkt festgelegt werden.
const AVCodec *codec;
Eine andere Möglichkeit ist die Verwendung des avcodec_open2()
, das beim Aufruf von avcodec_open2()
als drittes Argument avcodec_open2()
(siehe unten).
Funktion verwenden
const AVOption* av_opt_next(const void* ctx, const AVOption* prev);
Sie können eine Liste aller vom Codec-Kontext unterstützten Optionen abrufen. Dies ist nützlich, wenn Sie einen Codec untersuchen. Vorher müssen Sie jedoch sicherstellen, dass codec_ctx->codec->priv_class
auf einen Wert ungleich Null gesetzt ist, da der Kontext sonst keine Optionen unterstützt und jede Operation mit Optionen das Programm zum Absturz bringt.
3.2. Funktionen
Lassen Sie uns nun genauer untersuchen, wie die Funktionen, die bei der Initialisierung des Codecs und der tatsächlichen Codierung / Decodierung verwendet werden, angeordnet sind. Normalerweise müssen sie immer einen Zeiger auf einen FroxContext
.
AVCodecContext *codec_ctx;
Die Funktionen frox_decode_init()
und frox_encode_init()
werden aufgerufen, wenn die Funktion ausgeführt wird
int avcodec_open2( AVCodecContext *codec_ctx, const AVCodec *codec, AVDictionary **options);
Sie müssen die erforderlichen Ressourcen zuweisen, damit der Codec funktioniert, und bei Bedarf einige Mitglieder der AVCodecContext
Struktur initialisieren, z. B. frame_size
für einen Audio- frame_size
.
Die Funktionen frox_decode_close()
und frox_encode_close()
werden bei der Ausführung aufgerufen
int avcodec_close(AVCodecContext *codec_ctx);
Sie müssen die zugewiesenen Ressourcen freigeben.
Betrachten Sie eine Funktion zum Implementieren der Decodierung
int frox_decode( AVCodecContext *codec_ctx, void *outdata, int *outdata_size, AVPacket *pkt);
Sie sollte die folgenden Operationen implementieren:
- Tatsächliche Dekodierung;
- Zuweisung des notwendigen Puffers für den Ausgaberahmen;
- Kopieren Sie dekodierte Daten in den Bildspeicher.
Überlegen Sie, wie Sie den erforderlichen Puffer für den Ausgaberahmen zuweisen. Der Parameter outdata
verweist tatsächlich auf einen AVFrame
. Sie müssen also zuerst eine Typkonvertierung durchführen:
AVFrame* frm = outdata;
Als nächstes müssen Sie einen Puffer zum Speichern von Rahmendaten zuweisen. Initialisieren Sie dazu die AVFrame
Mitglieder, die die AVFrame
bestimmen. Für Audio ist dies nb_samples
, channel_layout
, format
(für nb_samples
, nb_samples
und channel_layout
).
Danach müssen Sie die Funktion aufrufen
int av_frame_get_buffer(AVFrame* frm, int alignment);
Der Zeiger auf den Frame, bei dem es sich um den konvertierten outdata
Parameter handelt, wird als erstes Argument verwendet, und es wird empfohlen, als zweites Argument Null zu übergeben. Nach Verwendung des Frames (dies geschieht bereits außerhalb des Codecs) wird der von dieser Funktion zugewiesene Puffer von der Funktion freigegeben
void av_frame_unref(AVFrame* frm);
Die Funktion frox_decode()
sollte die Anzahl der zum Dekodieren verwendeten Bytes aus dem Paket zurückgeben, auf das pkt
. Wenn die Rahmenbildung abgeschlossen ist, wird der Variablen, auf die outdata_size
, ein Wert ungleich Null zugewiesen, andernfalls erhält diese Variable den Wert 0
.
Betrachten Sie eine Funktion zum Implementieren der Codierung
int frox_encode( AVCodecContext *codec_ctx, AVPacket *pkt, const AVFrame *frame, int *got_pkt_ptr);
Sie sollte die folgenden Operationen implementieren:
- Tatsächliche Kodierung;
- Zuweisung des notwendigen Puffers für das Ausgabepaket;
- Kopieren Sie verschlüsselte Daten in den Paketpuffer.
Verwenden Sie die Funktion, um den gewünschten Puffer auszuwählen
int av_new_packet(AVPacket *pkt, int pack_size);
Der Parameter pkt
als erstes Argument verwendet, und die Größe der codierten Daten ist das zweite. Nach der Verwendung des Pakets (dies geschieht bereits außerhalb des Codecs) werden die von dieser Funktion zugewiesenen Puffer von der Funktion freigegeben
void av_packet_unref(AVPacket *pkt);
Wenn das Paket abgeschlossen ist, wird der Variablen, auf die got_pkt_ptr
, ein Wert ungleich Null zugewiesen, andernfalls erhält diese Variable den Wert 0
. Liegt kein Fehler vor, gibt die Funktion Null zurück, andernfalls einen Fehlercode.
Bei der Implementierung des Codecs wird normalerweise die Protokollierung verwendet (bei Fehlern kann dies als obligatorische Anforderung angesehen werden). Hier ist ein Beispiel:
static int frox_decode_close(AVCodecContext *codec_ctx) { av_log(codec_ctx, AV_LOG_INFO, "FROX decode close\n");
In diesem Fall wird bei der Ausgabe in das Protokoll der Codec-Name als Kontextname verwendet.
3.3. Zeitstempel
Zum Einstellen der Zeit in FFmpeg wird eine Zeitbasis verwendet, die in Sekunden unter Verwendung der durch den AVRational
Typ dargestellten rationalen Zahl AVRational
. (Ein ähnlicher Ansatz wird in C ++ 11 verwendet. 1/1000 setzt beispielsweise die Millisekunde.) Frames und Pakete haben Zeitstempel vom Typ int64_t
, deren Werte die Zeit in den entsprechenden Zeiteinheiten enthalten. Ein Frame, AVFrame
eine AVFrame
Struktur, verfügt über einen Member pts
(Präsentationszeitstempel), dessen Wert die relative Zeit der im Frame erfassten Szene bestimmt. Ein Paket, AVPacket
eine AVPacket
Struktur, enthält die Member AVPacket
(Präsentationszeitstempel) und dts
(Dekomprimierungszeitstempel). Der Wert dts
bestimmt die relative Übertragungszeit des zu decodierenden Pakets. Für einfache Codecs ist es dasselbe wie für h264
, aber für komplexe Codecs kann es anders sein (z. B. für h264
bei Verwendung von B-Frames), h264
, Pakete können in der falschen Reihenfolge decodiert werden, in der Frames verwendet werden sollen.
Die Zeiteinheit ist für den Stream und den Codec definiert, die AVStream
Struktur hat ein entsprechendes Member - time_base
, dasselbe Member hat die AVCodecContext
Struktur.
Die Zeitstempel des mit av_read_frame()
aus dem Stream extrahierten Pakets werden in Zeiteinheiten dieses Streams angegeben. Bei der Dekodierung wird die Zeiteinheit des Codecs nicht verwendet. Für einen Videodecoder ist dies normalerweise einfach nicht festgelegt, für einen Audiodecoder hat dies einen Standardwert - die Inverse der Abtastfrequenz. Der Decoder sollte einen Zeitstempel für den Ausgaberahmen basierend auf dem Zeitstempel des Pakets festlegen. FFmpeg definiert ein solches Label unabhängig und schreibt es in das best_effort_timestamp
der best_effort_timestamp
Struktur. Alle diese Zeitstempel verwenden die Zeiteinheit des Streams, aus dem das Paket extrahiert wird.
Für den Encoder müssen Sie die Zeiteinheit angeben. In dem Clientcode, der die Dekodierung organisiert, müssen Sie den Wert für das time_base
Struktur avcodec_open2()
time_base
, bevor Sie avcodec_open2()
aufrufen. Verwenden Sie normalerweise die Zeiteinheit, die für Zeitstempel des codierten Frames verwendet wird. Geschieht dies nicht, geben Video-Encoder normalerweise einen Fehler aus, Audio-Encoder setzen den Standardwert - die Inverse der Abtastfrequenz. Ob ein Codec eine bestimmte Zeiteinheit ändern kann, ist nicht ganz klar. Für alle Fälle ist es besser, nach dem Aufruf von avcodec_open2()
immer den Wert von avcodec_open2()
zu überprüfen und, falls er sich geändert hat, die Zeitstempel der Eingaberahmen pro Zeiteinheit des Codecs neu zu berechnen. dts
Kodierungsprozesses müssen Sie die pts
und pts
dts
Pakets installieren. Nach dem Codieren muss vor dem Schreiben eines Pakets in den Ausgabestream der Paketzeitstempel von der Codec-Zeiteinheit in die Stream-Zeiteinheit neu berechnet werden. Verwenden Sie dazu die Funktion
void av_packet_rescale_ts( AVPacket *pkt, AVRational tb_src, AVRational tb_dst);
Beim Schreiben von Paketen in den Stream ist darauf zu achten, dass die dts
Werte strikt ansteigen, da sonst der Multiplexer einen Fehler auslöst. (Weitere Informationen finden Sie in der Dokumentation zur Funktion av_interleaved_write_frame()
.)
3.4. Andere vom Codec verwendete Funktionen
Wenn Sie eine AVCodec
Instanz initialisieren, können zwei weitere Funktionen registriert werden. Hier sind die relevanten Mitglieder von AVCodec
:
typedef struct AVCodec {
Der erste von ihnen wird einmalig bei der Registrierung des Codecs aufgerufen.
Der zweite setzt den internen Zustand des Codecs zurück, der während der Ausführung der Funktion aufgerufen wird
void avcodec_flush_buffers(AVCodecContext *codec_ctx);
Dieser Aufruf ist beispielsweise erforderlich, wenn die aktuelle Wiedergabeposition gewaltsam geändert wird.
4. Externe Implementierung des Codecs
4.1. Externer Funktionsanschluss
Betrachten Sie die folgende Codec-Organisation: Der in FFmpeg registrierte Codec spielt die Rolle eines Frameworks und delegiert die eigentliche Codierungs- / Decodierungsprozedur an externe Funktionen (eine Art von Plugins), die außerhalb von FFmpeg implementiert sind.
Eine solche Lösung kann aus vielen Gründen wünschenswert sein. Hier sind einige von ihnen:
- , FFmpeg ;
- C, , C++;
- framework, FFmpeg.
, FFmpeg «», FFmpeg API. «» FFmpeg ( , ), . — . .
typedef int(*dec_extern_t)(const void*, int, void*); static int frox_decode( AVCodecContext* codec_ctx, void* outdata, int *outdata_size, AVPacket* pkt) { int ret = -1; void* out_buff;
FFmpeg API ( C++) .
extern "C" { int DecodeFroxData(const void* buff, int size, void* outBuff); typedef int(*dec_extern_t)(const void*, int, void*); #include <libavcodec/avcodec.h> #include <libavutil/opt.h> } // ... AVCodecContext* ctx; // ... dec_extern_t dec = DecodeFroxData; void* pv = &dec; auto pb = static_cast<const uint8_t*>(pv); auto sz = sizeof(dec); av_opt_set_bin(ctx->priv_data, "frox_bin", pb, sz, 0);
4.2.
— . , . , . , , FFmpeg , «» , . . , . FFmpeg API - , , . . , . PC (Windows) DirectShow AVI . PC - DirectShow. 32- FourCC. ( biCompression
BITMAPINFOHEADER
.) , DirectShow , PC -. FFmpeg , , , codec_tag
AVCodecParameters
FourCC, . FFmpeg API , . FFmpeg FFmpeg API.
, *.mkv
FFmpeg ( ENCODER
).
Fazit
, , FFmpeg: , changelog, .. «» FFmpeg, , .
Ressourcen
FFmpeg
[1] FFmpeg —
[2] FFmpeg —
[3] FFmpeg —
[4] FFmpeg — Ubuntu
[5] FFmpeg Compilation Guide
[6] Compilation of FFmpeg 4.0 in Windows 10
FFmpeg API
[7] ffmpeg
[8] FFmpeg codec HOWTO
[9] FFmpeg video codec tutorial