OpenSL ES dành cho Android

CẢNH BÁO: OpenSL ES không còn được dùng nữa. Nhà phát triển nên sử dụng thư viện Oboe nguồn mở có trên GitHub. Oboe là một trình bao bọc C++, cung cấp một API gần giống với AAudio. Oboe gọi AAudio khi có API này và quay lại sử dụng OpenSL ES nếu không có AAudio.

Trang này cung cấp thông tin chi tiết về sự khác biệt giữa quá trình triển khai OpenSL ES™ bằng NDK và thông số kỹ thuật tham chiếu cho OpenSL ES 1.0.1. Khi sử dụng mã mẫu từ thông số kỹ thuật, bạn có thể cần sửa đổi mã để dùng được trên Android.

Trừ khi có ghi chú khác, tất cả các tính năng đều có trên Android 2.3 (API cấp 9) trở lên. Một số tính năng chỉ có trên Android 4.0 (API cấp 14); tất cả đều được ghi chú.

Lưu ý: Tài liệu định nghĩa về khả năng tương thích (CDD) cho Android có liệt kê các yêu cầu về phần cứng và phần mềm đối với thiết bị Android tương thích. Xem bài viết Khả năng tương thích với Android để biết thêm thông tin về chương trình tương thích tổng thể và truy cập vào CDD để xem tài liệu CDD thực tế.

OpenSL ES cung cấp giao diện ngôn ngữ C cũng có thể truy cập được bằng C++. API này cho thấy các tính năng tương tự như các phần âm thanh của các Android Java API sau:

Giống như với tất cả Bộ phát triển mã gốc (Native Development Kit – NDK) cho Android, mục đích chính của OpenSL ES dành cho Android là tạo điều kiện triển khai các thư viện dùng chung sẽ được gọi bằng Giao diện gốc Java (Java Native Interface – JNI). NDK không dành cho việc viết những ứng dụng C/C++ thuần tuý. Tuy nhiên, OpenSL ES là một API đầy đủ tính năng và chúng tôi hy vọng bạn sẽ có thể đáp ứng hầu hết các nhu cầu âm thanh của mình chỉ bằng cách sử dụng API này, mà không cần có lệnh gọi lên trên (up-call) đến mã chạy trong môi trường Android Runtime.

Lưu ý: Mặc dù dựa trên OpenSL ES, API âm thanh gốc (âm thanh hiệu suất cao) của Android không phải là cách triển khai phù hợp cho bất kỳ cấu hình OpenSL ES 1.0.1 nào (trò chơi, âm nhạc hoặc điện thoại). Điều này là do Android không triển khai tất cả các tính năng mà bất kỳ cấu hình nào trong số đó yêu cầu. Bất kỳ trường hợp đã biết nào mà Android hoạt động khác với quy cách đều được mô tả trong trang Tiện ích Android.

Các tính năng kế thừa từ quy cách tham chiếu

Việc triển khai OpenSL ES bằng Android NDK thừa hưởng phần lớn bộ tính năng từ quy cách tham chiếu, với một số hạn chế nhất định.

Điểm truy cập trên toàn cục

OpenSL ES dành cho Android hỗ trợ tất cả các điểm truy cập trên toàn cục trong quy cách của Android. Các điểm truy cập này bao gồm:

  • slCreateEngine
  • slQueryNumSupportedEngineInterfaces
  • slQuerySupportedEngineInterfaces

Đối tượng và giao diện

Bảng sau đây cho biết các đối tượng và giao diện mà phương thức triển khai OpenSL ES bằng Android NDK hỗ trợ. Nếu xuất hiện trong ô thì tính năng này sẽ xuất hiện trong quá trình triển khai.

Hỗ trợ về Android NDK cho đối tượng và giao diện.

