تونلسازی چند رسانهای، دادههای ویدیوی فشرده را قادر میسازد تا از طریق رمزگشای ویدیوی سختافزاری مستقیماً به صفحه نمایش تونل شوند، بدون اینکه توسط کد برنامه یا کد فریمورک اندروید پردازش شوند. کد مخصوص دستگاه در زیر پشته Android تعیین میکند که کدام فریمهای ویدیویی به نمایشگر ارسال شود و چه زمانی باید آنها را با مقایسه مهرهای زمانی ارائه فریم ویدیو با یکی از انواع ساعت داخلی زیر ارسال کرد:
برای پخش ویدیوی درخواستی در اندروید 5 یا بالاتر، یک ساعت
AudioTrack
که با مُهرهای زمانی ارائه صوتی ارسال شده توسط برنامه همگامسازی شده است.برای پخش پخش زنده در Android 11 یا بالاتر، یک ساعت مرجع برنامه (PCR) یا ساعت سیستم (STC) که توسط تیونر هدایت می شود
پس زمینه
پخش ویدیوی سنتی در اندروید، زمانی که یک فریم ویدیوی فشرده رمزگشایی شد، به برنامه اطلاع میدهد . سپس این برنامه فریم ویدیوی رمزگشایی شده را روی نمایشگر رها می کند تا در همان زمان ساعت سیستم با فریم صوتی مربوطه رندر شود و نمونه های تاریخی AudioTimestamps
برای محاسبه زمان بندی صحیح بازیابی می کند .
از آنجایی که پخش ویدیوی تونلی شده کد برنامه را دور می زند و تعداد فرآیندهای انجام شده روی ویدیو را کاهش می دهد، بسته به اجرای OEM می تواند رندر ویدیویی کارآمدتری را ارائه دهد. همچنین میتواند آهنگ و همگامسازی دقیقتر ویدیویی را با ساعت انتخابی (PRC، STC، یا صدا) با اجتناب از مشکلات زمانبندی ناشی از انحراف احتمالی بین زمانبندی درخواستهای اندروید برای رندر کردن ویدیو و زمانبندی سختافزار واقعی و همگامسازی فراهم کند. با این حال، تونلسازی همچنین میتواند پشتیبانی از جلوههای GPU مانند تار شدن یا گوشههای گرد در پنجرههای تصویر در تصویر (PiP) را کاهش دهد، زیرا بافرها پشته گرافیکی اندروید را دور میزنند.
نمودار زیر نشان می دهد که چگونه تونل سازی فرآیند پخش ویدیو را ساده می کند.
شکل 1. مقایسه فرآیندهای پخش ویدیوی سنتی و تونلی شده
برای توسعه دهندگان برنامه
از آنجایی که اکثر توسعه دهندگان برنامه برای اجرای بازپخش با یک کتابخانه ادغام می شوند، در بیشتر موارد پیاده سازی فقط نیاز به پیکربندی مجدد آن کتابخانه برای پخش تونلی دارد. برای اجرای سطح پایین پخش کننده ویدیوی تونل شده، از دستورالعمل های زیر استفاده کنید.
برای پخش ویدیوی درخواستی در Android 5 یا بالاتر:
یک نمونه
SurfaceView
ایجاد کنید.یک نمونه
audioSessionId
ایجاد کنید.نمونه های
AudioTrack
وMediaCodec
را با نمونهaudioSessionId
ایجاد شده در مرحله 2 ایجاد کنید.داده های صوتی را با مهر زمانی ارائه برای اولین فریم صوتی در داده های صوتی
AudioTrack
قرار دهید.
برای پخش پخش زنده در اندروید 11 یا بالاتر:
یک نمونه
SurfaceView
ایجاد کنید.یک نمونه
avSyncHwId
را ازTuner
دریافت کنید.نمونه های
AudioTrack
وMediaCodec
را با نمونهavSyncHwId
ایجاد شده در مرحله 2 ایجاد کنید.
جریان تماس API در قطعه کد زیر نشان داده شده است:
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 شکافهای منفی را با متوقف کردن فریم ویدیوی فعلی، و شکافهای مثبت را با انداختن فریمهای ویدیو یا قرار دادن فریمهای صوتی بیصدا (بسته به اجرای OEM) تصحیح میکند. موقعیت قاب
AudioTimestamp
برای فریم های صوتی بی صدا درج شده افزایش نمی یابد.
برای سازندگان دستگاه
پیکربندی
OEM ها باید یک رمزگشای ویدیویی جداگانه برای پشتیبانی از پخش ویدیوی تونلی ایجاد کنند. این رمزگشا باید اعلام کند که قادر به پخش تونلی در فایل 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 باید یک دسته باند جانبی را برگرداند که می تواند برای مرتبط کردن کدک با لایه Hardware Composer (HWC) استفاده شود. هنگامی که برنامه یک سطح را با MediaCodec
مرتبط می کند، این دسته باند جانبی از طریق SurfaceFlinger
به HWC منتقل می شود، که لایه را به عنوان یک لایه باند جانبی پیکربندی می کند.
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
در اندروید 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, ¶ms);
if (c2err == C2_OK && params.size() == 1u) {
C2PortTunnelHandleTuning::output *videoTunnelSideband =
C2PortTunnelHandleTuning::output::From(params[0].get());
return OK;
}
اگر مؤلفه از این پیکربندی پشتیبانی میکند، باید یک دسته باند جانبی به این کدک اختصاص دهد و آن را از طریق C2PortTunnelHandlingTuning
برگرداند تا HWC بتواند کدک مرتبط را شناسایی کند.
HAL صوتی
برای پخش ویدیوی درخواستی، Audio HAL مُهرهای زمانی ارائهی صوتی را به صورت همزمان با دادههای صوتی در قالب big-endian در داخل هدر موجود در ابتدای هر بلوک از دادههای صوتی که برنامه مینویسد دریافت میکند:
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 نیاز به تجزیه فراداده در داخل داده های صوتی فشرده برای تعیین مدت زمان پخش آن داشته باشد.
توقف پشتیبانی
اندروید 5 یا پایینتر شامل پشتیبانی مکث نمیشود. میتوانید پخش تونلشده را فقط با گرسنگی A/V متوقف کنید، اما اگر بافر داخلی ویدیو بزرگ باشد (مثلاً یک ثانیه داده در مؤلفه OMX وجود دارد)، باعث میشود که مکث پاسخگو نباشد.
در اندروید 5.1 یا بالاتر، AudioFlinger
از مکث و ازسرگیری برای خروجی های صوتی مستقیم (تونل شده) پشتیبانی می کند. اگر HAL مکث و رزومه را پیاده سازی کند، مکث آهنگ و رزومه به HAL ارسال می شود.
توالی مکث، تراز، از سرگیری تماس با اجرای تماسهای HAL در رشته پخش (همانند بارگذاری) رعایت میشود.
پیشنهادات اجرایی
HAL صوتی
برای Android 11، شناسه همگامسازی HW از PCR یا STC را میتوان برای همگامسازی A/V استفاده کرد، بنابراین پخش جریانی فقط ویدیویی پشتیبانی میشود.
برای 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)
وقتی یک لایه تونلشده (لایهای با HWC_SIDEBAND
compositionType
) روی نمایشگر وجود دارد، sidebandStream
لایه، دسته باند جانبی است که توسط مؤلفه ویدیوی OMX اختصاص داده شده است.
HWC فریمهای ویدیوی رمزگشایی شده (از جزء تونلشده OMX) را با آهنگ صوتی مرتبط (با شناسه audio-hw-sync
) همگامسازی میکند. هنگامی که یک فریم ویدیویی جدید جاری می شود، HWC آن را با محتویات جاری تمام لایه های دریافت شده در آخرین تماس آماده یا تنظیم ترکیب می کند و تصویر حاصل را نمایش می دهد. فراخوانی آماده یا تنظیم تنها زمانی اتفاق میافتد که لایههای دیگر تغییر میکنند، یا زمانی که ویژگیهای لایه باند جانبی (مانند موقعیت یا اندازه) تغییر میکند.
شکل زیر HWC را نشان میدهد که با سختافزار (یا هسته یا درایور) همگامکننده کار میکند، تا فریمهای ویدیو (7b) را با آخرین ترکیب (7a) برای نمایش در زمان صحیح، بر اساس صدا (7c) ترکیب کند.
شکل 2. همگام ساز سخت افزار HWC (یا هسته یا درایور).