نفق الوسائط المتعددة

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.

يتيح نفق الوسائط المتعددة نقل بيانات الفيديو المضغوطة عبر نفق عبر وحدة فك ترميز فيديو الأجهزة مباشرةً إلى شاشة العرض ، دون معالجتها بواسطة رمز التطبيق أو رمز إطار عمل Android. يحدد الكود الخاص بالجهاز الموجود أسفل مكدس Android إطارات الفيديو التي سيتم إرسالها إلى الشاشة ومتى يتم إرسالها عن طريق مقارنة الطوابع الزمنية لعرض إطار الفيديو بأحد الأنواع التالية من الساعة الداخلية:

  • لتشغيل الفيديو عند الطلب في Android 5 أو أعلى ، تمت مزامنة ساعة AudioTrack مع الطوابع الزمنية للعرض الصوتي التي يمر بها التطبيق

  • لتشغيل البث المباشر في Android 11 أو أعلى ، ساعة مرجعية للبرنامج (PCR) أو ساعة وقت النظام (STC) يقودها موالف

خلفية

يقوم تشغيل الفيديو التقليدي على Android بإعلام التطبيق عند فك تشفير إطار فيديو مضغوط. يقوم التطبيق بعد ذلك بإصدار إطار الفيديو الذي تم فك تشفيره إلى الشاشة ليتم عرضه في نفس وقت ساعة النظام مثل إطار الصوت المقابل ، واسترداد مثيلات AudioTimestamps التاريخية لحساب التوقيت الصحيح.

نظرًا لأن تشغيل الفيديو عبر الأنفاق يتجاوز رمز التطبيق ويقلل من عدد العمليات التي تعمل على الفيديو ، فيمكنه توفير عرض فيديو أكثر كفاءة اعتمادًا على تنفيذ OEM. يمكن أن يوفر أيضًا إيقاعًا ومزامنة فيديو أكثر دقة للساعة المختارة (PRC أو STC أو الصوت) من خلال تجنب مشكلات التوقيت الناتجة عن الانحراف المحتمل بين توقيت طلبات Android لتقديم الفيديو وتوقيت الأجهزة الحقيقية. ومع ذلك ، يمكن أن يقلل الاتصال النفقي أيضًا من دعم تأثيرات GPU مثل عدم الوضوح أو الزوايا الدائرية في نوافذ صورة داخل صورة (PiP) ، لأن المخازن المؤقتة تتجاوز مكدس رسومات Android.

يوضح الرسم البياني التالي كيف يبسط الاتصال النفقي عملية تشغيل الفيديو.

مقارنة بين الأنماط التقليدية والأنفاق

الشكل 1. مقارنة بين عمليات تشغيل الفيديو التقليدية والنفقية

لمطوري التطبيقات

نظرًا لأن معظم مطوري التطبيقات يتكاملون مع مكتبة لتنفيذ التشغيل ، فإن التنفيذ في معظم الحالات يتطلب فقط إعادة تكوين تلك المكتبة للتشغيل النفقي. للتنفيذ منخفض المستوى لمشغل الفيديو النفقي ، استخدم الإرشادات التالية.

لتشغيل الفيديو عند الطلب في Android 5 أو أعلى:

  1. قم بإنشاء مثيل SurfaceView .

  2. قم بإنشاء مثيل audioSessionId .

  3. قم بإنشاء مثيلات AudioTrack و MediaCodec باستخدام مثيل audioSessionId الذي تم إنشاؤه في الخطوة 2.

  4. ضع بيانات الصوت في قائمة الانتظار إلى AudioTrack مع الطابع الزمني للعرض التقديمي لأول إطار صوتي في البيانات الصوتية.

لتشغيل البث المباشر في نظام Android 11 أو إصدار أحدث:

  1. قم بإنشاء مثيل SurfaceView .

  2. احصل على مثيل avSyncHwId من Tuner .

  3. قم بإنشاء مثيلات AudioTrack و MediaCodec باستخدام مثيل avSyncHwId الذي تم إنشاؤه في الخطوة 2.

يظهر تدفق استدعاء واجهة برمجة التطبيقات في مقتطفات التعليمات البرمجية التالية:

aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE);

// configure for audio clock sync
aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC);
// or, for tuner clock sync (Android 11 or higher)
new tunerConfig = TunerConfiguration(0, avSyncId);
aab.setTunerConfiguration(tunerConfig);
if (codecName == null) {
  return FAILURE;
}

