RED: تحسين جودة الصوت مع التكرار



مرة أخرى في أبريل 2020 ، أبلغت Citizenlab عن تشفير Zoom الضعيف نوعًا ما وذكر أن Zoom كان يستخدم برنامج ترميز الصوت SILK. لسوء الحظ ، لم تحتوي المقالة على البيانات الأولية لتأكيد ذلك ومنحني فرصة الرجوع إليها في المستقبل. ومع ذلك ، بفضل ناتالي سيلفانوفيتش من Google Project Zeroوبالنسبة لأداة التتبع Frida ، فقد تمكنت من الحصول على تفريغ بعض إطارات SILK الخام. ألهمني تحليلهم لإلقاء نظرة على كيفية تعامل WebRTC مع الصوت. عندما يتعلق الأمر بجودة المكالمات المتصورة بشكل عام ، فإن جودة الصوت هي الأكثر تأثيرًا ، حيث نميل إلى ملاحظة بعض الثغرات الصغيرة. كانت عشر ثوانٍ فقط من التحليل كافية للانطلاق في مغامرة حقيقية - البحث عن خيارات لتحسين جودة الصوت التي يوفرها WebRTC.



لقد تعاملت مع عميل Zoom الأصلي في عام 2017 (قبل نشر DataChannel ) ولاحظت أن حزم الصوت الخاصة به كانت في بعض الأحيان كبيرة جدًا مقارنة بحزم الحلول المستندة إلى WebRTC:



يوضح الرسم البياني أعلاه عدد الحزم بطول حمولة UDP محدد. تعتبر الحزم بين 150 و 300 بايت غير عادية عند مقارنتها بمكالمة WebRTC النموذجية. إنها أطول بكثير من الحزم التي نحصل عليها عادةً من Opus. لقد اشتبهنا في وجود تحكم في الأخطاء الأمامية (FEC) أو تكرار ، ولكن بدون الوصول إلى الإطارات غير المشفرة ، كان من الصعب استخلاص المزيد من الاستنتاجات أو القيام بشيء ما.



أظهرت إطارات SILK غير المشفرة في التفريغ الجديد توزيعًا مشابهًا جدًا. بعد تحويل الإطارات إلى ملف ثم تشغيل رسالة قصيرة (بفضل Giacomo Vacca لمنشور مدونة مفيد للغايةتصف الخطوات اللازمة) عدت إلى Wireshark ونظرت في الحزم. إليك مثال على ثلاث حزم وجدتها مثيرة للاهتمام بشكل خاص:



packet 7:
e9e4ab17ad8b9b5176b1659995972ac9b63737f8aa4d83ffc3073d3037b452fe6e1ee
5e6e68e6bcd73adbd59d3d31ea5fdda955cbb7f

packet 8: 
e790ba4908639115e02b457676ea75bfe50727bb1c44144d37f74756f90e1ab926ef
930a3ffc36c6a8e773a780202af790acfbd6a4dff79698ea2d96365271c3dff86ce6396
203453951f00065ec7d26a03420496f

packet 9:
e93997d503c0601e918d1445e5e985d2f57736614e7f1201711760e4772b020212dc
854000ac6a80fb9a5538741ddd2b5159070ebbf79d5d83363be59f10ef
e790ba4908639115e02b457676ea75bfe50727bb1c44144d37f74756f90e1ab926ef
930a3ffc36c6a8e773a780202af790acfbd6a4dff79698ea2d96365271c3dff86ce6396
203453951f00065ec7d26a03420496f
e9e4ab17ad8b9b5176b1659995972ac9b63737f8aa4d83ffc3073d3037b452fe6e1ee
5e6e68e6bcd73adbd59d3d31ea5fdda955cbaef


تحتوي الحزمة 9 على حزمتين سابقتين ، الحزمة 8-1 الحزمة السابقة. ينتج هذا التكرار عن استخدام تنسيق LBRR - Low Bit-Rate Redundancy ، والذي تم توضيحه من خلال دراسة عميقة لوحدة فك ترميز SILK (يمكن العثور عليها في مشروع الإنترنت المقدم من فريق Skype ، أو في المستودع على GitHub ):





يستخدم Zoom SKP_SILK_LBRR_VER1 ولكن مع حزمتين احتياطيتين. إذا كانت كل حزمة UDP تحتوي ليس فقط على إطار الصوت الحالي ، ولكن أيضًا على الإطارين السابقين ، فستكون قوية حتى إذا فقدت اثنتين من الحزم الثلاث. إذن ، ربما يكون مفتاح جودة صوت Zoom هو وصفة سكايب السرية للجدة؟



أوبوس FEC



كيف يمكنني تحقيق الشيء نفسه مع WebRTC؟ كانت الخطوة الواضحة التالية هي النظر في Opus FEC.



تم العثور أيضًا على LBRR (حجز منخفض السعر) من SILK في Opus (تذكر أن Opus هو برنامج ترميز هجين يستخدم SILK للنهاية الأدنى من نطاق معدل البت). ومع ذلك ، فإن Opus SILK يختلف تمامًا عن SILK الأصلي ، والذي تم اكتشاف رمز المصدر الخاص به من قبل بواسطة Skype ، كما هو الحال مع جزء LBRR المستخدم في وضع التحكم في الأخطاء.



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