Tính năng Trình phát âm thanh Trình ghi âm Công cụ Trộn dữ liệu đầu ra
Tăng âm trầm Không Không
Hàng đợi bộ đệm Không Không Không
Trình định vị dữ liệu hàng đợi bộ đệm Có: Nguồn Không Không Không
Quản lý giao diện động
Gửi hiệu ứng Không Không Không
Công cụ Không Không Không
Âm vang trong môi trường Không Không Không
Bộ cân bằng âm thanh Không Không
Trình định vị dữ liệu thiết bị I/O Không Có: Nguồn Không Không
Trích xuất siêu dữ liệu Có: Giải mã sang PCM Không Không Không
Tắt tiếng bản đơn Không Không Không
Đối tượng
Công cụ định vị trộn dữ liệu đầu ra Có: Bồn lưu trữ dữ liệu Không Không Không
Phát Không Không Không
Tốc độ phát Không Không Không
Trạng thái tìm nạp trước Không Không Không
Hiệu ứng dội lại được thiết lập sẵn Không Không Không
Ghi âm Không Không Không
Tua Không Không Không
Trình định vị dữ liệu URI Có: Nguồn Không Không Không
Bộ ảo hoá Không Không
Âm lượng Không Không Không

Phần tiếp theo giải thích các hạn chế đối với một số tính năng trong số này.

Hạn chế

Có một số hạn chế nhất định áp dụng cho các tính năng trong Bảng 1. Những hạn chế này thể hiện sự khác biệt so với quy cách tham chiếu. Nội dung còn lại của phần này cung cấp thông tin về những điểm khác biệt này.

Quản lý giao diện động

OpenSL ES cho Android không hỗ trợ RemoveInterface hoặc ResumeInterface.

Kết hợp hiệu ứng: âm vang trong môi trường và hiệu ứng dội lại được thiết lập sẵn

Bạn không thể có cả âm vang trong môi trường và hiệu ứng dội lại được thiết lập sẵn trên cùng một dữ liệu đầu ra đã trộn.

Nền tảng này có thể bỏ qua các yêu cầu hiệu ứng nếu ước tính thấy CPU có khả năng phải tải quá nhiều

Gửi hiệu ứng

SetSendLevel() hỗ trợ một cấp độ gửi duy nhất cho mỗi trình phát âm thanh.

Âm vang trong môi trường

Âm vang trong môi trường không hỗ trợ các trường reflectionsDelay, reflectionsLevel hoặc reverbDelay của cấu trúc SLEnvironmentalReverbSettings.

Định dạng dữ liệu MIME

Bạn chỉ có thể sử dụng định dạng dữ liệu MIME với trình định vị dữ liệu URI và chỉ dành cho trình phát âm thanh. Bạn không thể sử dụng định dạng dữ liệu này cho trình ghi âm.

Việc triển khai OpenSL ES trên Android yêu cầu bạn khởi tạo mimeType tới NULL hoặc một chuỗi UTF-8 hợp lệ. Bạn cũng phải khởi tạo containerType đến một giá trị hợp lệ. Trong trường hợp không cần xem xét những yếu tố khác, chẳng hạn như khả năng có thể di chuyển sang các cách triển khai khác hoặc định dạng nội dung mà ứng dụng không thể xác định theo tiêu đề, bạn nên đặt mimeType thành NULL còn containerType thành SL_CONTAINERTYPE_UNSPECIFIED.

OpenSL ES dành cho Android hỗ trợ các định dạng âm thanh sau đây, miễn là nền tảng Android cũng hỗ trợ các định dạng đó:

  • WAV PCM.
  • WAV alaw.
  • WAV ulaw.
  • MP3 Ogg Vorbis.
  • AAC LC.
  • HE-AACv1 (AAC+).
  • HE-AACv2 (AAC+ nâng cao).
  • AMR.
  • FLAC.

Lưu ý: Để biết danh sách các định dạng âm thanh mà Android hỗ trợ, hãy xem Các định dạng nội dung nghe nhìn được hỗ trợ.