// configure for audio clock sync
mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId);
// or, for tuner clock sync (Android 11 or higher)
mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId);

سلوك تشغيل الفيديو عند الطلب

نظرًا لأن تشغيل الفيديو النفق عند الطلب مرتبط ضمنيًا بتشغيل AudioTrack ، فقد يعتمد سلوك تشغيل الفيديو النفقي على سلوك تشغيل الصوت.

  • في معظم الأجهزة ، افتراضيًا ، لا يتم عرض إطار الفيديو حتى يبدأ تشغيل الصوت. ومع ذلك ، قد يحتاج التطبيق إلى عرض إطار فيديو قبل بدء تشغيل الصوت ، على سبيل المثال ، لتظهر للمستخدم موضع الفيديو الحالي أثناء البحث.

    • للإشارة إلى وجوب عرض إطار الفيديو الأول في قائمة الانتظار بمجرد فك تشفيره ، اضبط المعلمة PARAMETER_KEY_TUNNEL_PEEK على 1 . عند إعادة ترتيب إطارات الفيديو المضغوطة في قائمة الانتظار (كما هو الحال عند وجود إطارات B ) ، فهذا يعني أن أول إطار فيديو معروض يجب أن يكون دائمًا إطار I.

    • إذا كنت لا تريد عرض إطار الفيديو الأول في قائمة الانتظار حتى يبدأ تشغيل الصوت ، فاضبط هذه المعلمة على 0 .

    • إذا لم يتم تعيين هذه المعلمة ، يحدد OEM سلوك الجهاز.

  • عندما لا يتم توفير بيانات الصوت إلى AudioTrack وتكون المخازن المؤقتة فارغة (الصوت السفلي) ، يتوقف تشغيل الفيديو حتى تتم كتابة المزيد من البيانات الصوتية لأن الساعة الصوتية لم تعد تتقدم.

  • أثناء التشغيل ، قد تظهر حالات التوقف التي يتعذر على التطبيق تصحيحها في الطوابع الزمنية للعرض الصوتي. عند حدوث ذلك ، يقوم المصنِّع الأصلي للجهاز بتصحيح الفجوات السلبية عن طريق إيقاف إطار الفيديو الحالي ، والفجوات الإيجابية إما بإسقاط إطارات الفيديو أو إدخال إطارات صوت صامتة (اعتمادًا على تنفيذ OEM). لا يزيد موضع إطار AudioTimestamp لإطارات الصوت الصامتة المدرجة.

لمصنعي الأجهزة

ترتيب

يجب على مصنعي المعدات الأصلية إنشاء وحدة فك ترميز فيديو منفصلة لدعم تشغيل الفيديو النفقي. يجب أن تعلن وحدة فك التشفير هذه عن قدرتها على التشغيل النفقي في ملف media_codecs.xml :

<Feature name="tunneled-playback" required="true"/>

عندما يتم تكوين مثيل MediaCodec النفقي بمعرف جلسة الصوت ، فإنه يستعلم عن برنامج AudioFlinger لمعرف HW_AV_SYNC هذا:

if (entry.getKey().equals(MediaFormat.KEY_AUDIO_SESSION_ID)) {
    int sessionId = 0;
    try {
        sessionId = (Integer)entry.getValue();
    }
    catch (Exception e) {
        throw new IllegalArgumentException("Wrong Session ID Parameter!");
    }
    keys[i] = "audio-hw-sync";
    values[i] = AudioSystem.getAudioHwSyncForSession(sessionId);
}

أثناء هذا الاستعلام ، يسترد برنامج AudioFlinger معرف HW_AV_SYNC من جهاز الصوت الأساسي ويربطه داخليًا بمعرف جلسة الصوت:

audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
char *reply = dev->get_parameters(dev, AUDIO_PARAMETER_HW_AV_SYNC);
AudioParameter param = AudioParameter(String8(reply));
int hwAVSyncId;
param.getInt(String8(AUDIO_PARAMETER_HW_AV_SYNC), hwAVSyncId);

إذا تم بالفعل إنشاء مثيل AudioTrack ، فسيتم تمرير معرف HW_AV_SYNC إلى دفق الإخراج بنفس معرف جلسة الصوت. إذا لم يتم إنشاؤه بعد ، فسيتم تمرير معرف HW_AV_SYNC إلى دفق الإخراج أثناء إنشاء AudioTrack . يتم ذلك عن طريق مؤشر ترابط التشغيل :

mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC, hwAVSyncId);

يتم تمرير معرف HW_AV_SYNC ، سواء كان يتوافق مع دفق إخراج صوتي أو تكوين Tuner ، إلى مكون OMX أو Codec2 بحيث يمكن لرمز OEM ربط برنامج الترميز بدفق خرج الصوت المقابل أو دفق الموالف.

أثناء تكوين المكون ، يجب أن يقوم مكون OMX أو Codec2 بإرجاع مقبض النطاق الجانبي الذي يمكن استخدامه لربط برنامج الترميز بطبقة مؤلف الأجهزة (HWC). عندما يربط التطبيق سطحًا بـ MediaCodec ، يتم تمرير مقبض النطاق الجانبي هذا إلى HWC من خلال SurfaceFlinger ، والذي يقوم بتكوين الطبقة كطبقة نطاق جانبي .

err = native_window_set_sideband_stream(nativeWindow.get(), sidebandHandle);
if (err != OK) {
  ALOGE("native_window_set_sideband_stream(%p) failed! (err %d).", sidebandHandle, err);
  return err;
}

HWC هي المسؤولة عن تلقي المخازن المؤقتة للصور الجديدة من إخراج برنامج الترميز في الوقت المناسب ، إما متزامنة مع دفق إخراج الصوت المرتبط أو الساعة المرجعية لبرنامج الموالف ، وتكوين المخازن المؤقتة بالمحتويات الحالية للطبقات الأخرى ، وعرض الصورة الناتجة. يحدث هذا بشكل مستقل عن دورة الإعداد والتعيين العادية. تحدث مكالمات التحضير والتعيين فقط عندما تتغير الطبقات الأخرى ، أو عندما تتغير خصائص طبقة النطاق الجانبي (مثل الموضع أو الحجم).

OMX

يجب أن يدعم مكون مفكك الشفرة النفقي ما يلي:

  • إعداد المعلمة OMX.google.android.index.configureVideoTunnelMode الموسعة ، والتي تستخدم بنية ConfigureVideoTunnelModeParams لتمرير معرف HW_AV_SYNC المرتبط بجهاز الإخراج الصوتي.

  • تكوين معلمة OMX_IndexConfigAndroidTunnelPeek التي تخبر برنامج الترميز بتقديم أو عدم عرض إطار الفيديو الأول الذي تم فك تشفيره ، بغض النظر عما إذا كان تشغيل الصوت قد بدأ أم لا.

  • إرسال حدث OMX_EventOnFirstTunnelFrameReady عندما يتم فك تشفير أول إطار للفيديو النفقي ويكون جاهزًا للعرض.

يعمل تطبيق AOSP على تكوين وضع النفق في ACodec من خلال OMXNodeInstance كما هو موضح في مقتطف الكود التالي:

OMX_INDEXTYPE index;
OMX_STRING name = const_cast<OMX_STRING>(
        "OMX.google.android.index.configureVideoTunnelMode");

OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);

ConfigureVideoTunnelModeParams tunnelParams;
InitOMXParams(&tunnelParams);
tunnelParams.nPortIndex = portIndex;
tunnelParams.bTunneled = tunneled;
tunnelParams.nAudioHwSync = audioHwSync;
err = OMX_SetParameter(mHandle, index, &tunnelParams);
err = OMX_GetParameter(mHandle, index, &tunnelParams);
sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow;

إذا كان المكون يدعم هذا التكوين ، فيجب أن يخصص معالج النطاق الجانبي لبرنامج الترميز هذا ويمرره مرة أخرى عبر عضو pSidebandWindow حتى يتمكن HWC من تحديد برنامج الترميز المرتبط. إذا كان المكون لا يدعم هذا التكوين ، فيجب تعيين bTunneled على OMX_FALSE .

الترميز 2

