אזהרה: OpenSL ES הוצאה משימוש. מפתחים צריכים להשתמש בספריית Oboe בקוד פתוח שזמינה ב-GitHub. Oboe הוא מעטפת של C++ שמספקת ממשק API שדומה מאוד ל-AAudio. כש-AAudio זמין, Oboe קורא ל-AAudio, ואם AAudio לא זמין, הוא עובר ל-OpenSL ES.
בדף הזה מוסבר איך ההטמעה של OpenSL ES™ ב-NDK שונה מהמפרט העזר של OpenSL ES 1.0.1. כשמשתמשים בקוד לדוגמה מהמפרט, יכול להיות שתצטרכו לשנות אותו כדי שיפעל ב-Android.
אלא אם צוין אחרת, כל התכונות זמינות ב-Android מגרסה 2.3 (רמת API 9) ואילך. חלק מהתכונות זמינות רק ב-Android 4.0 (רמת API 14). הן מסומנות בהערה.
הערה: במסמך הגדרת התאימות ל-Android (CDD) מפורטות הדרישות לחומרה ולתוכנה של מכשיר Android תואם. למידע נוסף על תוכנית התאימות הכוללת, אפשר לעיין במאמר תאימות ל-Android. למסמך ה-CDD בפועל, אפשר לעיין במאמר CDD.
OpenSL ES מספק ממשק בשפת C שאפשר לגשת אליו גם באמצעות C++. הוא חושף תכונות שדומות לחלקים של האודיו בממשקי ה-API האלה של Java ל-Android:
כמו בכל ערכת הפיתוח המקורית של Android (NDK), המטרה העיקרית של OpenSL ES ל-Android היא להקל על ההטמעה של ספריות משותפות לקריאה באמצעות ממשק Java Native (JNI ). NDK לא מיועד לכתיבת אפליקציות C/C++ בלבד. עם זאת, OpenSL ES הוא ממשק API מלא, ואנחנו צופים שתוכלו לענות על רוב הצרכים שלכם בתחום האודיו באמצעות ממשק ה-API הזה בלבד, בלי קריאות למעלה לקוד שפועל בסביבת זמן הריצה של Android.
הערה: למרות שהוא מבוסס על OpenSL ES, ממשק ה-API של האודיו המקורי של Android (אודיו בעל ביצועים גבוהים) הוא לא הטמעה תואמת של אף פרופיל OpenSL ES 1.0.1 (משחק, מוזיקה או טלפון). הסיבה לכך היא שמערכת Android לא מטמיעה את כל התכונות הנדרשות לכל אחד מהפרופילים. בדף תוספים ל-Android מתוארים מקרים ידועים שבהם התנהגות Android שונה מהמפרט.
תכונות שעברו בירושה ממפרט ההפניה
ההטמעה של OpenSL ES ב-Android NDK יורשת חלק גדול מקבוצת התכונות מהמפרט העזר, עם מגבלות מסוימות.
נקודות כניסה גלובליות
OpenSL ES ל-Android תומך בכל נקודות הכניסה הגלובליות במפרט של Android. נקודות הכניסה האלה כוללות:
slCreateEngine
slQueryNumSupportedEngineInterfaces
slQuerySupportedEngineInterfaces
אובייקטים וממשקים
בטבלה הבאה מוצגים האובייקטים והממשקים שנתמכים בהטמעה של OpenSL ES ב-Android NDK. אם הערך Yes מופיע בתא, סימן שהתכונה זמינה בהטמעה הזו.
תכונה | נגן אודיו | הקלטת אודיו | מנוע | תמהיל פלט |
---|---|---|---|---|
הגברת הבס | כן | לא | לא | כן |
תור במאגר נתונים זמני (buffer queue) | כן | לא | לא | לא |
תוסף לאיתור נתונים בתור במאגר נתונים זמני (buffer queue) | כן: מקור | לא | לא | לא |
ניהול ממשק דינמי | כן | כן | כן | כן |
שליחת אפקט | כן | לא | לא | לא |
מנוע | לא | לא | כן | לא |
הדהוד סביבתי | לא | לא | לא | כן |
אקולייזר | כן | לא | לא | כן |
כלי לאיתור נתונים של מכשירי קלט/פלט | לא | כן: מקור | לא | לא |
חילוץ מטא-נתונים | כן: פענוח ל-PCM | לא | לא | לא |
השתקה של לבד | כן | לא | לא | לא |
אובייקט | כן | כן | כן | כן |
מאתר של יצירת תערובת פלט | כן: כיור | לא | לא | לא |
Play | כן | לא | לא | לא |
קצב ההפעלה | כן | לא | לא | לא |
סטטוס השליפה מראש (prefetch) | כן | לא | לא | לא |
הדהוד מוגדר מראש | לא | לא | לא | כן |
הקלטה | לא | כן | לא | לא |
חפש | כן | לא | לא | לא |
תוסף לאיתור נתונים של URI | כן: מקור | לא | לא | לא |
Virtualizer | כן | לא | לא | כן |
עוצמת הקול | כן | לא | לא | לא |
בקטע הבא מוסבר על המגבלות של חלק מהתכונות האלה.
מגבלות
יש מגבלות מסוימות שחלות על התכונות בטבלה 1. המגבלות האלה מייצגות הבדלים מהמפרט של העזרה. בהמשך הקטע הזה מפורט מידע על ההבדלים האלה.
ניהול ממשק דינמי
ב-OpenSL ES ל-Android אין תמיכה ב-RemoveInterface
או ב-ResumeInterface
.
שילובי אפקטים: הדהוד סביבתי והדהוד מוגדר מראש
לא ניתן להפעיל גם הדהוד סביבתי וגם הדהוד מוגדרת מראש באותו תמהיל פלט.
יכול להיות שהפלטפורמה תתעלם מבקשות להפעלת אפקטים אם היא מעריכה שהעומס על המעבד יהיה גבוה מדי.
שליחת אפקט
SetSendLevel()
תומך ברמת שליחה אחת לכל נגן אודיו.
הדהוד סביבתי
ב-environmental reverb אין תמיכה בשדות reflectionsDelay
, reflectionsLevel
או reverbDelay
של המבנה SLEnvironmentalReverbSettings
.
פורמט נתונים של MIME
אפשר להשתמש בפורמט הנתונים MIME רק עם מאתר הנתונים URI, ורק לנגן אודיו. אי אפשר להשתמש בפורמט הנתונים הזה במכשיר להקלטת אודיו.
בהטמעה של OpenSL ES ב-Android, צריך לאתחל את mimeType
ל-NULL
או למחרוזת UTF-8 תקינה. צריך גם לאתחל את containerType
לערך תקין.
אם אין שיקולים אחרים, כמו ניידות להטמעות אחרות או פורמטים של תוכן שאפליקציה לא יכולה לזהות לפי כותרת, מומלץ להגדיר את mimeType
כ-NULL
ואת containerType
כ-SL_CONTAINERTYPE_UNSPECIFIED
.
OpenSL ES ל-Android תומך בפורמטים הבאים של אודיו, כל עוד פלטפורמת Android תומכת בהם גם כן:
- PCM WAV.
- WAV alaw.
- WAV ulaw.
- MP3 Ogg Vorbis.
- AAC LC.
- HE-AACv1 (AAC+).
- HE-AACv2 (enhanced AAC+).
- AMR.
- FLAC
הערה: לרשימת פורמטים של אודיו שנתמכים ב-Android, ראו פורמטים נתמכים של מדיה.
המגבלות הבאות חלות על הטיפול בפורמטים האלה ובפורמטים אחרים בהטמעה הזו של OpenSL ES:
- פורמטים של AAC חייבים להיות בתוך מאגר (container) מסוג MP4 או ADTS.
- OpenSL ES ל-Android לא תומך ב-MIDI.
- WMA הוא לא חלק מ-AOSP ולא אימתנו את התאימות שלו ל-OpenSL ES ל-Android.
- ההטמעה של OpenSL ES ב-Android NDK לא תומכת בהפעלה ישירה של DRM או תוכן מוצפן. כדי להפעיל תוכן אודיו מוגן, צריך לפענח אותו באפליקציה לפני ההפעלה, והאפליקציה צריכה לאכוף את כל ההגבלות של DRM.
שיטות שקשורות לאובייקטים
ב-OpenSL ES ל-Android אין תמיכה בשיטות הבאות לתמרן אובייקטים:
Resume()
RegisterCallback()
AbortAsyncOperation()
SetPriority()
GetPriority()
SetLossOfControlInterfaces()
פורמט נתוני PCM
PCM הוא פורמט הנתונים היחיד שאפשר להשתמש בו עם תורי אחסון למאגר נתונים זמני. ההגדרות הנתמכות של הפעלת PCM כוללות את המאפיינים הבאים:
- 8 ביט ללא סימן או 16 ביט עם סימן.
- מונו או סטריאו.
- סדר בייטים little-endian.
- תדירות דגימה של:
- 8,000 Hz.
- 11,025 הרץ.
- 12,000 Hz.
- 16,000 Hz.
- 22,050Hz
- 24,000 Hz.
- 32,000 Hz.
- 44,100 הרץ.
- 48,000 Hz
ההגדרות של OpenSL ES ל-Android לתמיכה בהקלטה תלויות במכשיר. בדרך כלל, ההגדרה 16,000Hz מונו/16-bit signed זמינה ללא קשר למכשיר.
הערך בשדה samplesPerSec
הוא ביחידות של מילי-הרץ, למרות השם המטעה. כדי להימנע משימוש בטעות בערך שגוי, מומלץ לאתחל את השדה הזה באמצעות אחת מהקבועות הסמליות שהוגדרו למטרה הזו, כמו SL_SAMPLINGRATE_44_1
.
נתונים של נקודה צפה נתמכים ב-Android מגרסה 5.0 (רמת API 21) ואילך.
קצב ההפעלה
קצב ההפעלה של OpenSL ES מציין את המהירות שבה אובייקט מציג נתונים, שמוצגת באלפים של המהירות הרגילה, או לפי אלף. לדוגמה, שיעור הפעלה של 1,000 ל-1,000 הוא 1,000/1,000, או מהירות רגילה. טווח קצב הוא מרווח סגור שמציג טווח של שיעורי הפעלה אפשריים.
התמיכה בטווחי קצב ההפעלה וביכולות אחרות עשויה להשתנות בהתאם לגרסה ולהטמעה של הפלטפורמה. האפליקציה יכולה לקבוע את היכולות האלה בזמן הריצה באמצעות PlaybackRate::GetRateRange()
או PlaybackRate::GetCapabilitiesOfRate()
כדי לשלוח שאילתה למכשיר.
בדרך כלל, מכשיר תומך באותו טווח קצב למקור נתונים בפורמט PCM, ובטווח קצב של 1,000 ל-1,000 לפורמטים אחרים. כלומר, טווח הקצב של יחידת המידה הוא למעשה ערך יחיד.
הקלטה
OpenSL ES ל-Android לא תומך באירועים SL_RECORDEVENT_HEADATLIMIT
או SL_RECORDEVENT_HEADMOVING
.
חפש
השיטה SetLoop()
מאפשרת לבצע לולאה בקובץ כולו. כדי להפעיל את הלולאה, צריך להגדיר את הפרמטר startPos
ל-0 ואת הפרמטר endPos
ל-SL_TIME_UNKNOWN
.
תוסף לאיתור נתונים בתור במאגר נתונים זמני (buffer queue)
נגן אודיו או מכשיר הקלטה עם מאתר נתונים לתור מאגר נתונים זמני תומכים בפורמט הנתונים PCM בלבד.
כלי לאיתור נתונים של מכשירי קלט/פלט
ב-OpenSL ES ל-Android אפשר להשתמש במאתר נתונים של מכשיר קלט/פלט רק אם ציינתם את המאתר כמקור הנתונים של Engine::CreateAudioRecorder()
.
מאתחלים את הכלי לאיתור נתוני המכשיר באמצעות הערכים שמופיעים בקטע הקוד הבא:
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
תוסף לאיתור נתונים של URI
OpenSL ES ל-Android יכול להשתמש במאתר הנתונים של URI רק בפורמט MIME, ורק לנגן אודיו. אי אפשר להשתמש במאתר נתוני URI למכשיר הקלטה אודיו. ה-URI יכול להשתמש רק בסכימות http:
ו-file:
. אסור להשתמש בסכמות אחרות, כמו https:
, ftp:
או content:
.
לא אימתנו תמיכה ב-rtsp:
עם אודיו בפלטפורמת Android.
מבני נתונים
Android תומך במבני הנתונים הבאים של OpenSL ES 1.0.1:
SLDataFormat_MIME
SLDataFormat_PCM
SLDataLocator_BufferQueue
SLDataLocator_IODevice
SLDataLocator_OutputMix
SLDataLocator_URI
SLDataSink
SLDataSource
SLEngineOption
SLEnvironmentalReverbSettings
SLInterfaceID
הגדרת הפלטפורמה
OpenSL ES ל-Android מיועד לאפליקציות עם מספר רב של שרשורים, והוא בטוח לשימוש בכמה שרשורים. יש תמיכה במנוע אחד לכל אפליקציה, ועד 32 אובייקטים לכל מנוע. המעבד (CPU) והזיכרון הזמינים במכשיר עשויים להגביל עוד יותר את מספר האובייקטים שאפשר להשתמש בהם.
האפשרויות הבאות של המנוע מזוהות, אבל slCreateEngine
מתעלמת מהן:
SL_ENGINEOPTION_THREADSAFE
SL_ENGINEOPTION_LOSSOFCONTROL
אפשר להשתמש ב-OpenMAX AL וב-OpenSL ES יחד באותה אפליקציה. במקרה כזה, יש אובייקט משותף אחד של מנוע חיפוש באופן פנימי, ומגבלת 32 האובייקטים משותפת בין OpenMAX AL ל-OpenSL ES. האפליקציה צריכה ליצור את שני המנועים, להשתמש בשניהם ולבסוף למחוק את שניהם. ההטמעה שומרת על ספירת הפניות במנוע המשותף, כדי שהוא ייהרס בצורה נכונה במהלך פעולת ההרס השנייה.
הערות לתכנות
הערות לתכנות ב-OpenSL ES מכילות מידע נוסף שיעזור לכם להטמיע את OpenSL ES בצורה נכונה.
הערה: כדי להקל עליכם, צירפנו עותק של המפרט של OpenSL ES 1.0.1 ל-NDK בקובץ docs/opensles/OpenSL_ES_Specification_1.0.1.pdf
.
בעיות בפלטפורמה
בקטע הזה מתוארות בעיות ידועות במהדורה הראשונית של הפלטפורמה שתומכת ב-APIs האלה.
ניהול ממשק דינמי
DynamicInterfaceManagement::AddInterface
לא פועל. במקום זאת, צריך לציין את הממשק במערך שמוענק ל-Create()
, כפי שמוצג בקוד לדוגמה של הדהוד סביבתי.
תכנון לקראת גרסאות עתידיות של OpenSL ES
ממשקי ה-API של Android לאיכות אודיו גבוהה מבוססים על Khronos Group OpenSL ES 1.0.1. Khronos פרסמה גרסה מתוקנת 1.1 של התקן. הגרסה המתוקנת כוללת תכונות חדשות, הבהרות, תיקונים של שגיאות דפוס וחלק מהאי-תאימות. רוב אי-התאימות הצפויה היא יחסית קלה, או שהיא נמצאת באזורים של OpenSL ES שלא נתמכים ב-Android.
אפליקציה שפותחה בגרסה הזו אמורה לפעול בגרסאות עתידיות של פלטפורמת Android, בתנאי שתפעלו לפי ההנחיות שמפורטות בקטע תכנון תאימות בינארית בהמשך.
הערה: המטרה היא לא תאימות עתידית של המקור. כלומר, אם משדרגים לגרסה חדשה יותר של NDK, יכול להיות שיהיה צורך לשנות את קוד המקור של האפליקציה כך שיתאים ל-API החדש. אנחנו צופים שרוב השינויים האלה יהיו קלים. פרטים נוספים מופיעים בהמשך.
תכנון התאימות הבינארית
כדי לשפר את התאימות העתידית של קובצי הבינארי, מומלץ לפעול לפי ההנחיות הבאות באפליקציה:
- יש להשתמש רק בקבוצת המשנה המתועדת של התכונות הנתמכות ב-Android מ-OpenSL ES 1.0.1.
- אל תסתמכו על קוד תוצאה מסוים של פעולה שנכשלה. צריך להיות מוכנים לקוד תוצאה אחר.
- בדרך כלל, פונקציות טיפול בקריאה חוזרת (callbacks) של אפליקציות פועלות בהקשר מוגבל. צריך לכתוב אותם כך שיבצעו את העבודה במהירות, ואז יחזרו בהקדם האפשרי. אין להריץ פעולות מורכבות בתוך פונקציית קריאה חוזרת. לדוגמה, בתוך קריאה חוזרת (callback) להשלמת תור של מאגרים, אפשר להוסיף מאגר נוסף לתור, אבל לא ליצור נגן אודיו.
- עליכם להכין את פונקציות הטיפול בקריאה החוזרת (callbacks) כך שיהיה אפשר להפעיל אותן בתדירות גבוהה או נמוכה יותר, לקבל סוגי אירועים נוספים ולהתעלם מסוגי אירועים שהן לא מזהות. צריך להכין קריאות חוזרות (callbacks) שמוגדרות עם מסכת אירועים שמכילה סוגים של אירועים מופעלים, כך שיהיה אפשר להפעיל אותן עם כמה ביטים של סוגים של אירועים מוגדרים בו-זמנית. משתמשים ב-"&" כדי לבדוק כל ביט של אירוע במקום מקרה של מתג.
- אפשר להשתמש בסטטוס של האחזור מראש ובקריאות חזרה (callbacks) כאינדיקציות כלליות להתקדמות, אבל לא להסתמך על רמות מילוי ספציפיות שמוגדרות בקוד או על רצפי קריאות חזרה ספציפיים. ייתכן שהמשמעות של רמת המילוי של סטטוס האחסון המקדים וההתנהגות של שגיאות שזוהו במהלך האחסון המקדים ישתנו.
הערה: פרטים נוספים זמינים בקטע התנהגות של תור מאגר שבהמשך.
תכנון בהתאם לתאימות למקור
כפי שצוין, צפויות אי-תאימות בקוד המקור בגרסה הבאה של OpenSL ES מ-Khronos Group. תחומי השינוי האפשריים כוללים:
- צפויים שינויים משמעותיים בממשק של תור המאגר, במיוחד בתחומים של
BufferQueue::Enqueue
, רשימת הפרמטרים שלslBufferQueueCallback
ושם השדהSLBufferQueueState.playIndex
. במקום זאת, מומלץ שקוד האפליקציה ישתמש בתורים פשוטים למאגר נתונים זמני ב-Android. לכן, בקוד לדוגמה שמסופק עם NDK, השתמשנו בתורים פשוטים של מאגרים לצורך הפעלה ב-Android. (אנחנו משתמשים גם בתור פשוט של מאגר נתונים ב-Android להקלטה ולפענוח ל-PCM, אבל זה בגלל ש-OpenSL ES 1.0.1 הסטנדרטי לא תומך בהקלטה או בפענוח למאגר נתונים של תור מאגר). - הערך
const
יתווסף לפרמטר הקלט שמוענק באמצעות הפניה, ולשדות המבנהSLchar *
שמשמשים כערכים של קלט. לא אמורים להיות שינויים בקוד. - חלק מהפרמטרים שחתומים כרגע יוחלפו בסוגי נתונים לא חתומים.
יכול להיות שתצטרכו לשנות את סוג הפרמטר מ-
SLint32
ל-SLuint32
או לאפשרות דומה, או להוסיף הטמעה (cast). Equalizer::GetPresetName
מעתיק את המחרוזת לזיכרון האפליקציה במקום להחזיר pointer לזיכרון ההטמעה. זהו שינוי משמעותי, לכן מומלץ להימנע מהפעלת השיטה הזו או לבודד את השימוש בה.- יהיו שדות נוספים בסוגי המבנה. אפשר להתעלם מהשדות החדשים האלה בפרמטרים של הפלט, אבל בפרמטרים של הקלט צריך לאתחל את השדות החדשים. למרבה המזל, כל השדות האלה צפויים להיות באזורים שלא נתמכים ב-Android.
- מזהי GUID של הממשק ישתנו. כדי למנוע תלות, צריך להפנות לממשקים לפי שם סימבולי ולא לפי GUID.
- הערך של
SLchar
ישתנה מ-unsigned char
ל-char
. ההגדרה הזו משפיעה בעיקר על מאתר הנתונים של URI ועל פורמט הנתונים של MIME. - השם של
SLDataFormat_MIME.mimeType
ישתנה ל-pMimeType
. השם שלSLDataLocator_URI.URI
ישתנה ל-pURI
. כדי לבודד את הקוד מפני השינוי הזה, מומלץ לאתחל את מבני הנתוניםSLDataFormat_MIME
ו-SLDataLocator_URI
באמצעות רשימת ערכים המופרדים בסוגריים מרובעים, המופרדים באמצעות פסיקים, ולא לפי שם השדה. הטכניקה הזו משמשת בקוד לדוגמה. SL_DATAFORMAT_PCM
לא מאפשר לאפליקציה לציין את הייצוג של הנתונים כמספר שלם עם סימן, כמספר שלם ללא סימן או כמספר עשרוני. בהטמעה של Android, ההנחה היא שנתוני 8 ביט הם מספר שלם לא חתום ו-16 ביט הם מספרים שלמים חתומים. בנוסף, השדהsamplesPerSec
שגוי, כי היחידות בפועל הן אלפיות השנייה. הבעיות האלה צפויות להיפתר בגרסה הבאה של OpenSL ES, שבה יוצג פורמט חדש של נתוני PCM מורחבים שמאפשר לאפליקציה לציין במפורש את הייצוג ומתקן את שם השדה. מכיוון שזה יהיה פורמט נתונים חדש, ופורמט נתוני ה-PCM הנוכחי עדיין יהיה זמין (אבל הוצא משימוש), לא יהיה צורך לבצע שינויים מיידיים בקוד שלך.