Comment ajouter un codec Ă  FFmpeg


FFmpeg est un grand projet Open Source, une sorte d'encyclopédie multimédia. Avec FFmpeg, vous pouvez résoudre un grand nombre de tâches multimédia informatiques. Mais encore, il est parfois nécessaire d'étendre FFmpeg. La méthode standard consiste à apporter des modifications au code du projet, puis à compiler la nouvelle version. L'article détaille comment ajouter un nouveau codec. Certaines fonctionnalités permettant de connecter des fonctions externes à FFmpeg sont également prises en compte. Si vous n'avez pas besoin d'ajouter un codec, l'article peut être utile pour mieux comprendre l'architecture des codecs FFmpeg et leurs paramètres. Il est supposé que le lecteur est familier avec l'architecture de FFmpeg, le processus de compilation de FFmpeg, et a également une expérience de programmation en utilisant l'API FFmpeg. La description est valable pour FFmpeg 4.2 "Ada", août 2019.



Table des matières



Présentation


Le codec (codec, provient de la combinaison des termes COder et DECoder) est un terme très courant et, comme cela arrive souvent dans de tels cas, sa signification varie légèrement selon le contexte. La signification principale est le logiciel ou le matériel de compression / décompression des données multimédias. Au lieu des termes compression / décompression, les termes codage / décodage sont souvent utilisés. Mais dans certains cas, un codec signifie simplement un format de compression (ils disent aussi le format du codec), quels que soient les moyens utilisés pour la compression / décompression. Voyons comment le terme codec est utilisé dans FFmpeg.



1. Identification du codec


Les codecs FFmpeg sont compilés dans la bibliothèque libavcodec .



1.1. Identifiant du codec


L' enum AVCodecID définie dans le enum AVCodecID libavcodec/avcodec.h . Chaque élément de cette énumération identifie le format de compression. Les éléments de cette énumération doivent être de la forme AV_CODEC_ID_XXX , où XXX unique de l'identificateur de codec en majuscules. Voici des exemples d'identificateurs de codec: AV_CODEC_ID_H264 , AV_CODEC_ID_AAC . Pour une description plus détaillée de l'identifiant du codec, utilisez la structure AVCodecDescriptor (déclarée sous libavcodec/avcodec.h , donnée sous forme abrégée):


 typedef struct AVCodecDescriptor { enum AVCodecID id; enum AVMediaType type; const char *name; const char *long_name; // ... } AVCodecDescriptor; 

Le membre clé de cette structure est id , les autres membres fournissent des informations supplémentaires sur l'identifiant du codec. Chaque identifiant de codec est associé de manière unique à un type de média (membre de type ) et possède un nom unique (membre de name ), écrit en minuscules. Un tableau de type AVCodecDescriptor défini dans le fichier libavcodec/codec_desc.c AVCodecDescriptor . Pour chaque identifiant de codec, il existe un élément de tableau correspondant. Les éléments de ce tableau doivent être classés par valeurs id , car la recherche binaire est utilisée pour rechercher des éléments. Pour obtenir des informations sur l'identifiant du codec, vous pouvez utiliser les fonctions:


 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


Le codec lui-même - un ensemble d'outils nécessaires pour effectuer le codage / décodage des données multimédias, combine la structure AVCodec (déclarée dans libavcodec/avcodec.h ). Voici sa version abrégée, plus complète sera discutée ci-dessous.


 typedef struct AVCodec { const char *name; const char *long_name; enum AVMediaType type; enum AVCodecID id; // ... } AVCodec; 

Le membre le plus important de cette structure est id , l'identifiant du codec, il existe également un membre définissant le type de média ( type ), mais sa valeur doit correspondre à la valeur du même membre d' AVCodecDescriptor . Les codecs sont divisés en deux catégories: les encodeurs, qui compressent ou encodent les supports, et les décodeurs, qui effectuent l'opération inverse - décompresser ou décoder. (Parfois, dans les textes russes, au lieu du terme, l'encodeur utilise du papier calque de l'anglais - l'encodeur.) Il n'y a pas de membre spécial dans AVCodec définissant la catégorie de codec (bien que la catégorie puisse être déterminée indirectement en utilisant les fonctions av_codec_is_encoder() et av_codec_is_decoder() , cette catégorie est déterminée lors de l'enregistrement. Voici comment procéder: Plusieurs codecs peuvent avoir le même identifiant de codec. S'ils ont la même catégorie, ils doivent différer par leur nom ( name membre). Un codeur et un décodeur ayant le même identifiant de codec peuvent en avoir un le même nom, qui peut également coïncider avec le nom de l'identifiant du codec (mais ces correspondances sont facultatives). Une telle situation peut créer une certaine confusion, mais il n'y a rien à faire, vous devez bien comprendre à quelle entité appartient le nom. Dans une catégorie, le nom Le codec doit être unique. Pour rechercher des codecs enregistrés, il existe des fonctions:


 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); 