على الرغم من أن الجهود لم تنجح ، إلا أنها أنتجت بعض الإحصائيات حول تأثير LBRR ، والتي تظهر في الشكل أعلاه. يستخدم LBRR معدلات بت تصل إلى 10 كيلو بت في الثانية (أو ثلثي معدل البيانات) لفقدان الحزمة العالي. المستودع متاح هنا . لا يتم عرض هذه الإحصائيات عند استدعاء WebRTC getStats() API ، لذلك كانت النتائج مسلية للغاية.



تحويل الشفرة ليس المشكلة الوحيدة مع Opus FEC. كما اتضح ، فإن إعداداته في WebRTC عديمة الفائدة إلى حد ما:





إن طرح معدل بت FEC من الحد الأقصى لمعدل البت المستهدف لا معنى له على الإطلاق - يعمل FEC بشكل فعال على تقليل معدل البت للتيار الرئيسي. عادةً ما ينتج عن تدفق معدل البت المنخفض جودة أقل. إذا لم يكن هناك فقدان للحزم يمكن تصحيحه باستخدام FEC ، فإن FEC سيؤدي فقط إلى تدهور الجودة وليس تحسينها. لماذا يحدث ذلك؟ النظرية الرئيسية هي أن الازدحام هو أحد أسباب فقدان الحزم. إذا كنت تعاني من الازدحام ، فلن ترغب في إرسال المزيد من البيانات لأن ذلك لن يؤدي إلا إلى تفاقم المشكلة. ومع ذلك ، كما يصف إميل إيفوف في حديثه الممتاز لعام 2017 KrankyGeekلا يكون الازدحام دائمًا سبب فقدان الحزمة. بالإضافة إلى ذلك ، يتجاهل هذا النهج أيضًا أي تدفقات فيديو مصاحبة. إن إستراتيجية FEC القائمة على الازدحام لصوت Opus ليست منطقية كثيرًا عندما ترسل مئات الكيلوبتات من الفيديو إلى جانب دفق صغير نسبيًا يبلغ 50 كيلوبت في الثانية. ربما سنرى في المستقبل بعض التغييرات في libopus ، لكن في الوقت الحالي أود محاولة تعطيله ، لأنه ممكّن حاليًا في WebRTC افتراضيًا .



نستنتج أن هذا لا يناسبنا ...



أحمر



إذا كنا نريد تكرارًا حقيقيًا ، فإن RTP لديها حل يسمى RTP Payload for Redundant Audio Data ، أو RED. إنه قديم جدًا ، تمت كتابة RFC 2198 في عام 1997 . يسمح الحل بحمولات RTP متعددة مع طوابع زمنية مختلفة ليتم وضعها في حزمة RTP نفسها بتكلفة منخفضة نسبيًا.



إن استخدام RED لوضع إطار واحد أو اثنين من الإطارات الصوتية الزائدة في كل حزمة من شأنه أن يمنح قوة أكبر بكثير لفقد الحزم من Opus FEC. ولكن هذا ممكن فقط من خلال مضاعفة معدل بت الصوت أو مضاعفته ثلاث مرات من 30 كيلوبت في الثانية إلى 60 أو 90 كيلوبت في الثانية (مع 10 كيلوبت في الثانية إضافية للرأس). بالمقارنة مع أكثر من 1 ميغا بايت من بيانات الفيديو في الثانية ، هذا ليس سيئًا للغاية.



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



وهو متاح كتجربة يتم تضمينها عندما يبدأ Chrome بالعلامات التالية:

--force-fieldtrials=WebRTC-Audio-Red-For-Opus/Enabled/


ثم يمكن تمكين RED عبر مفاوضات SDP ؛ سيتم عرضه مثل هذا:

a=rtpmap:someid red/48000/2


لا يتم تمكينه افتراضيًا نظرًا لوجود بيئات لا يعتبر فيها استخدام النطاق الترددي الإضافي فكرة جيدة. لاستخدام RED ، قم بتغيير ترتيب برامج الترميز بحيث تأتي قبل برنامج ترميز Opus. يمكن القيام بذلك باستخدام API RTCRtpTransceiver.setCodecPreferencesكما هو موضح هنا . من الواضح أن هناك بديلًا آخر وهو تغيير SDP يدويًا. يمكن أن يوفر تنسيق SDP أيضًا طريقة لتكوين الحد الأقصى لمستوى التكرار ، لكن دلالات استجابة العرض RFC 2198 لم تكن واضحة تمامًا ، لذلك قررت تأجيل ذلك لفترة من الوقت.



يمكنك توضيح كيفية عمل كل هذا من خلال تشغيله في مثال صوتي . هذا ما تبدو عليه النسخة القديمة مع حزمة نسخ احتياطي واحدة:





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







يُظهر فحص طول الحزمة النتيجة المتوقعة: الحزم ، في المتوسط ​​، ضعف الطول (أطول) مقارنة بالتوزيع الطبيعي لطول الحمولة الموضح أدناه.



