ما هو Media Foundation - لماذا يبدأ في الإحساس بأنّه COM وواجهات Windows الإعلاميّة في آنٍ واحد
عندما تبدأ بلمس Media Foundation للمرّة الأولى، غالباً ما يكون الإحساس هكذا:
«اعتقدت أنّني أستخدم واجهة Windows للفيديو / الصوت، لكنّي فجأة أغرق في COM.»
وهذا الردّ طبيعيّ.
بمجرّد أن تظهر CoInitializeEx و MFStartup و IMFSourceReader و IMFMediaType و IMFTransform و IMFActivate و HRESULT، إضافةً إلى كثرة الإعدادات المعتمدة على GUID دفعةً واحدة، يبدأ كلّ شيء بالشعور بأنّه أقرب إلى Win32 / COM منه إلى مكتبة وسائط بسيطة.
لا يحاول هذا المقال تغطية واجهة Media Foundation كاملةً كما لو كانت قاموساً. بل يرتّب أوّلاً ثلاثة أسئلة عمليّة:
- لماذا تظهر مفاهيم COM طبيعيّاً عند استخدام Media Foundation
- أين يصبح الطابع الـ COM قويّاً بشكل خاصّ
- من أين تدخل أوّلاً بين Source Reader / Sink Writer / Media Session / MFT
أمثلة الكود مبنيّة على C++، لكنّ طريقة التفكير تكاد تكون نفسها حتّى عندما تصل إلى Media Foundation من .NET عبر غلاف.
المحتويات
- النسخة المختصرة
- جدول التوجيه الأوّل
- الشكل العامّ لـ Media Foundation
- الأماكن التي يبدأ فيها Media Foundation بالظهور بمظهر COM
- 4.1.
CoInitializeExوMFStartupيظهران بجانب بعضهما عند التهيئة - 4.2. حدود الكائنات قائمة في معظمها على الواجهات
- 4.3. تظهر كائنات التفعيل
- 4.4. تدور معلومات الإعداد ونوع الوسائط حول
IMFAttributesو GUIDs - 4.5. عدم التزامن و callbacks وخيوط التنفيذ تُعالج أيضاً بأسلوب يشبه COM
- 4.6. لكنّ Media Foundation ليس «مجرّد COM»
- 4.1.
- استخدام تقريبيّ كقاعدة عامّة
- قائمة فحص عمليّة
- مقتطفات من الكود
- الخلاصة
- المراجع
1. النسخة المختصرة
- Media Foundation هو منصّة للتعامل مع الصوت والفيديو. سطح الـ API كاملاً ليس ببساطة COM خالصاً.
- لكنّ الحدود بين source / transform / sink / activation / attributes / callbacks تُعبَّر عنها عبر واجهات COM، لذلك بمجرّد استخدامه، تظهر طبيعيّاً مفاهيم
IUnknownوHRESULTو GUIDs ونقاشات apartment / threading. - عادةً يكون الأسهل أن تبدأ بـ Source Reader / Sink Writer أوّلاً، ثمّ تنتقل إلى Media Session عندما تحتاج إلى التحكّم بالتشغيل، وتدخل إلى MFT عندما تحتاج إلى مكوّن transform خاصّ بك.
بعبارة أخرى، Media Foundation هو منصّة لمعالجة الوسائط حدودها مشكَّلة بعمق بواسطة COM.
بمجرّد أن تفهم ذلك أوّلاً، يصبح من الأسهل بكثير رؤية لماذا تبدأ الـ API فجأةً بارتداء وجه COM.
2. جدول التوجيه الأوّل
2.1. ما الذي تلمسه أوّلاً حسب هدفك
هذا الجدول الأوّل يسهّل اختيار نقطة الدخول.
| ما تريد فعله | أوّل شيء تلمسه | مدى قوّة الإحساس بالـ COM | ملاحظات |
|---|---|---|---|
| سحب frames / samples من ملفّ أو كاميرا | Source Reader | متوسّط | يمكنه أيضاً الاهتمام بتحميل الـ decoder عند الحاجة |
| كتابة صوت / فيديو مولَّد إلى ملفّ | Sink Writer | متوسّط | يمكنه إدارة الـ encoders و media sinks معاً |
| التعامل مع التشغيل والإيقاف المؤقّت والـ seek والتزامن A/V والتحكّم بالجودة | Media Session | عالٍ | تحتاج إلى فهم مفهومَي topology و session |
| إدراج مكوّن transform أو codec من إنشائك | MFT | عالٍ | تصبح الواجهة الأساسيّة هي IMFTransform |
| تعداد المرشّحين أوّلاً وإنشاء النسخة الفعليّة لما تحتاجه فقط | IMFActivate |
عالٍ | ما تستعيده قد لا يكون الكائن الحقيقيّ بعد |
2.2. أين يظهر وجه الـ COM
| المكان | ما يظهر | ما يجب فهمه أوّلاً |
|---|---|---|
| التهيئة | CoInitializeEx, MFStartup |
تهيئة COM وتهيئة Media Foundation منفصلتان |
| إنشاء / نقل الكائنات | IMFSourceReader, IMFMediaType, IMFTransform |
معظم الأشياء عبارة عن مؤشّرات واجهة بالإضافة إلى HRESULT |
| الإعداد | IMFAttributes, GUIDs |
تُمثَّل الإعدادات ومعلومات نوع الوسائط على شكل بيانات key/value بالإضافة إلى GUIDs |
| التعداد / الإنشاء المؤجَّل | IMFActivate, ActivateObject |
نتائج التعداد ليست دائماً الكائن النهائيّ |
| التدفّق غير المتزامن | IMFSourceReaderCallback, work queues |
تحتاج إلى التفكير في الـ callbacks والـ apartments |
| التحكّم بالتشغيل | topology, Media Session | تدفّق خطّ الأنابيب الكامل هو مفهوم خاصّ بـ Media Foundation |
2.3. مصطلحات تستحقّ الفهم أوّلاً
| المصطلح | المعنى هنا |
|---|---|
| Media Source | نقطة الدخول التي تدخل منها بيانات الوسائط إلى خطّ الأنابيب. يمكن أن يكون ملفّاً أو مصدر شبكة أو جهاز التقاط، وهكذا |
| MFT | Media Foundation Transform. النموذج الموحَّد للـ decoders والـ encoders وكتل معالجة الفيديو/الصوت |
| Media Sink | وجهة بيانات الوسائط، مثل التشغيل أو الإخراج أو كتابة الملفّ |
| Media Session | المكوّن الذي يدير خطّ الأنابيب ككلّ، خاصّةً التشغيل والتزامن |
| Topology | رسم بيانيّ يصف كيفيّة ربط عقد source / transform / sink |
| Activation Object | كائن مساعد يُستخدم لإنشاء الكائن الحقيقيّ لاحقاً. في Media Foundation يُمثَّل عادةً بـ IMFActivate |
| Attributes | مخزن key/value مبنيّ على مفاتيح GUID. يستخدمه Media Foundation في كلّ مكان |
إذا أبقيت هذه المصطلحات في ذهنك أوّلاً، يصبح قراءة التوثيق أسهل بكثير.
3. الشكل العامّ لـ Media Foundation
على مستوى عالٍ، Media Foundation يدور حول media pipeline. الـ COM مهمّ، لكن من الأسهل غالباً فهم الشكل العامّ أوّلاً.
flowchart TB
subgraph Pipeline["Using the whole pipeline"]
Source1["Media Source"] --> Transform1["MFT"]
Transform1 --> Sink1["Media Sink"]
Session["Media Session"] --- Source1
Session --- Transform1
Session --- Sink1
end
subgraph Direct["Application handles data more directly"]
Source2["Media Source"] --> Reader["Source Reader (+ decoder)"]
App["Application"] --> Writer["Sink Writer (+ encoder)"]
Writer --> Sink2["Media Sink"]
end
هناك تقريباً طريقتان لاستخدام Media Foundation:
- استخدام خطّ الأنابيب ككلّ
- ربط source / transform / sink والسماح لـ Media Session بإدارة تدفّق البيانات والتزامن A/V
- التعامل مع البيانات بشكل أكثر مباشرةً في التطبيق
- سحب البيانات من مصدر باستخدام Source Reader ودفعها نحو مخرج باستخدام Sink Writer
الأخير غالباً ما يكون أسهل عندما تريد معالجة الـ frames أو الـ samples بنفسك. الأوّل هو المسار الأكثر طبيعيّة إذا كنت تريد أن تتولّى المنصّة التشغيل والتزامن.
النقطة المهمّة هي أنّ Media Foundation هو منصّة لمعالجة الوسائط، وليس مجرّد كيس من كائنات COM تعبث بها مباشرةً.
لكن بمجرّد أن تبدأ بالنظر إلى الحدود بين تلك المكوّنات، يصبح وجه الـ COM أقوى بكثير. هذا هو موضوع القسم التالي.
4. الأماكن التي يبدأ فيها Media Foundation بالظهور بمظهر COM
4.1. CoInitializeEx و MFStartup يظهران بجانب بعضهما عند التهيئة
هذا أحد أوائل الأشياء التي يبدو غريباً.
قبل أن تتمكّن من «فتح ملفّ فقط» أو «سحب البيانات من كاميرا فقط»، ترى كلاً من CoInitializeEx و MFStartup.
CoInitializeExيهيّئ COMMFStartupيهيّئ منصّة Media Foundation
لذلك، تهيئة COM وحدها ليست كافية. هذه غالباً هي اللحظة الأولى التي يصبح فيها واضحاً أنّ هذا ليس مجرّد «واجهة فيديو» بل منصّة فيها عقد قائم على COM بعمق تحت السطح.
في الكود العمليّ، يساعد أن تقرّر هذه الأمور مبكّراً:
- أيّ thread سيستخدم Media Foundation
- ما إذا كان هذا الـ thread سيكون STA أم MTA
- مَن يملك
MFStartup/MFShutdownوCoInitializeEx/CoUninitialize
إذا أبقيت ذلك غامضاً، تصبح مشاكل الـ callback ودمج الـ UI أصعب بكثير في الفهم لاحقاً.
4.2. حدود الكائنات قائمة في معظمها على الواجهات
بمجرّد أن تبدأ بقراءة واجهات Media Foundation، تكتشف أنّ كثيراً من القيم المُعادة ومعاملات الإخراج هي واجهات COM:
IMFSourceReaderIMFMediaTypeIMFTransformIMFActivateIMFSampleIMFMediaBuffer
ما يهمّ هنا هو أنّ ليس بيانات الوسائط نفسها فحسب، بل أوصاف الأنواع وكائنات الإعداد أيضاً تُمثَّل كواجهات.
على سبيل المثال:
IMFTransformيمثّل MFTIMFAttributesهو مخزن key/valueIMFMediaTypeهو في جوهره وصف لتنسيق الوسائط مبنيّ فوقIMFAttributes
لذلك حتّى الشيء الذي يبدو وكأنّه «بيانات إعداد» مُشكَّل أيضاً على هيئة واجهة COM.
لذلك يدخل IUnknown و QueryInterface و AddRef / Release و HRESULT إلى الصورة بشكل طبيعيّ.
flowchart TD
IUnknown["IUnknown"]
IUnknown --> IMFAttributes["IMFAttributes"]
IMFAttributes --> IMFMediaType["IMFMediaType"]
IMFAttributes --> IMFActivate["IMFActivate"]
IUnknown --> IMFSourceReader["IMFSourceReader"]
IUnknown --> IMFTransform["IMFTransform"]
عند هذه النقطة يصبح من الأسهل بكثير القول:
«Media Foundation هو واجهة وسائط، لكنّ حدوده مُشكَّلة بطريقة COM للغاية.»
4.3. تظهر كائنات التفعيل
أحد الأماكن التي يصبح فيها الطابع الـ COM واضحاً بشكل خاصّ هو نموذج كائنات التفعيل.
IMFActivate كائن مساعد يُستخدم لإنشاء الكائن الحقيقيّ لاحقاً.
أسهل نموذج ذهنيّ هو أن يكون الإحساس به نوعاً ما مشابهاً لـ COM class factory.
يظهر هذا في الأماكن التي لا ترجع فيها واجهات التعداد فوراً كائن transform أو sink قابل للاستخدام مباشرةً.
بدلاً من ذلك، تُرجع أوّلاً مصفوفة من IMFActivate*.
ثمّ تفحص المرشّح، وفقط لاحقاً تستدعي ActivateObject لإنشاء النسخة الفعليّة.
sequenceDiagram
participant App as App
participant Enum as Enumeration API
participant Act as IMFActivate
participant Obj as IMFTransform / Sink / etc.
App->>Enum: enumerate candidates
Enum-->>App: array of IMFActivate*
App->>Act: inspect attributes
App->>Act: ActivateObject(...)
Act-->>App: real COM object
تنسجم هذه البنية جيّداً مع كيفيّة رغبة Media Foundation في:
- اكتشاف الكتل القابلة للتبادل
- فحصها
- وإنشاء النسخة الفعليّة فقط لما هو مطلوب فعلاً
بما أنّ كائنات التفعيل يمكن أن تحمل خصائص أيضاً، فإنّ التدفّق يصبح طبيعيّاً:
- فحص بيانات تعريف المرشّح
- ربّما تكوينه
- إنشاء النسخة الفعليّة لاحقاً
هذا شكل يشبه COM إلى حدّ بعيد.
4.4. تدور معلومات الإعداد ونوع الوسائط حول IMFAttributes و GUIDs
لحظة أخرى يبدو فيها Media Foundation فجأةً «شديد الـ COM» هي عندما تتحوّل الإعدادات إلى غابة من GUIDs.
مركز تلك الغابة هو IMFAttributes.
IMFAttributes هو مخزن key/value مفاتيحه GUIDs، ويستخدمه Media Foundation باستمرار.
الشيء المهمّ بشكل خاصّ هو IMFMediaType.
IMFMediaType يرث من IMFAttributes ويخزّن معلومات تنسيق الوسائط كخصائص.
الأمثلة النموذجيّة تشمل:
- النوع الرئيسيّ (صوت أو فيديو)
- النوع الفرعيّ (
H.264وAACوRGB32وPCMوغيرها) - حجم الإطار
- معدّل الإطارات
- معدّل العيّنات
- عدد القنوات
flowchart LR
MediaType["IMFMediaType"] --> Major["MF_MT_MAJOR_TYPE"]
MediaType --> Subtype["MF_MT_SUBTYPE"]
MediaType --> Detail["frame size / FPS / sample rate / etc."]
من السهل أن تختبر هذا كـ «غابة من GUIDs.» لكنّ التصميم الكامن وراء ذلك مباشر فعلاً:
- استخدام مخزن خصائص للاحتفاظ بالإعدادات
- تمثيل أنواع الوسائط من خلال نموذج مخزن الخصائص نفسه
- التفاوض على التنسيقات بين source / transform / sink بقراءة تلك الخصائص ومطابقتها
لذلك النقطة الفعليّة هي:
Media Foundation يستخدم واجهات شبيهة بـ COM و GUIDs لتمثيل الإعدادات وبيانات تعريف نوع الوسائط.
4.5. عدم التزامن و callbacks وخيوط التنفيذ تُعالج أيضاً بأسلوب يشبه COM
مكان آخر يسهل التهوين من شأنه عمليّاً هو السلوك غير المتزامن ونموذج الخيوط.
على سبيل المثال، Source Reader متزامن افتراضيّاً.
في الوضع المتزامن، يَحجز ReadSample.
حسب الملفّ أو الشبكة أو الجهاز، يمكن أن يصبح زمن الحجز هذا ملحوظاً جدّاً.
إذا أردت الوضع غير المتزامن، تنشئ Source Reader مع callback. هذا يعني:
- تنفيذ
IMFSourceReaderCallback - وضع ذلك الكائن في خاصّيّة
MF_SOURCE_READER_ASYNC_CALLBACK - ثمّ إنشاء Source Reader
هناك أيضاً نقطة threading تهمّ كثيراً: المعالجة غير المتزامنة في Media Foundation تستخدم work queue، وخيوط work queue الخاصّة بـ Media Foundation هي MTA.
هذا يعني أنّه من الأبسط غالباً إبقاء جانب التطبيق في MTA أيضاً.
sequenceDiagram
participant App as App thread
participant Reader as Source Reader
participant Queue as MF work queue (MTA)
participant Cb as IMFSourceReaderCallback
App->>Reader: ReadSample(...)
Reader-->>App: returns immediately
Reader->>Queue: internal processing
Queue->>Cb: OnReadSample(...)
النقاط العمليّة هي:
- لا تلمس كائنات UI الخاصّة بـ STA مباشرةً داخل الـ callback
- اجعل تنفيذ الـ callback آمناً لخيوط التنفيذ المتعدّدة
- إذا كانت تحديثات الـ UI مطلوبة، أعد النتيجة فقط إلى thread الـ UI
- قرّر مبكّراً أيّ خيوط من المتوقّع أن تستقبل callbacks الخاصّة بـ Media Foundation
Media Foundation لا يمتصّ افتراضات STA الخاصّة بك سحريّاً. لذلك في كثير من التطبيقات يكون من الأبسط أن:
تُبقي كود عمّال Media Foundation على جانب MTA، وتبني جسراً صريحاً للعودة إلى الـ UI.
4.6. لكنّ Media Foundation ليس «مجرّد COM»
عند هذه النقطة، يسهل أن تفكّر:
«إذاً Media Foundation هو في الأساس COM.»
هذا ليس صحيحاً تماماً.
Media Foundation له مفاهيم تتجاوز COM العامّ:
MFStartup/MFShutdown- Media Session
- topology
- topology loader
- presentation clock
- Source Reader / Sink Writer
هذه ليست مجرّد مفاهيم COM. إنّها النموذج الخاصّ بـ Media Foundation على مستوى المنصّة للتعامل مع خطوط أنابيب الوسائط.
على سبيل المثال، في Media Session، يمكن للتطبيق أن يقدّم topology جزئيّاً، ويمكن لـ topology loader أن يحلّه إلى topology كامل بتزويد الـ transforms المطلوبة. هذا ليس مجرّد «COM بشكل عامّ.» إنّه Media Foundation يتصرّف كمنصّة لمعالجة الوسائط.
flowchart LR
Partial["Partial Topology<br/>Source -> Output"] --> Loader["Topology Loader"]
Loader --> Full["Full Topology<br/>Source -> Decoder MFT -> Output"]
لذلك أسهل طريقة لفهمه هي كصورة من طبقتين:
- Media Foundation يستخدم COM للتعبير عن عقود المكوّنات
- وفوق ذلك، يعمل كمنصّة لمعالجة الوسائط بمفاهيم خطّ أنابيب خاصّة به
هذا التأطير يجعل الـ API أسهل بكثير في الاستيعاب.
5. استخدام تقريبيّ كقاعدة عامّة
هذا التفرّع الأوّل يكفي بشكل مفاجئ في كثير من الأحيان:
flowchart TD
Start["What do you want to do?"] --> Q1{"What is the first thing you need?"}
Q1 -- "I want to read frames / samples" --> A1["Source Reader"]
Q1 -- "I want to write media to a file" --> A2["Sink Writer"]
Q1 -- "I need playback control and A/V sync" --> A3["Media Session"]
Q1 -- "I want to insert a custom transform" --> A4["MFT"]
5.1. ابدأ بـ Source Reader عندما تريد سحب samples
Source Reader هو نقطة دخول سهلة المنال جدّاً عندما تريد سحب بيانات الوسائط من ملفّ أو جهاز.
التطابقات الجيّدة تشمل:
- استخراج الـ frames من ملفّ فيديو
- فكّ ترميز الـ samples الصوتيّة
- سحب الـ frames من كاميرا
- تغذية مصادر Media Foundation إلى خطّ أنابيب المعالجة الخاصّ بك
يمكن لـ Source Reader أن يهتمّ بتحميل الـ decoder عند الحاجة. لكنّه لا يدير presentation clocks أو التزامن A/V أو التشغيل الفعليّ نيابةً عنك.
من الأسهل التفكير فيه على أنّه:
نقطة دخول لـ قراءة البيانات، وليست نقطة دخول للتشغيل الكامل.
5.2. استخدم Sink Writer عندما تريد كتابة الملفّات
Sink Writer هو النظير على جانب الإخراج.
التطابقات الجيّدة تشمل:
- كتابة الـ frames المولَّدة إلى ملفّ فيديو
- ترميز الـ samples الصوتيّة
- تحويل تنسيق إلى آخر وتخزين النتيجة
يمكن لـ Sink Writer أيضاً جلب الـ encoders و media sinks إلى التدفّق عند الضرورة.
5.3. استخدم Media Session عندما يهمّ التشغيل والتزامن
إذا كان هدفك الفعليّ ليس مجرّد «سحب samples» بل «تشغيل الوسائط بشكل صحيح»، فإنّ Media Session عادةً هو المركز الصحيح.
هو تطابق جيّد عندما تحتاج إلى:
- تشغيل / إيقاف مؤقّت / seek
- تزامن الصوت/الفيديو
- التحكّم بالجودة عبر خطّ الأنابيب
- الربط القائم على topology بين sources / transforms / sinks
عند هذا المستوى، أنت أقرب بكثير إلى Media Foundation نفسه منك إلى مساعدات Source Reader / Sink Writer الأبسط.
5.4. استخدم MFT عندما تحتاج إلى إدراج مكوّناتك الخاصّة
MFT هو نموذج الـ transform الموحَّد في Media Foundation.
تدخل هذا العالم عندما تريد:
- بناء decoder أو encoder خاصّ بك
- إدراج كتلة معالجة صوت / فيديو مخصّصة
- تعداد مكوّنات الـ transform واختيارها بنفسك
- التحكّم بخطّ الأنابيب بشكل أكثر صراحةً من المسار التلقائيّ
عالم MFT يجلب IMFTransform و IMFActivate والتفاوض على نوع الوسائط وإدارة الـ samples / buffers أقرب بكثير إلى السطح.
لهذا السبب يكون عادةً أوضح عدم البدء هناك أوّلاً ما لم تكن بحاجة فعليّة لذلك.
6. قائمة فحص عمليّة
| البند | ما يجب تقريره / فحصه | ما يميل إلى الخطأ إذا تجاوزته |
|---|---|---|
| ملكيّة التهيئة | قرّر أين يُستدعى CoInitializeEx و MFStartup وأين تعيش نظائر الإيقاف الخاصّة بهما |
تهيئة مفقودة وترتيب إيقاف مربك |
| نموذج apartment | قرّر مسبقاً ما إذا كانت الخيوط ذات الصلة STA أم MTA | ارتباك الـ callback وتصادمات الـ UI |
| وضع Source Reader | قرّر متزامن مقابل غير متزامن وقت الإنشاء | حجز غير متوقّع أو تغييرات وضع متأخّرة مستحيلة |
| التفاوض على نوع الوسائط | عدّد بشكل صريح واختر تنسيقات الإخراج الفعليّة | MF_E_INVALIDMEDIATYPE وعدم تطابق تنسيقات غير متوقّع |
| عمر الكائنات | اجعل مسؤوليّة Release و Unlock والإيقاف صريحة |
تسرّبات و buffers عالقة وعدم اتّساق في الإيقاف |
| التعامل مع كائنات التفعيل | ميّز بين الكائن الحقيقيّ وغلاف IMFActivate |
فشل لأنّك افترضت أنّ المرشّح كان فعلاً الكائن النهائيّ |
| فهم topology | اعرف ما إذا كنت تنظر إلى topology جزئيّ أم topology كامل محلول | التعلّق وأنت تتوقّع أن يحدث الربط التلقائيّ سحريّاً |
| فحص الأخطاء | افحص HRESULT و stream flags والأحداث باتّساق |
تفوّت الإخفاقات الجزئيّة |
| دمج الـ UI | لا تدع الـ callbacks تلمس الـ UI مباشرةً؛ مرّر النتيجة بشكل صريح | تعليقات وحالات سباق وإخفاقات عبر الخيوط مربكة |
الأولويّات الثلاث الأكثر أهمّيّة هي:
- اختيار واجهة الدخول الصحيحة أوّلاً
- تقرير نموذج apartment قبل أن ينمو كلّ شيء حوله
- عدم التعامل مع التفاوض على نوع الوسائط باستهانة
7. مقتطفات من الكود
هذه ليست مقصودة كأمثلة كاملة. إنّها قصيرة بما يكفي لإظهار أين يبدأ Media Foundation بالشعور بأنّه COM.
7.1. التهيئة
template <class T>
void SafeRelease(T** pp)
{
if (pp != nullptr && *pp != nullptr)
{
(*pp)->Release();
*pp = nullptr;
}
}
HRESULT InitializeMediaFoundationForCurrentThread()
{
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(hr))
{
return hr;
}
hr = MFStartup(MF_VERSION);
if (FAILED(hr))
{
CoUninitialize();
return hr;
}
return S_OK;
}
void UninitializeMediaFoundationForCurrentThread()
{
MFShutdown();
CoUninitialize();
}
هذه إحدى أوائل النقاط التي يصبح فيها شكل الـ COM مرئيّاً:
CoInitializeExMFStartup
إذا كانت طبقة أخرى تملك بالفعل تهيئة COM، فلا بأس بذلك أيضاً. المهمّ هو جعل المسؤوليّة صريحة.
7.2. إنشاء Source Reader في الوضع المتزامن
HRESULT ReadOneVideoSample(PCWSTR path)
{
IMFSourceReader* pReader = nullptr;
IMFMediaType* pType = nullptr;
IMFSample* pSample = nullptr;
HRESULT hr = MFCreateSourceReaderFromURL(path, nullptr, &pReader);
if (FAILED(hr)) goto done;
hr = MFCreateMediaType(&pType);
if (FAILED(hr)) goto done;
hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
if (FAILED(hr)) goto done;
hr = pType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
if (FAILED(hr)) goto done;
hr = pReader->SetCurrentMediaType(
MF_SOURCE_READER_FIRST_VIDEO_STREAM,
nullptr,
pType);
if (FAILED(hr)) goto done;
DWORD streamFlags = 0;
LONGLONG timestamp = 0;
hr = pReader->ReadSample(
MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
nullptr,
&streamFlags,
×tamp,
&pSample);
if (FAILED(hr)) goto done;
// Process IMFMediaBuffer extracted from pSample here
done:
SafeRelease(&pSample);
SafeRelease(&pType);
SafeRelease(&pReader);
return hr;
}
ما يجعله هذا المثال مرئيّاً هو:
- الـ reader ونوع الوسائط هما واجهات COM
- الإعداد قائم على GUID
- النتائج محمولة عبر
HRESULT - الوضع المتزامن يعني أنّ
ReadSampleيَحجز
حتّى «أريد فقط إطاراً واحداً» يبدو بالفعل مُشكَّلاً بطريقة COM للغاية عند حدود Media Foundation.
7.3. إنشاء Source Reader في الوضع غير المتزامن
HRESULT CreateSourceReaderAsync(
PCWSTR path,
IMFSourceReaderCallback* pCallback,
IMFSourceReader** ppReader)
{
IMFAttributes* pAttributes = nullptr;
HRESULT hr = MFCreateAttributes(&pAttributes, 1);
if (FAILED(hr))
{
return hr;
}
hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, pCallback);
if (SUCCEEDED(hr))
{
hr = MFCreateSourceReaderFromURL(path, pAttributes, ppReader);
}
SafeRelease(&pAttributes);
return hr;
}
هنا، يُختار الوضع غير المتزامن بوضع الـ callback في مجموعة الخصائص قبل إنشاء الـ reader.
هذا يعني:
- الـ callback نفسه واجهة COM
- يُكوَّن الوضع غير المتزامن من خلال
IMFAttributes - يُقرَّر الوضع وقت الإنشاء
عمليّاً، من المهمّ أن يكون تنفيذ IMFSourceReaderCallback آمناً لخيوط التنفيذ المتعدّدة وألّا يلتقط كائنات UI مباشرةً.
7.4. تعداد وتفعيل MFTs باستخدام MFTEnumEx
HRESULT FindH264Decoder(IMFTransform** ppTransform)
{
*ppTransform = nullptr;
IMFActivate** ppActivate = nullptr;
UINT32 count = 0;
MFT_REGISTER_TYPE_INFO inputType = {};
inputType.guidMajorType = MFMediaType_Video;
inputType.guidSubtype = MFVideoFormat_H264;
HRESULT hr = MFTEnumEx(
MFT_CATEGORY_VIDEO_DECODER,
MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT,
&inputType,
nullptr,
&ppActivate,
&count);
if (FAILED(hr) || count == 0)
{
return FAILED(hr) ? hr : MF_E_TOPO_CODEC_NOT_FOUND;
}
hr = ppActivate[0]->ActivateObject(IID_PPV_ARGS(ppTransform));
for (UINT32 i = 0; i < count; ++i)
{
SafeRelease(&ppActivate[i]);
}
CoTaskMemFree(ppActivate);
return hr;
}
ما يهمّ هنا هو أنّ التعداد لا يسلّمك بالضرورة الـ transform النهائيّ مباشرةً. قد تحصل على كائنات تفعيل أوّلاً وتفحصها وفقط بعدها تنشئ النسخة الفعليّة من الـ transform الفعليّ.
هذا جزء مميّز جدّاً من تصميم Media Foundation المُشكَّل بطريقة COM.
8. الخلاصة
Media Foundation ليس «مجرّد COM»، لكنّه يستخدم COM بالتأكيد لتعريف كثير من حدوده الأكثر أهمّيّة.
إذا فهمت:
- لماذا يجلس
CoInitializeExوMFStartupجنباً إلى جنب - لماذا الكثير من الكائنات الأساسيّة قائم على الواجهات
- لماذا تظهر طبيعيّاً كائنات التفعيل وخصائص GUID و callbacks ومخاوف apartment
فإنّ الـ API يصبح أسهل بكثير في القراءة.
أبسط قاعدة عمليّة هي:
- ابدأ بـ Source Reader / Sink Writer إذا استطعت
- انتقل إلى Media Session عندما يكون تنظيم التشغيل مهمّاً
- ادخل إلى MFT فقط عندما تكون مشكلتك الفعليّة هي دمج transform مخصّص
هذا يُبقي حجم العبء المفهوميّ أكثر قابليّة للإدارة بكثير.
9. المراجع
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
كيفيّة دمج الصورة والنصّ في كلّ إطار من فيديو MP4 باستخدام Media Foundation - ترتيب Source Reader والرسم وتحويل الألوان وSink Writer مع نسخة بملفّ .cpp واحد قابل للّصق مباشرةً
نُنظِّم في هذا المقال خطوات Media Foundation لرسم شعار ونصّ على كلّ إطار MP4 بعقليّة Source Reader ثمّ Sink Writer، مع نموذج C++ بملفّ وا...
كيفيّة تحويل إطارات YUV إلى RGB باستخدام Media Foundation - أنماط التحويل التلقائيّ في Source Reader والتحويل اليدويّ
دليل عمليّ لتحويل إطارات YUV إلى RGB في Media Foundation: التحويل التلقائيّ عبر Source Reader مقابل التحويل اليدويّ لـ NV12 و YUY2 مع col...
كيفيّة استخراج صورة ثابتة من MP4 باستخدام Media Foundation - ملفّ .cpp واحد يمكن لصقه في تطبيق وحدة تحكّم C++
دليل عمليّ لاستخراج إطار ثابت قرب لحظة محدّدة من ملفّ MP4 عبر Source Reader في Media Foundation، مع معالجة الـ seek و stride واتّجاه الصو...
المزالق الشائعة في تطوير مكوّنات COM و OCX / ActiveX - فخاخ Visual Studio بين 32-bit و 64-bit، والتسجيل، وصلاحيّات المسؤول
دليل عمليّ يكشف الأسباب الحقيقيّة لإخفاق مكوّنات COM و OCX و ActiveX: عدم تطابق 32-bit / 64-bit مع Visual Studio 2022، وأخطاء regsvr32 و ...
المزالق وأفضل الممارسات عند استخدام shared memory - تنظيم مسبق للتزامن، الرؤية، العمر، ABI، والأمان
نُلخّص أبرز المزالق عند استخدام shared memory ونصمّم للتزامن، الرؤية، العمر، ABI، والاستعادة، حتّى يبني القارئ تكاملاً ثابتاً منخفض الأعطال.
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
دعم إعادة استخدام الأصول القديمة وترحيلها
ندعم إعادة استخدام وترحيل الأصول التي تحمل قيود COM / ActiveX / OCX أو 32bit / 64bit.