Các hạn chế sau đây áp dụng cho việc xử lý các định dạng này và các định dạng khác trong cách triển khai OpenSL ES này:

  • Định dạng AAC phải nằm trong vùng chứa MP4 hoặc ADTS.
  • OpenSL ES cho Android không hỗ trợ MIDI.
  • WMA không thuộc AOSP (Dự án nguồn mở Android) và chúng tôi chưa xác minh khả năng tương thích của định dạng này với OpenSL ES dành cho Android.
  • Việc triển khai OpenSL ES bằng Android NDK không hỗ trợ phát trực tiếp DRM hoặc nội dung được mã hoá. Để phát nội dung âm thanh được bảo vệ, bạn phải giải mã nội dung đó trong ứng dụng trước khi phát, đồng thời ứng dụng thực thi mọi quy định hạn chế DRM.

OpenSL ES dành cho Android không hỗ trợ các phương thức điều khiển đối tượng sau đây:

  • Resume()
  • RegisterCallback()
  • AbortAsyncOperation()
  • SetPriority()
  • GetPriority()
  • SetLossOfControlInterfaces()

Định dạng dữ liệu PCM

PCM là định dạng dữ liệu duy nhất bạn có thể sử dụng cùng với hàng đợi bộ đệm. Các cấu hình phát PCM được hỗ trợ có các đặc điểm sau:

  • 8 bit không dấu hoặc 16 bit có dấu.
  • Đơn âm hoặc âm nổi.
  • Sắp xếp thứ tự byte Little-endian.
  • Tốc độ lấy mẫu:
    • 8.000 Hz.
    • 11.025 Hz.
    • 12.000 Hz.
    • 16.000 Hz.
    • 22.050 Hz.
    • 24.000 Hz.
    • 32.000 Hz.
    • 44.100 Hz.
    • 48.000 Hz.

Các cấu hình mà OpenSL ES dành cho Android hỗ trợ cho quá trình ghi âm sẽ phụ thuộc vào thiết bị; thường là 16.000 Hz đơn âm/16 bit có dấu bất kể thiết bị là gì.

Giá trị của trường samplesPerSec tính bằng đơn vị milliHz, mặc dù tên trường dễ gây hiểu nhầm. Để tránh vô tình sử dụng sai giá trị, bạn nên khởi chạy trường này bằng một trong các hằng ký hiệu được xác định cho mục đích này, chẳng hạn như SL_SAMPLINGRATE_44_1.

Android 5.0 (API cấp 21) trở lên hỗ trợ dữ liệu dấu phẩy động.

Tốc độ phát

Tốc độ phát của OpenSL ES cho biết tốc độ mà một đối tượng trình bày dữ liệu, được biểu thị bằng phần nghìn tốc độ bình thường hoặc tính trên mỗi nghìn. Ví dụ: Tốc độ phát 1.000 trên mỗi nghìn là 1.000/1.000 hoặc tốc độ bình thường. Phạm vi tốc độ là khoảng thời gian khép kín thể hiện phạm vi tốc độ phát khả dĩ.

Việc hỗ trợ cho phạm vi tốc độ phát và các khả năng khác có thể khác nhau tuỳ thuộc vào phiên bản nền tảng và cách triển khai. Ứng dụng của bạn có thể xác định các khả năng này trong thời gian chạy bằng cách sử dụng PlaybackRate::GetRateRange() hoặc PlaybackRate::GetCapabilitiesOfRate() để truy vấn thiết bị.

Một thiết bị thường hỗ trợ cùng một phạm vi tốc độ cho nguồn dữ liệu ở định dạng PCM và phạm vi tốc độ thống nhất 1.000 trên mỗi nghìn đến 1.000 trên mỗi nghìn cho các định dạng khác; nghĩa là phạm vi tốc độ thống nhất sẽ là giá trị duy nhất một cách hiệu quả.

Ghi âm

OpenSL ES dành cho Android không hỗ trợ các sự kiện SL_RECORDEVENT_HEADATLIMIT hoặc SL_RECORDEVENT_HEADMOVING.

Tua