Étant donné que plusieurs codecs peuvent avoir le même identifiant, les deux dernières fonctions renvoient l'une d'entre elles, qui peut être considérée comme le codec par défaut pour un identifiant de codec donné.


Une liste de tous les codecs enregistrés peut être demandée avec la commande


ffmpeg -codecs >codecs.txt


Après avoir exécuté la commande, le fichier codecs.txt contiendra cette liste. Chaque identifiant de codec sera représenté par un enregistrement distinct (ligne). Voici, par exemple, l'entrée pour l'identifiant de codec 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)


Au début de l'enregistrement, des caractères spéciaux déterminent les caractéristiques communes disponibles pour cet identifiant de codec: D - les décodeurs sont enregistrés, E - les encodeurs sont enregistrés, V - est utilisé pour la vidéo, L - il y a la possibilité d'une compression avec perte, S - il y a la possibilité d'une compression sans perte. Vient ensuite le nom de l'identificateur de codec ( h264 ), suivi d'un nom d'identification de codec long ( H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 ), puis une liste de noms de décodeurs et encodeurs enregistrés.



2. Ajout d'un nouveau codec Ă  FFmpeg


Nous considérerons la procédure pour ajouter un nouveau codec à FFmpeg en utilisant un exemple de codec audio, que nous appellerons FROX .


Étape 1. Ajoutez un nouvel élément à l' enum AVCodecID .


Cette liste se trouve dans le libavcodec/avcodec.h . Lors de l'ajout, vous devez suivre les règles:


  1. La valeur d'un élément ne doit pas coïncider avec les valeurs des éléments d'énumération existants;
  2. Ne modifiez pas les valeurs des éléments d'énumération existants;
  3. Publiez une nouvelle valeur dans un groupe de codecs similaires.

Selon le modèle, l'identifiant de cet élément devrait être AV_CODEC_ID_FROX . Placez-le avant AV_CODEC_ID_PCM_S64LE et donnez la valeur 0x10700 .