في Android 11 أو أعلى ، يدعم Codec2 التشغيل النفقي. يجب أن يدعم مكون وحدة فك التشفير ما يلي:

  • تكوين C2PortTunneledModeTuning ، الذي يقوم بتكوين وضع النفق ويمر في HW_AV_SYNC المسترجع إما من جهاز إخراج الصوت أو تكوين الموالف.

  • الاستعلام C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE ، لتخصيص واسترداد مقبض النطاق الجانبي لـ HWC.

  • معالجة C2_PARAMKEY_TUNNEL_HOLD_RENDER عند إرفاقه بـ C2Work ، والذي يوجه برنامج الترميز لفك تشفير وإشارة اكتمال العمل ، ولكن ليس لتقديم المخزن المؤقت للإخراج حتى 1) يُطلب من برنامج الترميز لاحقًا تقديمه أو 2) يبدأ تشغيل الصوت.

  • معالجة C2_PARAMKEY_TUNNEL_START_RENDER ، والتي توجّه برنامج الترميز إلى تقديم الإطار الذي تم تمييزه بـ C2_PARAMKEY_TUNNEL_HOLD_RENDER على الفور ، حتى إذا لم يبدأ تشغيل الصوت.

  • اترك debug.stagefright.ccodec_delayed_params (مستحسن). إذا قمت بتكوينه ، فاضبط على false .

يعمل تطبيق AOSP على تهيئة وضع النفق في CCodec من خلال C2PortTunnelModeTuning ، كما هو موضح في مقتطف الشفرة التالي:

if (msg->findInt32("audio-hw-sync", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::AUDIO_HW_SYNC;
} else if (msg->findInt32("hw-av-sync-id", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::HW_AV_SYNC;
} else {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::REALTIME;
    tunneledPlayback->setFlexCount(0);
}
c2_status_t c2err = comp->config({ tunneledPlayback.get() }, C2_MAY_BLOCK,
        failures);
std::vector<std::unique_ptr<C2Param>> params;
c2err = comp->query({}, {C2PortTunnelHandleTuning::output::PARAM_TYPE},
        C2_DONT_BLOCK, &params);
if (c2err == C2_OK && params.size() == 1u) {
    C2PortTunnelHandleTuning::output *videoTunnelSideband =
            C2PortTunnelHandleTuning::output::From(params[0].get());
    return OK;
}

إذا كان المكون يدعم هذا التكوين ، فيجب أن يخصص مقبض النطاق الجانبي لبرنامج الترميز هذا ويمرره مرة أخرى عبر C2PortTunnelHandlingTuning حتى يتمكن HWC من تحديد برنامج الترميز المرتبط.

صوت HAL

لتشغيل الفيديو عند الطلب ، يتلقى Audio HAL الطوابع الزمنية للعرض الصوتي المضمنة مع البيانات الصوتية بتنسيق كبير داخل رأس موجود في بداية كل كتلة من البيانات الصوتية التي يكتبها التطبيق:

struct TunnelModeSyncHeader {
  // The 32-bit data to identify the sync header (0x55550002)
  int32 syncWord;
  // The size of the audio data following the sync header before the next sync
  // header might be found.
  int32 sizeInBytes;
  // The presentation timestamp of the first audio sample following the sync
  // header.
  int64 presentationTimestamp;
  // The number of bytes to skip after the beginning of the sync header to find the
  // first audio sample (20 bytes for compressed audio, or larger for PCM, aligned
  // to the channel count and sample size).
  int32 offset;
}

لكي يقوم HWC بعرض إطارات الفيديو متزامنة مع إطارات الصوت المقابلة ، يجب على Audio HAL تحليل رأس المزامنة واستخدام الطابع الزمني للعرض التقديمي لإعادة مزامنة ساعة التشغيل مع عرض الصوت. لإعادة المزامنة عند تشغيل الصوت المضغوط ، قد تحتاج Audio HAL إلى تحليل البيانات الأولية داخل بيانات الصوت المضغوط لتحديد مدة التشغيل.

دعم وقفة

لا يتضمن Android 5 أو أقل دعم الإيقاف المؤقت. لا يمكنك إيقاف التشغيل النفقي مؤقتًا إلا عن طريق تجويع الصوت والفيديو ، ولكن إذا كان المخزن المؤقت الداخلي للفيديو كبيرًا (على سبيل المثال ، هناك ثانية واحدة من البيانات في مكون OMX) ، فإنه يجعل الإيقاف المؤقت يبدو غير مستجيب.

في Android 5.1 أو أعلى ، يدعم AudioFlinger الإيقاف المؤقت والاستئناف لمخرجات الصوت المباشرة (النفقية). إذا قام HAL بتنفيذ الإيقاف المؤقت والاستئناف ، فسيتم إعادة توجيه إيقاف التتبع المؤقت والاستئناف إلى HAL.

يتم احترام تسلسل مكالمات الإيقاف المؤقت والتدفق والاستئناف عن طريق تنفيذ مكالمات HAL في سلسلة عمليات التشغيل (مثل إلغاء التحميل).

اقتراحات التنفيذ

صوت HAL

بالنسبة لنظام Android 11 ، يمكن استخدام معرّف مزامنة HW من PCR أو STC لمزامنة الصوت والفيديو ، لذلك يتم دعم دفق الفيديو فقط.

بالنسبة لنظام التشغيل Android 10 أو الإصدارات الأقدم ، يجب أن تحتوي الأجهزة التي تدعم تشغيل الفيديو النفقي على ملف تعريف تدفق إخراج صوتي واحد على الأقل مع FLAG_HW_AV_SYNC و AUDIO_OUTPUT_FLAG_DIRECT في ملف audio_policy.conf الخاص به. تُستخدم هذه العلامات لضبط ساعة النظام من ساعة الصوت.

OMX

يجب أن يكون لدى الشركات المصنعة للأجهزة مكون OMX منفصل لتشغيل الفيديو النفقي (يمكن أن يكون لدى الشركات المصنعة مكونات OMX إضافية لأنواع أخرى من تشغيل الصوت والفيديو ، مثل التشغيل الآمن). يجب أن يكون المكون النفقي:

  • حدد 0 مخازن مؤقتة ( nBufferCountMin ، nBufferCountActual ) على منفذ الإخراج الخاص بها.

  • نفِّذ ملحق OMX.google.android.index.prepareForAdaptivePlayback setParameter .

  • حدد إمكانياته في ملف media_codecs.xml وأعلن ميزة التشغيل النفقي. يجب أن يوضح أيضًا أي قيود على حجم الإطار أو المحاذاة أو معدل البت. ويرد أدناه مثال على ذلك:

    <MediaCodec name="OMX.OEM_NAME.VIDEO.DECODER.AVC.tunneled"
    type="video/avc" >
        <Feature name="adaptive-playback" />
        <Feature name="tunneled-playback" required=”true” />
        <Limit name="size" min="32x32" max="3840x2160" />
        <Limit name="alignment" value="2x2" />
        <Limit name="bitrate" range="1-20000000" />
            ...
    </MediaCodec>
    

إذا تم استخدام نفس مكون OMX لدعم فك التشفير النفقي وغير النفق ، فيجب أن يترك ميزة التشغيل النفقي على أنها غير مطلوبة. عندئذٍ يكون لكل من مفككات الشفرة النفقية وغير الموصلة نفس قيود القدرة. ويرد أدناه مثال على ذلك:

<MediaCodec name="OMX._OEM\_NAME_.VIDEO.DECODER.AVC" type="video/avc" >
    <Feature name="adaptive-playback" />
    <Feature name="tunneled-playback" />
    <Limit name="size" min="32x32" max="3840x2160" />
    <Limit name="alignment" value="2x2" />
    <Limit name="bitrate" range="1-20000000" />
        ...
</MediaCodec>

مؤلف الأجهزة (HWC)

عندما تكون هناك طبقة نفقية (طبقة بها نوع compositionType HWC_SIDEBAND ) على شاشة عرض ، فإن sidebandStream للطبقة هو مقبض النطاق الجانبي المخصص بواسطة مكون الفيديو OMX.

يقوم HWC بمزامنة إطارات الفيديو المفكوكة (من مكون OMX النفقي) إلى المسار الصوتي المرتبط (مع معرف audio-hw-sync ). عندما يصبح إطار فيديو جديدًا حديثًا ، يقوم HWC بتكوينه بالمحتويات الحالية لجميع الطبقات المستلمة أثناء آخر مكالمة تحضيرية أو محددة ، ويعرض الصورة الناتجة. تحدث مكالمات التحضير أو الضبط فقط عندما تتغير الطبقات الأخرى ، أو عندما تتغير خصائص طبقة النطاق الجانبي (مثل الموضع أو الحجم).

يمثل الشكل التالي عمل HWC مع جهاز المزامنة (أو النواة أو برنامج التشغيل) ، لدمج إطارات الفيديو (7 ب) مع أحدث تكوين (7 أ) للعرض في الوقت الصحيح ، استنادًا إلى الصوت (7 ج).

HWC يجمع إطارات الفيديو على أساس الصوت

الشكل 2. جهاز مزامنة HWC (أو kernel أو driver)