Phương thức SetLoop() cho phép lặp lại toàn bộ tệp. Để bật tính năng lặp lại, hãy đặt tham số startPos thành 0 và tham số endPos thành SL_TIME_UNKNOWN.

Trình định vị dữ liệu hàng đợi bộ đệm

Trình phát âm thanh hoặc trình ghi âm có trình định vị dữ liệu cho hàng đợi bộ đệm chỉ hỗ trợ định dạng dữ liệu PCM.

Trình định vị dữ liệu thiết bị I/O

OpenSL ES dành cho Android chỉ hỗ trợ sử dụng trình định vị dữ liệu thiết bị I/O khi bạn đã chỉ định trình định vị làm nguồn dữ liệu cho Engine::CreateAudioRecorder(). Khởi chạy trình định vị dữ liệu thiết bị bằng các giá trị có trong đoạn mã sau:

SLDataLocator_IODevice loc_dev =
  {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
  SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};

Trình định vị dữ liệu URI

OpenSL ES dành cho Android chỉ có thể sử dụng trình định vị dữ liệu URI với định dạng dữ liệu MIME, và chỉ dùng cho trình phát âm thanh. Bạn không thể sử dụng trình định vị dữ liệu URI cho trình ghi âm. URI chỉ có thể sử dụng giao thức http:file:. Không cho phép các giao thức khác, chẳng hạn như https:, ftp: hoặc content:.

Chúng tôi chưa xác minh khả năng hỗ trợ dành cho rtsp: có âm thanh trên nền tảng Android.

Cấu trúc dữ liệu

Android hỗ trợ các cấu trúc dữ liệu OpenSL ES 1.0.1 sau đây:

  • SLDataFormat_MIME
  • SLDataFormat_PCM
  • SLDataLocator_BufferQueue
  • SLDataLocator_IODevice
  • SLDataLocator_OutputMix
  • SLDataLocator_URI
  • SLDataSink
  • SLDataSource
  • SLEngineOption
  • SLEnvironmentalReverbSettings
  • SLInterfaceID

Cấu hình nền tảng

OpenSL ES dành cho Android an toàn về luồng và được thiết kế dành cho các ứng dụng đa luồng. API này hỗ trợ một công cụ cho mỗi ứng dụng và tối đa 32 đối tượng cho mỗi công cụ. Bộ nhớ thiết bị và CPU hiện có có thể hạn chế hơn nữa số lượng đối tượng có thể sử dụng.

Các tuỳ chọn công cụ sau được nhận dạng, nhưng sẽ bị slCreateEngine bỏ qua:

  • SL_ENGINEOPTION_THREADSAFE
  • SL_ENGINEOPTION_LOSSOFCONTROL

Bạn có thể dùng OpenMAX AL và OpenSL ES trong cùng một ứng dụng. Trong trường hợp này, có một đối tượng công cụ dùng chung duy nhất ở nội bộ và giới hạn 32 đối tượng là giới hạn chung giữa OpenMAX AL và OpenSL ES. Ứng dụng sẽ tạo cả hai công cụ, sử dụng cả hai công cụ và cuối cùng là huỷ bỏ cả hai công cụ. Quá trình triển khai duy trì số lượng tham chiếu trên công cụ dùng chung để công cụ được huỷ đúng cách trong lần thực hiện thao tác huỷ thứ hai.

Ghi chú về việc lập trình

Ghi chú về việc lập trình OpenSL ES cung cấp thông tin bổ sung để đảm bảo triển khai OpenSL ES đúng cách.

Lưu ý: Để thuận tiện cho bạn, chúng tôi đã gửi kèm một bản sao quy cách OpenSL ES 1.0.1 với NDK trong docs/opensles/OpenSL_ES_Specification_1.0.1.pdf.

Vấn đề về nền tảng

Phần này mô tả các vấn đề đã biết trong bản phát hành nền tảng đầu tiên có hỗ trợ những API này.

Quản lý giao diện động

DynamicInterfaceManagement::AddInterface không hoạt động. Thay vào đó, hãy chỉ định giao diện trong mảng được truyền đến Create(), như minh hoạ trong mã ví dụ về âm vang trong môi trường.