لا يزال هذا مختلفًا قليلاً عما يفعله Zoom ، حيث رأينا الحجوزات الجزئية. دعنا نعيد النظر في الرسم البياني لطول حزمة التكبير الذي تم عرضه سابقًا لمشاهدة المقارنة:



إضافة دعم اكتشاف النشاط الصوتي (VAD)



يرسل Opus FEC بيانات النسخ الاحتياطي فقط إذا كان هناك نشاط صوتي في الحزمة. يجب تطبيق نفس الشيء على تنفيذ RED. لهذا الغرض ، يجب تغيير مشفر Opus لعرض معلومات VAD الصحيحة ، والتي يتم تحديدها على مستوى SILK. باستخدام هذا الإعداد ، يصل معدل البت إلى 60 كيلوبت في الثانية فقط في وجود الكلام (مقارنة بـ 60+ كيلوبت في الثانية الثابت):





ويصبح "الطيف" أشبه بما رأيناه مع Zoom:





التغيير لتحقيق ذلك لم يظهر بعد.



إيجاد المسافة الصحيحة



المسافة هي عدد الحزم الاحتياطية ، أي عدد الحزم السابقة في الحزمة الحالية. في عملية العمل لإيجاد المسافة الصحيحة ، وجدنا أنه إذا كان RED على مسافة 1 باردًا ، فإن RED على مسافة 2 يكون أكثر برودة. يحاكي تقديرنا المعملي خسارة رزمة عشوائية بنسبة 60٪. في هذه البيئة ، كان أداء Opus + RED صوتًا ممتازًا ، بينما كان أداء Opus بدون RED أسوأ بكثير. يوفر API يتطلب WebRTC getStats () قدرة مفيدة جدا لقياس هذا بمقارنة نسبة العينات مخبأة بقسمة concealedSamples التي كتبها totalSamplesReceived .



في صفحة نماذج الصوت ، يمكن استرداد هذه البيانات بسهولة مع لصق مقتطف JavaScript هذا في وحدة التحكم:

(await pc2.getReceivers()[0].getStats()).forEach(report => {
  if(report.type === "track") console.log(report.concealmentEvents, report.concealedSamples, report.totalSamplesReceived, report.concealedSamples / report.totalSamplesReceived)})


قمت بإجراء اختبارين لفقدان الحزمة باستخدام علامة غير مشهورة ولكنها مفيدة للغاية WebRTCFakeNetworkReceiveLossPercent:

--force-fieldtrials=WebRTC-Audio-Red-For-Opus/Enabled/WebRTCFakeNetworkReceiveLossPercent/20/


عند فقدان الحزمة بنسبة 20٪ وتمكين FEC افتراضيًا ، لم يكن هناك فرق كبير في جودة الصوت ، ولكن كان هناك اختلاف بسيط في المقياس:

سيناريو نسبة الخسارة
بدون أحمر الثامنة عشر٪
لا أحمر ، FEC معطل 20٪
أحمر مع المسافة 1
أحمر مع المسافة 2 0.7٪


بدون RED أو FEC ، يتطابق المقياس تقريبًا مع فقدان الحزمة المطلوب. يوجد تأثير FEC ، لكنه صغير.



بدون RED ، عند فقدان 60٪ ، تصبح جودة الصوت رديئة نوعًا ما ، معدنية قليلاً ، والكلمات صعبة الفهم:

سيناريو نسبة الخسارة
بدون أحمر 60٪
أحمر مع المسافة 1 32٪
أحمر مع المسافة 2 الثامنة عشر٪


كانت هناك بعض القطع الأثرية المسموعة في RED بمسافة = 1 ، لكن صوتًا مثاليًا تقريبًا مع المسافة 2 (وهو مقدار التكرار المستخدم حاليًا).

هناك شعور بأن الدماغ البشري يمكنه تحمل مستوى معين من الصمت الذي يحدث بشكل غير منتظم. ( ويبدو أن Google Duo يستخدم خوارزمية التعلم الآلي لملء الصمت).



قياس الأداء في العالم الحقيقي



نأمل أن يؤدي تضمين RED في Opus إلى تحسين جودة الصوت ، على الرغم من أنه في بعض الحالات قد يزيد الأمر سوءًا. تطوع إميل إيفوف لإجراء بعض اختبارات الاستماع باستخدام طريقة POLQA-MOS. لقد تم القيام بذلك بالفعل لـ Opus ، لذلك لدينا أساس للمقارنة.

إذا أظهرت الاختبارات الأولية نتائج واعدة ، فسنجري تجربة واسعة النطاق على الفحص الرئيسي لـ Jitsi Meet باستخدام مقاييس خسارة النسبة المئوية التي استخدمناها أعلاه.



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



شكراً جزيلاً لـ Jitsi / 8 × 8 Inc لرعايتها هذه المغامرة المثيرة والأشخاص في Google الذين حللوا التغييرات المطلوبة وقدموا تعليقات عليها.



وبدون ناتالي سيلفانوفيتش ، كنت سأجلس أبحث في البايتات المشفرة!



All Articles