FFmpeg هو مشروع مفتوح المصدر كبير ، وهو نوع من موسوعة الوسائط المتعددة. مع FFmpeg ، يمكنك حل عدد كبير من مهام الوسائط المتعددة للكمبيوتر. ولكن لا يزال ، في بعض الأحيان هناك حاجة لتوسيع FFmpeg. الطريقة القياسية هي إجراء تغييرات على رمز المشروع ومن ثم ترجمة الإصدار الجديد. تفاصيل المقالة كيفية إضافة برنامج ترميز جديد. بعض الميزات لربط الوظائف الخارجية مع FFmpeg تعتبر أيضا. إذا لم تكن هناك حاجة لإضافة برنامج ترميز ، فقد تكون المقالة مفيدة لفهم أفضل لبنية برامج ترميز FFmpeg وإعداداتها. من المفترض أن يكون القارئ معتادًا على بنية FFmpeg ، وعملية تجميع FFmpeg ، ولديه أيضًا خبرة برمجية باستخدام API FFmpeg. الوصف صالح لـ FFmpeg 4.2 "Ada" ، أغسطس 2019.
جدول المحتويات
مقدمة
يعد برنامج الترميز (برنامج الترميز) الذي يأتي من مجموعة المصطلحين COder و DECoder) مصطلحًا شائعًا للغاية ، وكما يحدث غالبًا في مثل هذه الحالات ، يختلف معناه بعض الشيء حسب السياق. المعنى الأساسي هو البرنامج أو الأجهزة لضغط / إلغاء ضغط بيانات الوسائط. بدلاً من المصطلحات compression / decompression ، غالبًا ما تستخدم المصطلحات encoding / decoding. ولكن في بعض الحالات ، يُفهم عادةً أن برنامج الترميز يعني مجرد تنسيق ضغط (كما يقول تنسيق برنامج الترميز) ، بغض النظر عن الوسائل المستخدمة للضغط / إلغاء الضغط. دعونا نرى كيف يتم استخدام مصطلح الترميز في FFmpeg.
1. تحديد الترميز
يتم تصنيف برامج الترميز FFmpeg في مكتبة libavcodec .
1.1. كود الترميز
enum AVCodecID
تعريف enum AVCodecID
في enum AVCodecID
libavcodec/avcodec.h
. يحدد كل عنصر من عناصر هذا التعداد تنسيق الضغط. يجب أن تكون عناصر هذا التعداد من النوع AV_CODEC_ID_XXX
، حيث XXX
اسم معرف برنامج الترميز الفريد في الحالة العليا. فيما يلي أمثلة AV_CODEC_ID_H264
برنامج الترميز: AV_CODEC_ID_H264
، AV_CODEC_ID_AAC
. للحصول على وصف أكثر تفصيلًا لمعرّف برنامج الترميز ، استخدم بنية AVCodecDescriptor
(تم الإعلان عنها في libavcodec/avcodec.h
، libavcodec/avcodec.h
في شكل مختصر):
typedef struct AVCodecDescriptor { enum AVCodecID id; enum AVMediaType type; const char *name; const char *long_name;
العضو الرئيسي في هذه البنية هو id
، أما باقي الأعضاء فيقدمون معلومات إضافية حول معرّف برنامج الترميز. يرتبط كل معرف لبرنامج الترميز بشكل فريد بنوع الوسائط (عضو في type
) وله اسم فريد (عضو في name
) ، وهو مكتوب في الحالة الصغيرة. يتم تعريف صفيف من النوع AVCodecDescriptor
في الملف libavcodec/codec_desc.c
AVCodecDescriptor
. لكل معرف برنامج الترميز ، هناك عنصر صفيف المقابلة. يجب أن يتم ترتيب عناصر هذه المجموعة حسب قيم id
، حيث يتم استخدام البحث الثنائي للبحث عن العناصر. للحصول على معلومات حول معرّف برنامج الترميز ، يمكنك استخدام الوظائف:
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. الترميز
برنامج الترميز نفسه - مجموعة من الأدوات اللازمة لتنفيذ تشفير / فك تشفير بيانات الوسائط ، يجمع بين بنية AVCodec
(تم الإعلان عنها في libavcodec/avcodec.h
). فيما يلي نسخته المختصرة ، ستتم مناقشة الأكثر اكتمالا أدناه.
typedef struct AVCodec { const char *name; const char *long_name; enum AVMediaType type; enum AVCodecID id;
أهم عضو في هذه البنية هو id
، id
الكودك ، وهناك أيضًا عضو يحدد نوع الوسائط ( type
) ، ولكن يجب أن تتطابق قيمته مع قيمة نفس العضو من AVCodecDescriptor
. وتنقسم برامج الترميز إلى فئتين: التشفير ، الذي يضغط أو يشفر الوسائط ، وأجهزة فك التشفير ، التي تؤدي العملية المعاكسة - فك الضغط أو فك التشفير. (في بعض الأحيان ، في النصوص الروسية ، يستخدم المشفر ورقة التتبع من الإنجليزية - المشفر.) لا يوجد عضو خاص في AVCodec
يحدد فئة برنامج الترميز (على الرغم من أنه يمكن تحديد الفئة بشكل غير مباشر باستخدام الدالتين av_codec_is_encoder()
و av_codec_is_decoder()
ويتم تحديدهما أثناء التسجيل. سيتم عرض كيفية القيام بذلك: يمكن أن يكون للعديد من برامج الترميز نفس معرف برنامج الترميز ، وإذا كان لديهم نفس الفئة ، فيجب أن يختلفوا حسب الاسم ( name
العضو) ، ويمكن لجهاز التشفير وفك الشفرة الذي يحمل نفس معرف برنامج الترميز أن يكون واحد نفس الاسم ، والذي قد يتزامن أيضًا مع اسم معرف برنامج الترميز (ولكن هذه التطابقات اختيارية) ، قد يؤدي هذا الموقف إلى بعض الالتباس ، ولكن لا يوجد شيء يجب القيام به ، يجب أن تفهم بوضوح الكيان الذي ينتمي إليه الاسم. ضمن فئة واحدة ، الاسم يجب أن يكون برنامج الترميز فريداً ، وللبحث عن برامج الترميز المسجلة ، هناك وظائف:
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);
نظرًا لأن العديد من برامج الترميز يمكن أن يكون لها نفس المعرف ، فإن الدالتين الأخيرتين تقومان بإرجاع واحدة منها ، والتي يمكن اعتبارها برنامج الترميز الافتراضي لمعرّف برنامج ترميز معين.
يمكن طلب قائمة بجميع برامج الترميز المسجلة باستخدام الأمر
ffmpeg -codecs >codecs.txt
بعد تنفيذ الأمر ، codecs.txt
ملف codecs.txt
على هذه القائمة. سيتم تمثيل كل معرّف برنامج الترميز بسجل منفصل (سطر). هنا ، على سبيل المثال ، إدخال معرف برنامج الترميز 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)
في بداية التسجيل ، هناك أحرف خاصة تحدد الميزات الشائعة المتوفرة لمعرّف الترميز: يتم تسجيل وحدات فك الترميز ، ويتم تسجيل أجهزة التشفير E
، يتم استخدام V
- للفيديو ، L
- هناك إمكانية ضغط مع خسائر ، S
- هناك إمكانية ضغط دون خسائر. بعد ذلك يأتي اسم معرف الكودك ( h264
) ، متبوعًا باسم معرف الكودك الطويل ( H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
) ، ثم قائمة بأسماء وحدات فك التشفير وأجهزة التشفير المسجلة.
2. إضافة برنامج ترميز جديد إلى FFmpeg
سننظر في الإجراء الخاص بإضافة برنامج ترميز جديد إلى FFmpeg باستخدام مثال لبرنامج ترميز صوتي ، والذي FROX
عليه FROX
.
الخطوة 1. إضافة عنصر جديد إلى enum AVCodecID
.
هذه القائمة موجودة في libavcodec/avcodec.h
. عند الإضافة ، يجب عليك اتباع القواعد:
- يجب ألا تتزامن قيمة العنصر مع قيم عناصر التعداد الحالية ؛
- لا تقم بتغيير قيم عناصر التعداد الحالية ؛
- نشر قيمة جديدة في مجموعة من برامج الترميز المشابهة.
وفقًا للقالب ، يجب أن يكون معرف هذا العنصر هو AV_CODEC_ID_FROX
. AV_CODEC_ID_PCM_S64LE
قبل AV_CODEC_ID_PCM_S64LE
واعط القيمة 0x10700
.
الخطوة 2. إضافة العنصر إلى صفيف codec_descriptors
(ملف libavcodec/codec_desc.c
).
static const AVCodecDescriptor codec_descriptors[] = {
تحتاج إلى إضافة العنصر إلى المكان "الصحيح" ، ويجب عدم انتهاك رتابة عناصر الصفيف بواسطة قيمة id
.
الخطوة 3. تحديد مثيلات AVCodec
بشكل منفصل لجهاز التشفير وفك التشفير.
للقيام بذلك ، تحتاج أولاً إلى تحديد بنية سياق برنامج الترميز والعديد من الوظائف التي ستؤدي الترميز / فك الترميز الفعلي وبعض العمليات الضرورية الأخرى. في هذا القسم ، سيتم إجراء هذه التعريفات بشكل تخطيطي للغاية ؛ وسيتم تقديم وصف أكثر تفصيلًا لاحقًا. libavcodec/frox.c
الكود في ملف libavcodec/frox.c
#include "avcodec.h"
بالنسبة للبساطة ، في هذا المثال ، يكون لكل من برنامج التشفير وفك الشفرة نفس السياق - FroxContext
، ولكن في معظم الأحيان يكون لكل من برنامج التشفير وفك الشفرة سياقات مختلفة. لاحظ أيضًا أن AVCodec
مثيل AVCodec
يجب أن تتبع نمطًا خاصًا.
الخطوة 4. إضافة مثيلات AVCodec
إلى قائمة التسجيل.
انتقل إلى ملف libavcodec/allcodecs.c
. في بداية هذا الملف توجد قائمة بالإعلانات لجميع برامج الترميز المسجلة. أضف برامج الترميز الخاصة بنا إلى هذه القائمة:
extern AVCodec ff_frox_decoder; extern AVCodec ff_frox_encoder;
أثناء التنفيذ ، يقوم البرنامج النصي configure
بالبحث عن كل هذه التعريفات ويقوم بإنشاء libavcodec/codec_list.c
، والذي يحتوي على مجموعة من المؤشرات إلى برامج الترميز المعلنة في libavcodec/allcodecs.c
. بعد تنفيذ البرنامج النصي في ملف libavcodec/codec_list.c
سنرى:
static const AVCodec * const codec_list[] = {
أيضًا ، أثناء تنفيذ البرنامج النصي configure
، يتم config.h
ملف config.h
، حيث نجد الإعلانات
#define CONFIG_FROX_DECODER 1 #define CONFIG_FROX_ENCODER 1
الخطوة 5. تحرير libavcodec/Makefile
فتح libavcodec/Makefile
. نجد القسم # decoders/encoders
، ونضيف هناك
OBJS-$(CONFIG_FROX_DECODER) += frox.o OBJS-$(CONFIG_FROX_ENCODER) += frox.o
الخطوة 6. تحرير رمز المضاعف و demultiplexer.
يجب أن يعرف "مُضاعِف الإرسال" (muxer) و demultiplexer (demuxer) برنامج الترميز الجديد. عند التسجيل ، من الضروري تسجيل معلومات التعريف الخاصة ببرنامج الترميز هذا ، أثناء القراءة ، تحديد معرف برنامج الترميز من معلومات التعريف. إليك ما تحتاج إلى القيام به لتنسيق matroska
( *.mkv
matroska
*.mkv
).
1. في ملف libavformat/matroska.c
، أضف عنصرًا لبرنامج الترميز الجديد إلى صفيف libavformat/matroska.c
:
const CodecTags ff_mkv_codec_tags[] = {
سلسلة "A_FROX"
وسيتم كتابتها بواسطة المضاعف إلى الملف "A_FROX"
تعريف. في هذه المصفوفة ، يتم ربطها بمعرّف برنامج الترميز ، وبالتالي ، عند القراءة ، يمكن لمزيل تعدد الإرسال تحديده بسهولة. يكتب demultiplexer معرف برنامج الترميز لعضو codec_id
بنية codec_id
. مؤشر هذا الهيكل هو عضو في بنية AVStream
.
2. في الملف libavformat/matroskaenc.c
، أضف العنصر إلى صفيف libavformat/matroskaenc.c
:
static const AVCodecTag additional_audio_tags[] = {
لذلك كل شيء جاهز. أولاً ، قم بتشغيل configure
البرنامج النصي. بعد ذلك ، تحتاج إلى التأكد من إجراء التغييرات الموضحة أعلاه في الملفات libavcodec/codec_list.c
و config.h
. ثم يمكنك تشغيل الترجمة:
make clean
make
إذا تمت عملية ffmpeg.exe
، ffmpeg.exe
ffmpeg
القابل للتنفيذ (أو ffmpeg.exe
، إذا كان نظام التشغيل الهدف هو Windows). تنفيذ الأمر
./ffmpeg -codecs >codecs.txt
وتأكد من أن FFmpeg "يرى" برامج الترميز الجديدة الخاصة بنا ، نجد الإدخال في ملف codecs.txt
DEA..S frox FROX audio (decoders: frox_dec) (encoders: frox_enc)
3. وصف مفصل للسياق والوظائف المطلوبة
في هذا القسم ، وصفنا بمزيد من التفصيل شكل بنية سياق الترميز والوظائف اللازمة.
3.1. سياق الترميز
قد يدعم سياق برنامج الترميز تثبيت الخيارات. بالنسبة لأجهزة التشفير ، يتم استخدام هذا الدعم في كثير من الأحيان بما فيه الكفاية ، لأجهزة فك التشفير أقل كثيرًا. يجب أن يكون للهيكل الذي يدعم تثبيت الخيارات مؤشرًا إلى بنية AVClass
كأول عضو ثم للخيارات نفسها.
#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;
بعد ذلك ، تحتاج إلى تحديد مجموعة من النوع AVOption
، AVOption
كل عنصر منها خيارًا محددًا.
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 }, };
لكل خيار ، يجب عليك تحديد الاسم والوصف والإزاحة في الهيكل والنوع. يمكنك أيضًا تحديد قيمة افتراضية ولخيارات عدد صحيح مجموعة من القيم الصالحة.
بعد ذلك ، تحتاج إلى تعريف مثيل من نوع AVClass
.
static const AVClass frox_class = { .class_name = "FroxContext", .item_name = av_default_item_name, .option = frox_options, .version = LIBAVUTIL_VERSION_INT, };
يجب استخدام مؤشر لهذا المثيل لتهيئة عضو AVCodec
المقابل.
AVCodec ff_frox_decoder = {
الآن عندما تكون الوظيفة
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
يتم إنشاء مثيل بنية AVCodecContext
عضو codec
. بعد ذلك ، استنادًا إلى codec->priv_data_size
، سيتم تخصيص الذاكرة اللازمة FroxContext
، وباستخدام codec->priv_class
سيتم تهيئة العضو الأول من هذا المثيل ، ثم يتم استدعاء وظيفة av_opt_set_defaults()
، والتي ستقوم بتعيين القيم الافتراضية للخيارات. سيكون مؤشر لمثيل FroxContext
متاحًا من خلال عضو priv_data
بنية priv_data
.
عند العمل مع FFmpeg API ، يمكن تعيين قيم الخيارات مباشرةً.
const AVCodec *codec;
هناك طريقة أخرى تتمثل في استخدام قاموس الخيارات ، والذي سيتم تمريره كوسيطة ثالثة عند استدعاء avcodec_open2()
(انظر أدناه).
باستخدام وظيفة
const AVOption* av_opt_next(const void* ctx, const AVOption* prev);
يمكنك الحصول على قائمة بجميع الخيارات التي يدعمها سياق برنامج الترميز. هذا مفيد عند فحص برنامج الترميز. ولكن قبل ذلك ، يجب التأكد من أن codec_ctx->codec->priv_class
تعيينه على قيمة غير صفرية ، وإلا فإن السياق لا يدعم الخيارات وأن أي عملية مع خيارات ستعطل البرنامج.
3.2. وظائف
دعونا الآن نفحص بمزيد من التفصيل كيفية ترتيب الوظائف المستخدمة في تهيئة برنامج الترميز والترميز / فك التشفير الفعلي. عادة ما يحتاجون دائمًا إلى الحصول على مؤشر إلى FroxContext
.
AVCodecContext *codec_ctx;
سيتم frox_decode_init()
و frox_encode_init()
عند تنفيذ الوظيفة
int avcodec_open2( AVCodecContext *codec_ctx, const AVCodec *codec, AVDictionary **options);
إنهم بحاجة إلى تخصيص الموارد اللازمة لتشغيل برنامج الترميز ، وإذا لزم الأمر ، قم بتهيئة بعض أعضاء بنية AVCodecContext
، على سبيل المثال ، frame_size
صوتي.
سيتم frox_decode_close()
و frox_encode_close()
عند تنفيذهما
int avcodec_close(AVCodecContext *codec_ctx);
انهم بحاجة الى تحرير الموارد المخصصة.
النظر في وظيفة لتنفيذ فك التشفير
int frox_decode( AVCodecContext *codec_ctx, void *outdata, int *outdata_size, AVPacket *pkt);
يجب أن تنفذ العمليات التالية:
- فك التشفير الفعلي
- تخصيص المخزن المؤقت اللازم لإطار الإخراج ؛
- نسخ البيانات فك الشفرة إلى الإطار العازلة.
النظر في كيفية تخصيص المخزن المؤقت اللازم لإطار الإخراج. outdata
المعلمة outdata
فعليًا إلى AVFrame
، لذلك يجب عليك أولاً إجراء تحويل نوع:
AVFrame* frm = outdata;
بعد ذلك ، حدد مخزن مؤقت لتخزين بيانات الإطار. للقيام بذلك ، قم بتهيئة أعضاء AVFrame
التي تحدد حجم المخزن المؤقت للإطار. بالنسبة إلى الصوت ، هذا هو nb_samples
، channel_layout
، format
(لعرض الفيديو ، height
، format
).
بعد ذلك ، تحتاج إلى استدعاء وظيفة
int av_frame_get_buffer(AVFrame* frm, int alignment);
يتم استخدام المؤشر إلى الإطار ، وهو معلمة البيانات outdata
المحولة ، كوسيطة أولى ؛ يوصى بتمرير الصفر كوسيطة ثانية. بعد استخدام الإطار (يحدث هذا بالفعل خارج برنامج الترميز) ، يتم تحرير المخزن المؤقت المخصص بواسطة هذه الوظيفة بواسطة الوظيفة
void av_frame_unref(AVFrame* frm);
يجب أن ترجع الدالة frox_decode()
عدد البايتات المستخدمة في فك تشفير الحزمة من الإشارة إلى pkt
. في حالة اكتمال تكوين الإطار ، outdata_size
تعيين قيمة غير صفرية للمتغير الذي يشير إليه outdata_size
، وإلا فإن هذا المتغير يحصل على القيمة 0
.
النظر في وظيفة لتنفيذ الترميز
int frox_encode( AVCodecContext *codec_ctx, AVPacket *pkt, const AVFrame *frame, int *got_pkt_ptr);
يجب أن تنفذ العمليات التالية:
- الترميز الفعلي
- تخصيص المخزن المؤقت اللازم لحزمة الإخراج ؛
- نسخ البيانات المشفرة إلى المخزن المؤقت الحزمة.
لتحديد المخزن المؤقت المطلوب ، استخدم الوظيفة
int av_new_packet(AVPacket *pkt, int pack_size);
pkt
استخدام المعلمة pkt
كوسيطة الأولى ، وحجم البيانات المشفرة هو الثاني. بعد استخدام الحزمة (يحدث هذا بالفعل خارج برنامج الترميز) ، يتم تحرير المخازن المؤقتة المخصصة بواسطة هذه الوظيفة بواسطة الوظيفة
void av_packet_unref(AVPacket *pkt);
إذا تم إكمال الحزمة ، فسيتم تعيين المتغير الذي يشير إليه got_pkt_ptr
على قيمة غير صفرية ، وإلا فإن هذا المتغير يحصل على القيمة 0
. إذا لم يكن هناك خطأ ، فسوف تُرجع الدالة الصفر ، وإلا رمز الخطأ.
عند تنفيذ برنامج الترميز ، يتم عادةً استخدام التسجيل (للأخطاء يمكن اعتبار ذلك متطلبًا إلزاميًا). هنا مثال:
static int frox_decode_close(AVCodecContext *codec_ctx) { av_log(codec_ctx, AV_LOG_INFO, "FROX decode close\n");
في هذه الحالة ، عند الإخراج إلى السجل ، سيتم استخدام اسم برنامج الترميز كاسم سياق.
3.3. الطوابع الزمنية
لضبط الوقت في FFmpeg ، يتم استخدام قاعدة زمنية محددة بالثواني باستخدام الرقم الرشيد الذي يمثله نوع AVRational
. (يتم استخدام int64_t
C ++ 11. على سبيل المثال ، يعين 1/1000 مللي ثانية). تحتوي الإطارات والحزم على طوابع زمنية من النوع int64_t
، وتحتوي قيمها على وقت في وحدات الوقت المقابلة. يحتوي الإطار ، أي بنية AVFrame
، على pts
أعضاء (الطابع الزمني للعرض التقديمي) ، تحدد قيمته الوقت النسبي للمشهد الذي تم التقاطه في الإطار. تحتوي الحزمة ، وهي بنية AVPacket
، على أعضاء pts
(الطابع الزمني للعرض) و dts
(الطابع الزمني dts
الضغط). تحدد القيمة dts
الوقت النسبي لإرسال الحزمة لفك التشفير. بالنسبة إلى برامج الترميز البسيطة ، فهي pts
، لكن بالنسبة إلى برامج الترميز المعقدة يمكن أن تكون مختلفة (على سبيل المثال ، بالنسبة لـ h264
عند استخدام B-frames) ، أي أنه يمكن فك تشفير الحزم بالترتيب الخاطئ الذي يجب أن تستخدم به الإطارات.
يتم تعريف وحدة الوقت للدفق والترميز ، بنية AVStream
لديها عضو المطابق - time_base
، نفس العضو لديه بنية AVCodecContext
.
سيتم تحديد الطوابع الزمنية للحزمة المستخرجة من الدفق باستخدام av_read_frame()
بوحدات وقت هذا الدفق. عند فك التشفير ، لا يتم استخدام الوحدة الزمنية لبرنامج الترميز. بالنسبة لوحدة فك ترميز الفيديو ، عادةً ما لا يتم ضبطها ببساطة ، بالنسبة لوحدة فك ترميز الصوت ، تكون لها قيمة قياسية - عكس تردد أخذ العينات. يجب على وحدة فك الترميز أن تحدد طابعًا زمنيًا لإطار الخرج استنادًا إلى الطابع الزمني للرزمة. يعرّف FFmpeg هذه التسمية بشكل مستقل best_effort_timestamp
العضو best_effort_timestamp
بنية best_effort_timestamp
. ستستخدم كل هذه الطوابع الزمنية وحدة وقت الدفق التي يتم استخراج الحزمة منها.
بالنسبة لجهاز التشفير ، يجب عليك تحديد وحدة الوقت. في رمز العميل الذي ينظم عملية فك التشفير ، يجب عليك تعيين القيمة لعضو time_base
بنية time_base
قبل استدعاء avcodec_open2()
. عادةً ما تأخذ وحدة الوقت المستخدمة للطوابع الزمنية للإطار المشفر. إذا لم يتم ذلك ، فعادةً ما تتسبب برامج تشفير الفيديو في حدوث خطأ ، تقوم أجهزة تشفير الصوت بتعيين القيمة الافتراضية - وهي عكس تردد أخذ العينات. ما إذا كان برنامج الترميز يمكنه تغيير وحدة زمنية معينة ليس واضحًا تمامًا. فقط في حالة ، من الأفضل دائمًا التحقق من قيمة time_base
بعد استدعاء avcodec_open2()
وإذا كان قد تغير ، قم بإعادة حساب الطوابع الزمنية لإطارات الإدخال لكل وحدة زمنية من برنامج الترميز. في عملية الترميز ، يجب عليك تثبيت pts
و dts
الحزمة. بعد الترميز ، قبل كتابة حزمة إلى دفق الإخراج ، من الضروري إعادة حساب الطوابع الزمنية للرزمة من وحدة وقت الترميز إلى وحدة وقت الدفق. للقيام بذلك ، استخدم الوظيفة
void av_packet_rescale_ts( AVPacket *pkt, AVRational tb_src, AVRational tb_dst);
عند كتابة حزم إلى الدفق ، من الضروري التأكد من أن قيم dts
تزداد بشكل صارم ، وإلا فإن مُضاعِف الإرسال سوف يلقي خطأً. (لمزيد من المعلومات ، راجع الوثائق الخاصة av_interleaved_write_frame()
.)
3.4. وظائف أخرى يستخدمها الترميز
عند تهيئة مثيل AVCodec
، يمكن تسجيل وظيفتين أخريين. هنا الأعضاء المعنيين في AVCodec
:
typedef struct AVCodec {
يتم استدعاء أولهم مرة واحدة عند تسجيل برنامج الترميز.
والثاني يعيد ضبط الحالة الداخلية لبرنامج الترميز ، وسيتم استدعاؤه أثناء تنفيذ الوظيفة
void avcodec_flush_buffers(AVCodecContext *codec_ctx);
هذه الدعوة ضرورية ، على سبيل المثال ، عند تغيير وضع التشغيل الحالي بالقوة.
4. التنفيذ الخارجي لبرنامج الترميز
4.1. وظيفة اتصال خارجي
النظر في منظمة الترميز التالية: يلعب الترميز المسجل في FFmpeg دور إطار عمل ، ويفوض إجراء الترميز / فك التشفير الفعلي إلى الوظائف الخارجية (نوع من المكونات الإضافية) المنفذة خارج FFmpeg.
. هؤلاء بعض منهم:
- , 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
).
استنتاج
, , FFmpeg: , changelog, .. «» FFmpeg, , .
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