Lập kế hoạch cho các phiên bản trong tương lai của OpenSL ES

Các API âm thanh hiệu suất cao của Android dựa trên Khronos Group OpenSL ES 1.0.1. Khronos đã phát hành phiên bản sửa đổi 1.1 của bản tiêu chuẩn. Phiên bản sửa đổi bao gồm các tính năng mới, nội dung giải thích, sửa lỗi đánh máy và một số điểm không tương thích. Hầu hết các trường hợp không tương thích dự kiến đều tương đối nhỏ hoặc nằm trong khu vực mà Android không hỗ trợ của OpenSL ES.

Một ứng dụng được phát triển bằng phiên bản này sẽ hoạt động trên các phiên bản nền tảng Android trong tương lai, miễn là bạn tuân thủ các nguyên tắc đã nêu trong phần Lên kế hoạch cho tiêu chuẩn tương thích nhị phân bên dưới.

Lưu ý: Bạn không thể chọn mục tiêu tương thích với nguồn trong tương lai. Nói cách khác, nếu nâng cấp lên phiên bản NDK mới hơn, thì bạn có thể phải sửa đổi mã nguồn của ứng dụng để phù hợp với API mới. Chúng tôi dự kiến rằng hầu hết thay đổi như vậy sẽ không ảnh hưởng đang kể; xem chi tiết dưới đây.

Lên kế hoạch cho tiêu chuẩn tương thích nhị phân

Ứng dụng nên tuân theo các nguyên tắc sau để cải thiện khả năng tương thích tệp nhị phân trong tương lai:

  • Chỉ sử dụng tập hợp con các tính năng được Android hỗ trợ đã lập thành tài liệu từ OpenSL ES 1.0.1.
  • Không phụ thuộc vào một mã kết quả cụ thể cho phép toán không thành công; hãy chuẩn bị để xử lý một mã kết quả khác.
  • Trình xử lý gọi lại ứng dụng thường chạy trong ngữ cảnh hạn chế. Các trình xử lý nên được ghi để thực hiện công việc nhanh chóng và trả về càng sớm càng tốt. Không chạy các phép tính phức tạp trong trình xử lý gọi lại. Ví dụ: Trong lệnh gọi lại hoàn thành hàng đợi bộ đệm, bạn có thể thêm một bộ đệm khác vào hàng đợi nhưng không tạo trình phát âm thanh.
  • Bạn nên chuẩn bị trình xử lý gọi lại cho trường hợp được gọi thường xuyên hơn hoặc ít thường xuyên hơn, nhận các loại sự kiện khác và nên bỏ qua các loại sự kiện mà các trình này không nhận ra. Các lệnh gọi lại được định cấu hình bằng một mặt nạ sự kiện tạo bằng các loại sự kiện đã bật nên chuẩn bị nhận lệnh gọi với nhiều bit loại sự kiện được đặt đồng thời. Sử dụng "&" để kiểm thử từng bit sự kiện thay vì một trường hợp chuyển đổi.
  • Sử dụng trạng thái tìm nạp trước và lệnh gọi lại làm chỉ báo chung về tiến trình, nhưng không phụ thuộc vào mức độ lấp đầy mã hoá cứng cụ thể hoặc trình tự gọi lại. Ý nghĩa của mức độ lấp đầy trạng thái tìm nạp trước và hành vi của các lỗi được phát hiện trong quá trình tìm nạp trước có thể thay đổi.

Lưu ý: Hãy xem phần Hành vi của hàng đợi bộ đệm bên dưới để biết thêm chi tiết.

Lên kế hoạch cho tiêu chuẩn tương thích với nguồn