Étape 2. Ajoutez l'élément au tableau codec_descriptors (fichier libavcodec/codec_desc.c ).


 static const AVCodecDescriptor codec_descriptors[] = { // ... { .id = AV_CODEC_ID_FROX, .type = AVMEDIA_TYPE_AUDIO, .name = "frox", .long_name = NULL_IF_CONFIG_SMALL("FROX audio"), .props = AV_CODEC_PROP_LOSSLESS, }, // ... }; 

Vous devez ajouter l'élément à la «bonne» place, la monotonie des éléments du tableau par la valeur id ne doit pas être violée.


Étape 3. Définissez séparément les instances d' AVCodec pour l'encodeur et le décodeur.


Pour ce faire, vous devez d'abord déterminer la structure du contexte du codec et plusieurs fonctions qui effectueront le codage / décodage réel et certaines autres opérations nécessaires. Dans cette section, ces définitions seront faites de façon extrêmement schématique; une description plus détaillée sera faite plus tard. Nous libavcodec/frox.c le code dans le fichier libavcodec/frox.c


 #include "avcodec.h" // context typedef struct FroxContext { // ... } FroxContext; // decoder static int frox_decode_init(AVCodecContext *codec_ctx) { return -1; } static int frox_decode_close(AVCodecContext *codec_ctx) { return -1; } static int frox_decode(AVCodecContext *codec_ctx, void* outdata, int *outdata_size, AVPacket *pkt) { return -1; } AVCodec ff_frox_decoder = { .name = "frox_dec", .long_name = NULL_IF_CONFIG_SMALL("FROX audio decoder"), .type = AVMEDIA_TYPE_AUDIO, .id = AV_CODEC_ID_FROX, .priv_data_size = sizeof(FroxContext), .init = frox_decode_init, .close = frox_decode_close, .decode = frox_decode, .capabilities = AV_CODEC_CAP_LOSSLESS, .sample_fmts = (const enum AVSampleFormat[]) {AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_NONE}, .channel_layouts = (const int64_t[]) {AV_CH_LAYOUT_MONO, 0 }, }; // encoder static int frox_encode_init(AVCodecContext *codec_ctx) { return -1; } static int frox_encode_close(AVCodecContext *codec_ctx) { return -1; } static int frox_encode(AVCodecContext *codec_ctx, AVPacket *pkt, const AVFrame *frame, int *got_pkt_ptr) { return -1; } AVCodec ff_frox_encoder = { .name = "frox_enc", .long_name = NULL_IF_CONFIG_SMALL("FROX audio encoder"), .type = AVMEDIA_TYPE_AUDIO, .id = AV_CODEC_ID_FROX, .priv_data_size = sizeof(FroxContext), .init = frox_encode_init, .close = frox_encode_close, .encode2 = frox_encode, .sample_fmts = (const enum AVSampleFormat[]) {AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE}, .channel_layouts = (const int64_t[]) {AV_CH_LAYOUT_MONO, 0 }, }; 

Pour simplifier, dans cet exemple, l'encodeur et le décodeur ont le même contexte - FroxContext , mais le plus souvent l'encodeur et le décodeur ont des contextes différents. Notez également que les AVCodec instance AVCodec doivent suivre un modèle spécial.


Étape 4. Ajoutez des instances d' AVCodec à la liste d'enregistrement.


Accédez au fichier libavcodec/allcodecs.c . Au début de ce fichier se trouve une liste des déclarations de tous les codecs enregistrés. Ajoutez nos codecs à cette liste:


 extern AVCodec ff_frox_decoder; extern AVCodec ff_frox_encoder; 

Pendant l'exécution, le script configure trouve toutes ces déclarations et génère le libavcodec/codec_list.c , qui contient un tableau de pointeurs vers les codecs déclarés dans libavcodec/allcodecs.c . Après avoir exécuté le script dans le fichier libavcodec/codec_list.c nous verrons:


 static const AVCodec * const codec_list[] = { // ... &ff_frox_encoder, // ... &ff_frox_decoder, // ... NULL }; 

Aussi, lors de l'exécution du script configure , le fichier config.h est config.h , dans lequel on retrouve les déclarations


 #define CONFIG_FROX_DECODER 1 #define CONFIG_FROX_ENCODER 1 

Étape 5. Modifier libavcodec/Makefile


Ouvrez libavcodec/Makefile . Nous trouvons la section # decoders/encoders , et y ajoutons


 OBJS-$(CONFIG_FROX_DECODER) += frox.o OBJS-$(CONFIG_FROX_ENCODER) += frox.o 

Étape 6. Modifiez le code du multiplexeur et du démultiplexeur.


Le multiplexeur (muxer) et le démultiplexeur (demuxer) doivent «connaître» le nouveau codec. Lors de l'enregistrement, il est nécessaire d'enregistrer les informations d'identification de ce codec, tout en lisant, déterminer l'identifiant du codec à partir des informations d'identification. Voici ce que vous devez faire pour le format matroska ( *.mkv ).


1. Dans le fichier libavformat/matroska.c , ajoutez un élément pour le nouveau codec au tableau libavformat/matroska.c :


 const CodecTags ff_mkv_codec_tags[] = { // ... {"A_FROX", AV_CODEC_ID_FROX}, // ... }; 

Chaîne "A_FROX" et sera écrite par le multiplexeur dans le fichier comme information d'identification. Dans ce tableau, il est associé à l'identifiant du codec, donc lors de la lecture, le démultiplexeur peut facilement le déterminer. Le démultiplexeur écrit l'identifiant du codec dans le membre codec_id de la structure codec_id . Un pointeur vers cette structure est membre de la structure AVStream .


2. Dans le fichier libavformat/matroskaenc.c , ajoutez l'élément au tableau additional_audio_tags :


 static const AVCodecTag additional_audio_tags[] = { // ... { AV_CODEC_ID_FROX, 0XFFFFFFFF }, // ... }; 

Donc tout est prêt. Tout d'abord, exécutez le script de configure . Après cela, vous devez vous assurer que les modifications décrites ci-dessus dans les fichiers libavcodec/codec_list.c et config.h sont effectuées. Ensuite, vous pouvez exécuter la compilation:


make clean
make


Si la compilation s'est ffmpeg.exe , l'exécutable ffmpeg (ou ffmpeg.exe , si le système d'exploitation cible est Windows) apparaît. Exécutez la commande


./ffmpeg -codecs >codecs.txt


et assurez-vous que FFmpeg "voit" nos nouveaux codecs, nous trouvons l'entrée dans le fichier codecs.txt


DEA..S frox FROX audio (decoders: frox_dec) (encoders: frox_enc)



3. Description détaillée du contexte et des fonctions requises


Dans cette section, nous décrivons plus en détail à quoi peut ressembler la structure du contexte du codec et les fonctions nécessaires.



3.1. Contexte du codec


Le contexte du codec peut prendre en charge l'installation d'options. Pour les encodeurs, ce support est utilisé assez souvent, pour les décodeurs moins souvent. La structure prenant en charge l'installation d'options doit avoir un pointeur vers la structure AVClass tant que premier membre, puis les options elles-mêmes.


 #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; 

Ensuite, vous devez définir un tableau de type AVOption , dont chaque élément décrit une option spécifique.


 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 }, }; 

Pour chaque option, vous devez définir un nom, une description, un décalage dans la structure, un type. Vous pouvez également définir une valeur par défaut et pour les options entières une plage de valeurs valides.


Ensuite, vous devez définir une instance de type AVClass .


 static const AVClass frox_class = { .class_name = "FroxContext", .item_name = av_default_item_name, .option = frox_options, .version = LIBAVUTIL_VERSION_INT, }; 

Un pointeur vers cette instance doit être utilisé pour initialiser le membre AVCodec correspondant.


 AVCodec ff_frox_decoder = { // ... .priv_data_size = sizeof(FroxContext), .priv_class = &frox_class, // ... }; AVCodec ff_frox_encoder = { // ... .priv_data_size = sizeof(FroxContext), .priv_class = &frox_class, // ... }; 

Maintenant lors de l'exécution de la fonction


 AVCodecContext *avcodec_alloc_context3(const AVCodec *codec); 

une instance de la structure AVCodecContext est AVCodecContext et le membre du codec est initialisé. Ensuite, en fonction de la codec->priv_data_size , la mémoire nécessaire sera allouée pour l'instance FroxContext , en utilisant la codec->priv_class premier membre de cette instance sera initialisé puis la fonction av_opt_set_defaults() sera appelée, ce qui définira les valeurs par défaut pour les options. Un pointeur vers une instance de FroxContext sera disponible via le membre priv_data de la structure priv_data .


Lorsque vous travaillez avec l'API FFmpeg, les valeurs des options peuvent être définies directement.


 const AVCodec *codec; // ... AVCodecContext *codec_ctx = avcodec_alloc_context3(codec); // ... av_opt_set(codec_ctx->priv_data, "frox_str", "meow", 0); av_opt_set_int(codec_ctx->priv_data, "frox_int", 42, 0); 

Une autre façon consiste à utiliser le dictionnaire d'options, qui sera passé comme troisième argument lors de l'appel à avcodec_open2() (voir ci-dessous).


Utilisation de la fonction


 const AVOption* av_opt_next(const void* ctx, const AVOption* prev); 

Vous pouvez obtenir une liste de toutes les options prises en charge par le contexte du codec. Ceci est utile lors de l'examen d'un codec. Mais avant cela, vous devez vous assurer que codec_ctx->codec->priv_class défini sur une valeur non nulle, sinon le contexte ne prend pas en charge les options et toute opération avec des options plantera le programme.



3.2. Les fonctions


Examinons maintenant plus en détail comment les fonctions utilisées dans l'initialisation du codec et l'encodage / décodage proprement dit sont organisées. Ils ont généralement toujours besoin d'obtenir un pointeur sur un FroxContext .


 AVCodecContext *codec_ctx; // ... FroxContext* frox_ctx = codec_ctx->priv_data; 

Les fonctions frox_decode_init() et frox_encode_init() seront appelées lors de l'exécution de la fonction


 int avcodec_open2( AVCodecContext *codec_ctx, const AVCodec *codec, AVDictionary **options); 

Ils doivent allouer les ressources nécessaires au fonctionnement du codec et, si nécessaire, initialiser certains membres de la structure AVCodecContext , par exemple frame_size pour un frame_size audio.


Les fonctions frox_decode_close() et frox_encode_close() seront appelées lors de leur exécution


 int avcodec_close(AVCodecContext *codec_ctx); 

Ils doivent libérer les ressources allouées.


Considérons une fonction pour implémenter le décodage


 int frox_decode( AVCodecContext *codec_ctx, void *outdata, int *outdata_size, AVPacket *pkt); 

Elle doit mettre en œuvre les opérations suivantes:


  1. Décodage réel;
  2. Allocation du tampon nécessaire pour la trame de sortie;
  3. Copiez les données décodées dans le tampon de trame.

Considérez comment allouer le tampon nécessaire à la trame de sortie. Le paramètre outdata pointe en fait vers un AVFrame , vous devez donc d'abord effectuer une conversion de type:


 AVFrame* frm = outdata; 

Ensuite, vous devez allouer un tampon pour stocker les données de trame. Pour ce faire, initialisez les membres AVFrame qui déterminent la taille du tampon de trame. Pour l'audio, il s'agit de nb_samples , channel_layout , format (pour la width , la height et le format vidéo).


Après cela, vous devez appeler la fonction


 int av_frame_get_buffer(AVFrame* frm, int alignment); 

Le pointeur sur la trame, qui est le paramètre converti des données outdata , est utilisé comme premier argument; il est recommandé de passer zéro comme deuxième argument. Après avoir utilisé le cadre (cela se produit déjà en dehors du codec), le tampon alloué par cette fonction est libéré par la fonction


 void av_frame_unref(AVFrame* frm); 

La fonction frox_decode() doit renvoyer le nombre d'octets utilisés pour le décodage du paquet pointé par pkt . Si la formation de trame est terminée, la variable pointée par outdata_size attribuer une valeur non nulle, sinon cette variable obtient la valeur 0 .


Considérons une fonction pour implémenter le codage


 int frox_encode( AVCodecContext *codec_ctx, AVPacket *pkt, const AVFrame *frame, int *got_pkt_ptr); 

Elle doit mettre en œuvre les opérations suivantes:


  1. Codage réel;
  2. Allocation de la mémoire tampon nécessaire pour le paquet de sortie;
  3. Copiez les données encodées dans le tampon de paquets.

Pour sélectionner le tampon requis, utilisez la fonction


 int av_new_packet(AVPacket *pkt, int pack_size); 

Le paramètre pkt utilisé comme premier argument et la taille des données codées est le second. Après avoir utilisé le package (cela se produit déjà en dehors du codec), les tampons alloués par cette fonction sont libérés par la fonction


 void av_packet_unref(AVPacket *pkt); 

Si le package est terminé, la variable pointée par got_pkt_ptr attribuer une valeur non nulle, sinon cette variable obtient la valeur 0 . S'il n'y a pas d'erreur, la fonction renvoie zéro, sinon un code d'erreur.


Lors de la mise en œuvre du codec, la journalisation est généralement utilisée (pour les erreurs, cela peut être considéré comme une exigence obligatoire). Voici un exemple:


 static int frox_decode_close(AVCodecContext *codec_ctx) { av_log(codec_ctx, AV_LOG_INFO, "FROX decode close\n"); // ... } 

Dans ce cas, lors de la sortie dans le journal, le nom du codec sera utilisé comme nom de contexte.



3.3. Horodatages


Pour définir l'heure dans FFmpeg, une base de temps est utilisée, spécifiée en secondes à l'aide du nombre rationnel représenté par le type AVRational . (Une approche similaire est utilisée en C ++ 11. Par exemple, 1/1000 définit la milliseconde.) Les trames et les paquets ont des horodatages de type int64_t , leurs valeurs contiennent le temps dans les unités de temps correspondantes. Une image, c'est-à-dire une structure AVFrame , a un pts membre (horodatage de présentation), dont la valeur détermine l'heure relative de la scène capturée dans l'image. Un package, c'est-à-dire une structure AVPacket , a des membres pts (horodatage de présentation) et dts (horodatage de décompression). La valeur dts détermine le temps relatif de transmission du paquet pour le décodage. Pour les codecs simples, c'est la même chose que les pts , mais pour les codecs complexes, cela peut être différent (par exemple, pour h264 lors de l'utilisation de trames B), c'est-à-dire que les paquets peuvent être décodés dans le mauvais ordre dans lequel les trames doivent être utilisées.


L'unité de temps est définie pour le flux et le codec, la structure AVStream a un membre correspondant - time_base , le même membre a la structure AVCodecContext .


Les horodatages du paquet extrait du flux à l'aide de av_read_frame() seront spécifiés en unités de temps de ce flux. Lors du décodage, l'unité de temps du codec n'est pas utilisée. Pour un décodeur vidéo, il n'est généralement pas défini, pour un décodeur audio, il a une valeur standard - l'inverse de la fréquence d'échantillonnage. Le décodeur doit définir un horodatage pour la trame de sortie en fonction de l'horodatage du paquet. FFmpeg définit indépendamment une telle étiquette et l'écrit dans le membre best_effort_timestamp de la structure best_effort_timestamp . Tous ces horodatages utiliseront l'unité de temps du flux dont le paquet est extrait.


Pour l'encodeur, vous devez spécifier l'unité de temps. Dans le code client qui organise le décodage, vous devez définir la valeur du membre time_base de la structure time_base avant d'appeler avcodec_open2() . Prenez généralement l'unité de temps utilisée pour les horodatages de la trame codée. Si cela n'est pas fait, les encodeurs vidéo donnent généralement une erreur, les encodeurs audio définissent la valeur par défaut - l'inverse de la fréquence d'échantillonnage. Il n'est pas tout à fait clair si un codec peut changer une unité de temps donnée. Au cas où, il est préférable de toujours vérifier la valeur time_base après avoir appelé avcodec_open2() et, si elle a changé, recalculer les horodatages des trames d'entrée par unité de temps du codec. Dans le processus d'encodage, vous devez installer les pts et dts package. Après le codage, avant d'écrire un paquet dans le flux de sortie, il est nécessaire de recalculer les horodatages des paquets de l'unité de temps du codec vers l'unité de temps du flux. Pour ce faire, utilisez la fonction


 void av_packet_rescale_ts( AVPacket *pkt, AVRational tb_src, AVRational tb_dst); 

Lors de l'écriture de paquets dans le flux, il est nécessaire de s'assurer que les valeurs dts augmentent strictement, sinon le multiplexeur générera une erreur. (Pour plus d'informations, consultez la documentation de la fonction av_interleaved_write_frame() .)



3.4. Autres fonctions utilisées par le codec


Lorsque vous initialisez une instance AVCodec , deux fonctions supplémentaires peuvent être enregistrées. Voici les membres concernés d' AVCodec :


 typedef struct AVCodec { // ... void (*init_static_data)(AVCodec *codec); void (*flush)(AVCodecContext *codec_ctx); // ... } AVCodec; 

Le premier d'entre eux est appelé une fois lors de l'enregistrement du codec.


Le second réinitialise l'état interne du codec, il sera appelé lors de l'exécution de la fonction


 void avcodec_flush_buffers(AVCodecContext *codec_ctx); 

Cet appel est nécessaire, par exemple, lorsque vous changez de force la position de lecture actuelle.



4. Implémentation externe du codec



4.1. Connexion de fonction externe


Considérons l'organisation suivante du codec: le codec enregistré dans FFmpeg joue le rôle d'un framework et délègue la procédure de codage / décodage réelle à des fonctions externes (une sorte de plugins) implémentées en dehors de FFmpeg.


Une telle solution peut ĂŞtre souhaitable pour de nombreuses raisons. En voici quelques uns:


  1. , FFmpeg ;
  2. C, , C++;
  3. 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; //      out_buff FroxContext *fc = codec_ctx->priv_data; if (fc->bin_size > 0) { if (fc->bin_size == sizeof(dec_extern_t)) { dec_extern_t edec; memcpy(&edec, fc->frox_bin, fc->bin_size); ret = (*edec)(pkt->data, pkt->size, out_buff); if (ret >= 0) { //     out_buff   } } else { /*  */ } } else { /*    */ } // ... return ret; } 

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 ).



Conclusion


, , FFmpeg: , changelog, .. «» FFmpeg, , .



Les ressources


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




Source: https://habr.com/ru/post/fr480714/


All Articles