Như đã đề cập, tính không tương thích của mã nguồn dự kiến sẽ có trong phiên bản tiếp theo của OpenSL ES từ Khronos Group. Các vùng thay đổi có thể xảy ra bao gồm:

  • Giao diện hàng đợi bộ đệm dự kiến sẽ có những thay đổi đáng kể, đặc biệt là tại các vùng của BufferQueue::Enqueue, danh sách tham số cho slBufferQueueCallback và tên của trường SLBufferQueueState.playIndex. Thay vào đó, mã xử lý ứng dụng nên sử dụng các hàng đợi bộ đệm đơn giản của Android. Trong ví dụ về mã được cung cấp cùng với NDK, chúng tôi đã sử dụng hàng đợi bộ đệm đơn giản của Android để phát vì lý do này. (Chúng tôi cũng sử dụng hàng đợi bộ đệm đơn giản của Android để ghi và giải mã sang PCM, nhưng đó là vì OpenSL ES 1.0.1 tiêu chuẩn không hỗ trợ ghi hoặc giải mã sang một bồn lưu trữ dữ liệu hàng đợi bộ đệm).
  • Sẽ có thêm const trong tham số đầu vào được truyền qua tham chiếu và tới trường cấu trúc SLchar * được dùng làm giá trị nhập. Việc này không yêu cầu bạn thay đổi mã.
  • Sẽ có tham số thay thế thuộc các loại không dấu đối với một số tham số hiện đã có dấu. Bạn có thể cần thay đổi loại tham số từ SLint32 thành SLuint32 hoặc tương tự hoặc thêm một phiên truyền.
  • Equalizer::GetPresetName sao chép chuỗi vào bộ nhớ ứng dụng thay vì trả một con trỏ (pointer) vào bộ nhớ triển khai. Đây là một thay đổi đáng kể, vì vậy bạn nên tránh gọi phương thức này hoặc cần tách riêng việc sử dụng phương thức này.
  • Sẽ có các trường bổ sung trong các loại cấu trúc. Đối với các tham số đầu ra, những trường mới này có thể bị bỏ qua, nhưng đối với tham số đầu vào, bạn cần phải khởi chạy các trường mới. Rất may là tất cả các trường này dự kiến sẽ nằm trong vùng không được Android hỗ trợ.
  • Giao diện GUID sẽ thay đổi. Tham chiếu đến giao diện theo tên tượng trưng thay vì GUID để tránh phần phụ thuộc.
  • SLchar sẽ thay đổi từ unsigned char thành char. Thay đổi này chủ yếu ảnh hưởng đến trình định vị dữ liệu URI và định dạng dữ liệu MIME.
  • SLDataFormat_MIME.mimeType sẽ được đổi tên thành pMimeTypeSLDataLocator_URI.URI sẽ được đổi tên thành pURI. Bạn nên khởi chạy cấu trúc dữ liệu SLDataFormat_MIMESLDataLocator_URI bằng cách sử dụng danh sách các giá trị đặt trong dấu ngoặc nhọn, được phân tách bằng dấu phẩy, thay vì theo tên trường, để tách biệt mã với sự thay đổi này. Kỹ thuật này được sử dụng trong mã ví dụ.
  • SL_DATAFORMAT_PCM không cho phép ứng dụng chỉ định cách trình bày dữ liệu dưới dạng số nguyên có dấu, số nguyên không dấu hoặc dấu phẩy động. Quá trình triển khai trên Android giả định rằng dữ liệu 8 bit là số nguyên không dấu và dữ liệu 16 bit là số nguyên có dấu. Ngoài ra, trường samplesPerSec là một trường hợp dùng nhầm tên vì các đơn vị thực tế là milliHz. Chúng tôi dự kiến sẽ giải quyết những vấn đề này trong phiên bản OpenSL ES tiếp theo. Phiên bản này sẽ ra mắt định dạng dữ liệu PCM mở rộng mới, cho phép ứng dụng chỉ định rõ ràng cách trình bày và sửa tên trường. Vì đây sẽ là định dạng dữ liệu mới và định dạng dữ liệu PCM hiện tại vẫn sẽ có sẵn (mặc dù không dùng nữa), nên định dạng này không yêu cầu bạn thay đổi mã ngay lập tức.