Tiện ích Android

OpenSL ES dành cho Android mở rộng quy cách tham chiếu OpenSL ES để tương thích với Android, đồng thời tận dụng sức mạnh và sự linh hoạt của nền tảng Android.

Phần khai báo của API dành cho tiện ích Android nằm trong OpenSLES_Android.h và các tệp tiêu đề kèm theo. Hãy tham khảo OpenSLES_Android.h để biết thông tin chi tiết về các tiện ích này. Tệp này nằm dưới thư mục cài đặt gốc, trong thư mục sysroot/usr/include/SLES. Trừ phi có lưu ý khác, tất cả giao diện đều ở dạng rõ ràng (explicit).

Các tiện ích này giới hạn khả năng chuyển đổi của ứng dụng đối với các phương thức triển khai OpenSL ES khác, vì các tiện ích này dành riêng cho Android. Bạn có thể giảm nhẹ vấn đề này bằng cách tránh sử dụng tiện ích hoặc sử dụng #ifdef để loại trừ tiện ích tại thời điểm biên dịch.

Bảng sau đây cho thấy các giao diện dành riêng cho Android và trình định vị dữ liệu mà Android OpenSL ES hỗ trợ cho từng loại đối tượng. Các giá trị trong các ô thể hiện việc có sẵn giao diện và trình định vị dữ liệu cho từng loại đối tượng.

Tính năng Trình phát âm thanh Trình ghi âm Công cụ Trộn dữ liệu đầu ra
Hàng đợi bộ đệm Android Có: Nguồn (giải mã) Không Không Không
Cấu hình Android Không Không
Hiệu ứng Android Không Không
Năng lực hiệu ứng Android Không Không Không
Gửi hiệu ứng Android Không Không Không
Hàng đợi bộ đệm đơn giản Android Có: Nguồn (phát) hoặc bồn lưu trữ dữ liệu (giải mã) Không Không
Trình định vị dữ liệu hàng đợi bộ đệm Android Có: Nguồn (giải mã) Không Không Không
Trình định vị dữ liệu mô tả tệp Android Có: Nguồn Không Không Không
Trình định vị dữ liệu hàng đợi bộ đệm đơn giản Android Có: Nguồn (phát) hoặc bồn lưu trữ dữ liệu (giải mã) Có: Bồn lưu trữ dữ liệu Không Không

Giao diện cấu hình Android

Giao diện cấu hình Android cung cấp một phương thức để thiết lập các thông số theo nền tảng cụ thể cho các đối tượng. Giao diện này khác với các giao diện OpenSL ES 1.0.1 khác ở chỗ ứng dụng của bạn có thể sử dụng trước khi tạo bản sao cho đối tượng tương ứng. Do đó, bạn có thể định cấu hình đối tượng trước khi tạo bản sao đối tượng. Tệp tiêu đề OpenSLES_AndroidConfiguration.h (nằm tại /sysroot/usr/include/SLES) ghi lại các khoá và giá trị cấu hình hiện có sau đây:

  • Kiểu luồng dành cho trình phát âm thanh (mặc định là SL_ANDROID_STREAM_MEDIA).
  • Hồ sơ ghi âm dành cho trình ghi âm (mặc định là SL_ANDROID_RECORDING_PRESET_GENERIC).

Sau đây là đoạn mã ví dụ về cách đặt kiểu luồng âm thanh Android trên trình phát âm thanh:

// CreateAudioPlayer and specify SL_IID_ANDROIDCONFIGURATION
// in the required interface ID array. Do not realize player yet.
// ...
SLAndroidConfigurationItf playerConfig;
result = (*playerObject)->GetInterface(playerObject,
    SL_IID_ANDROIDCONFIGURATION, &playerConfig);
assert(SL_RESULT_SUCCESS == result);
SLint32 streamType = SL_ANDROID_STREAM_ALARM;
result = (*playerConfig)->SetConfiguration(playerConfig,
    SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
assert(SL_RESULT_SUCCESS == result);
// ...
// Now realize the player here.

Bạn có thể sử dụng mã tương tự để định cấu hình giá trị đặt trước cho trình ghi âm:

// ... obtain the configuration interface as the first four lines above, then:
SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
result = (*playerConfig)->SetConfiguration(playerConfig,
    RECORDING_PRESET, &presetValue, sizeof(SLuint32));

Giao diện hiệu ứng Android

Các giao diện hiệu ứng, gửi hiệu ứng và năng lực hiệu ứng của Android mang đến một cơ chế chung để ứng dụng có thể truy vấn và sử dụng hiệu ứng âm thanh tuỳ theo thiết bị. Nhà sản xuất thiết bị nên ghi chép mọi hiệu ứng âm thanh dành riêng cho thiết bị mà họ cung cấp.

Ứng dụng xách tay (portable) nên sử dụng các API OpenSL ES 1.0.1 cho hiệu ứng âm thanh thay vì dùng tiện ích hiệu ứng của Android.

Trình định vị dữ liệu mô tả tệp Android

Trình định vị dữ liệu mô tả tệp Android cho phép bạn chỉ định nguồn cho trình phát âm thanh dưới dạng chỉ số mở mô tả tệp có quyền đọc. Định dạng dữ liệu phải là MIME.

Tiện ích này đặc biệt hữu ích khi kết hợp với trình quản lý thành phần gốc vì ứng dụng đọc các thành phần trong tệp APK thông qua chỉ số mô tả tệp.

Giao diện và trình định vị dữ liệu hàng đợi bộ đệm đơn giản Android

Trong quy cách tham chiếu OpenSL ES 1.0.1, chỉ dùng được hàng đợi bộ đệm cho trình phát âm thanh. Các hàng đợi này tương thích với PCM và các định dạng dữ liệu khác. Quy cách của giao diện và trình định vị dữ liệu hàng đợi bộ đệm đơn giản Android giống hệt với quy cách tham chiếu, trừ hai ngoại lệ:

  • Bạn có thể sử dụng hàng đợi bộ đệm đơn giản Android trên trình ghi âm và trình phát âm thanh.
  • Bạn chỉ có thể sử dụng định dạng dữ liệu PCM với các hàng đợi này.

Để ghi âm, ứng dụng của bạn nên thêm các bộ đệm trống vào hàng đợi. Khi một lệnh gọi lại đã đăng ký gửi thông báo cho biết hệ thống đã ghi xong dữ liệu vào một bộ đệm, thì ứng dụng có thể đọc qua bộ đệm đó.

Tính năng phát cũng hoạt động theo cách tương tự. Tuy nhiên, để đảm bảo tính tương thích với mã nguồn sau này, bạn nên sử dụng hàng đợi bộ đệm đơn giản Android thay vì hàng đợi bộ đệm OpenSL ES 1.0.1.

Hành vi của hàng đợi bộ đệm

Trong phương thức triển khai Android không có yêu cầu của quy cách tham chiếu về việc con trỏ phát sẽ quay lại phần đầu của bộ đệm đang phát khi trạng thái phát là SL_PLAYSTATE_STOPPED. Phương thức triển khai này có thể phù hợp với hành vi đó hoặc có thể không thay đổi vị trí của con trỏ phát. Do đó, ứng dụng của bạn không thể giả định rằng một trong hai hành vi sẽ xảy ra. Vì vậy, bạn nên gọi phương thức BufferQueue::Clear() một cách rõ ràng sau khi chuyển sang SL_PLAYSTATE_STOPPED. Thao tác này sẽ thiết lập hàng đợi bộ đệm về một trạng thái đã biết.

Tương tự như vậy, không có quy cách nào quy định rằng trình kích hoạt lệnh gọi lại hàng đợi bộ đệm phải chuyển đổi sang SL_PLAYSTATE_STOPPED hoặc thực thi BufferQueue::Clear(). Do đó, bạn không nên tạo phần phụ thuộc đối với một trong hai trường hợp trên; thay vào đó, ứng dụng của bạn có thể xử lý được cả hai trường hợp.

Giao diện động khi tạo đối tượng

Để thuận tiện, phương thức triển khai Android của OpenSL ES 1.0.1 cho phép ứng dụng của bạn chỉ định giao diện động khi tạo bản sao của một đối tượng. Đây là giải pháp thay thế cho việc sử dụng DynamicInterfaceManagement::AddInterface() để thêm các giao diện này sau khi tạo bản sao.

Báo cáo tiện ích

Có 3 phương thức để truy vấn xem nền tảng có hỗ trợ các tiện ích Android hay không. Bao gồm các phương thức sau:

  • Engine::QueryNumSupportedExtensions()
  • Engine::QuerySupportedExtension()
  • Engine::IsExtensionSupported()

Bất kỳ phương thức nào trong số này đều trả về ANDROID_SDK_LEVEL_<API-level>, trong đó API-level là cấp API của nền tảng; ví dụ như ANDROID_SDK_LEVEL_23. Cấp API của nền tảng từ 9 trở lên có nghĩa là nền tảng có hỗ trợ tiện ích.

Giải mã âm thanh sang PCM

Phần này mô tả một tiện ích dành riêng cho Android (hiện không còn sử dụng) cho OpenSL ES 1.0.1 để giải mã luồng được mã hoá tới PCM mà không cần phát ngay. Bảng dưới đây đưa ra các đề xuất về việc sử dụng tiện ích này cũng như các tiện ích thay thế.

Cấp API Lựa chọn thay thế
15 trở xuống Một bộ mã hoá và giải mã nguồn mở có giấy phép phù hợp
16 đến 20 Lớp MediaCodec hoặc một bộ mã hoá và giải mã nguồn mở có giấy phép phù hợp
21 trở lên NDK MediaCodec trong các tệp tiêu đề <media/NdkMedia*.h>, lớp MediaCodec hoặc một bộ mã hoá và giải mã nguồn mở có giấy phép phù hợp

Lưu ý: Hiện không có tài liệu nào về phiên bản NDK của API MediaCodec. Tuy nhiên, bạn có thể tham khảo mã nguồn mẫu native-codec để xem ví dụ.

Một trình phát âm thanh tiêu chuẩn phát trở về lại một thiết bị âm thanh, chỉ định dữ liệu trộn đầu ra là bồn lưu trữ dữ liệu. Tiện ích Android khác ở chỗ trình phát âm thanh hoạt động như một bộ giải mã nếu ứng dụng đã chỉ định nguồn dữ liệu dưới dạng URI hoặc bộ định vị dữ liệu mô tả tệp Android được mô tả bằng định dạng dữ liệu MIME. Trong trường hợp như vậy, bồn lưu trữ dữ liệu là một trình định vị dữ liệu hàng đợi bộ đệm đơn giản Android và sử dụng định dạng dữ liệu PCM.

Tính năng này chủ yếu được dùng trong các trò chơi để tải trước thành phần âm thanh khi chuyển sang một màn chơi mới, tương tự như chức năng mà lớp SoundPool cung cấp.

Trước tiên, ứng dụng sẽ thêm một tập hợp bộ đệm trống vào hàng đợi bộ đệm đơn giản Android. Sau đó, ứng dụng sẽ điền dữ liệu PCM vào bộ đệm. Lệnh gọi lại hàng đợi bộ đệm đơn giản Android sẽ kích hoạt sau khi mỗi bộ đệm được lấp đầy. Trình xử lý gọi lại sẽ xử lý dữ liệu PCM, sắp xếp lại bộ đệm hiện đang trống rồi trở lại. Ứng dụng có trách nhiệm theo dõi các bộ đệm được giải mã. Danh sách thông số gọi lại không bao gồm đủ thông tin để biểu thị rằng bộ đệm có chứa dữ liệu hoặc bộ đệm cần được thêm vào hàng đợi tiếp theo.

Nguồn dữ liệu báo cáo một cách ngầm ẩn về việc kết thúc sự kiện phát trực tuyến (EOS) bằng cách gửi một sự kiện SL_PLAYEVENT_HEADATEND ở cuối sự kiện phát trực tuyến. Sau khi giải mã xong tất cả dữ liệu đã nhận được, ứng dụng sẽ không thực hiện lệnh gọi nào khác đến lệnh gọi lại của hàng đợi bộ đệm đơn giản Android.

Định dạng dữ liệu PCM của bồn lưu trữ dữ liệu thường khớp với định dạng của nguồn dữ liệu được mã hoá, về tốc độ lấy mẫu, số lượng kênh và độ sâu bit (bit depth). Tuy nhiên, bạn có thể giải mã sang một tốc độ lấy mẫu, số lượng kênh hoặc độ sâu bit khác. Để biết thông tin về một biện pháp phát hiện định dạng PCM thực tế, hãy xem nội dung Xác định định dạng của dữ liệu PCM được giải mã thông qua siêu dữ liệu.

Tính năng giải mã PCM của OpenSL ES cho Android có hỗ trợ tạm dừng và tua lại ban đầu; nhưng không hỗ trợ kiểm soát âm lượng, hiệu ứng, phát lặp lại hoặc tốc độ phát.

Tuỳ thuộc vào phương thức triển khai nền tảng, quá trình giải mã có thể cần đến những tài nguyên không thể để ở trạng thái rảnh (idle). Do đó, bạn nên cung cấp đủ số lượng bộ đệm PCM trống; nếu không, bộ giải mã sẽ thiếu bộ đệm (starve). Lỗi này có thể xảy ra, chẳng hạn như khi ứng dụng của bạn trở lại từ lệnh gọi lại hàng đợi bộ đệm đơn giản Android mà không đưa một bộ đệm trống khác vào hàng đợi. Kết quả của tình trạng thiếu hụt của bộ giải mã (decoder starvation) không được chỉ định, nhưng có thể bao gồm: bỏ dữ liệu PCM đã giải mã, tạm dừng quá trình giải mã hoặc chấm dứt bộ giải mã ngay lập tức.

Lưu ý: Để giải mã luồng đã mã hoá thành PCM nhưng không phát ngay lập tức, đối với ứng dụng chạy trên Android 4.x (API cấp 16-20), bạn nên sử dụng MediaCodec. Đối với ứng dụng mới chạy trên Android 5.0 (API cấp 21) trở lên, bạn nên dùng NDK tương đương, <NdkMedia*.h>. Các tệp tiêu đề này nằm trong thư mục media/ trong thư mục cài đặt gốc.

Giải mã phương thức truyền trực tuyến ADTS AAC sang PCM

Trình phát âm thanh đóng vai trò như một bộ giải mã phát trực tuyến nếu nguồn dữ liệu là một trình định vị dữ liệu hàng đợi bộ đệm Android sử dụng định dạng dữ liệu MIME còn bồn lưu trữ dữ liệu là một trình định vị dữ liệu hàng đợi bộ đệm đơn giản Android sử dụng định dạng dữ liệu PCM. Hãy định cấu hình định dạng dữ liệu MIME như sau:

  • Vùng chứa: SL_CONTAINERTYPE_RAW
  • Chuỗi dạng MIME: SL_ANDROID_MIME_AACADTS

Tính năng này chủ yếu dành cho ứng dụng truyền trực tuyến nội dung đa phương tiện có xử lý âm thanh AAC nhưng cần thực hiện bước xử lý âm thanh tuỳ chỉnh trước khi phát. Hầu hết ứng dụng cần giải mã âm thanh sang PCM phải sử dụng phương thức Giải mã âm thanh sang PCM, vì phương thức này đơn giản hơn và xử lý được nhiều định dạng âm thanh hơn. Kỹ thuật được mô tả ở đây là một phương pháp chuyên biệt hơn và chỉ sử dụng nếu đáp ứng cả hai điều kiện sau:

  • Nguồn âm thanh nén là một luồng gồm các khung AAC chứa trong các tiêu đề ADTS.
  • Ứng dụng quản lý luồng này. Dữ liệu không nằm trong tài nguyên mạng có giá trị nhận dạng là một URI hoặc trong một tệp cục bộ có giá trị nhận dạng là chỉ số mô tả tệp.

Ban đầu, ứng dụng sẽ thêm một nhóm bộ đệm được lấp đầy vào hàng đợi bộ đệm Android. Mỗi bộ đệm chứa một hoặc nhiều khung ADTS AAC hoàn chỉnh. Lệnh gọi lại hàng đợi bộ đệm Android sẽ kích hoạt sau khi mỗi bộ đệm được làm trống. Trình xử lý gọi lại sẽ nạp lại và xếp lại hàng bộ đệm, sau đó trở lại. Ứng dụng không cần theo dõi các bộ đệm đã mã hoá; danh sách thông số gọi lại có đủ thông tin để cho biết vùng đệm sẽ được thêm vào hàng đợi tiếp theo. Điểm kết thúc truyền trực tuyến được đánh dấu rõ ràng bằng cách thêm một mục EOS vào hàng đợi. Sau EOS, không cho phép thêm hàng đợi.

Bạn nên đảm bảo cung cấp đầy đủ bộ đệm ADTS AAC để tránh tình trạng thiếu hụt cho bộ giải mã. Tình trạng này có thể xảy ra, chẳng hạn như khi ứng dụng của bạn trở lại từ lệnh gọi lại hàng đợi bộ đệm Android mà không xếp một bộ đệm đầy đủ khác vào hàng đợi. Kết quả của tình trạng thiếu hụt không được chỉ định.

Trong mọi phương diện, ngoại trừ nguồn dữ liệu, phương thức giải mã truyền trực tuyến cũng giống như phương thức được mô tả trong nội dung Giải mã âm thanh sang PCM.

Mặc dù có sự tương đồng về tên, hàng đợi bộ đệm Android không giống với hàng đợi bộ đệm đơn giản Android. Bộ giải mã truyền trực tuyến sử dụng cả hai loại hàng đợi bộ đệm: hàng đợi bộ đệm Android cho nguồn dữ liệu ADTS AAC và hàng đợi bộ đệm đơn giản Android cho bồn chứa dữ liệu PCM. Để biết thêm thông tin về API hàng đợi bộ đệm đơn giản Android, hãy xem nội dung giao diện và trình định vị dữ liệu hàng đợi bộ đệm đơn giản Android. Để biết thêm thông tin về API hàng đợi bộ đệm Android, hãy xem tệp index.html trong thư mục docs/Additional_library_docs/openmaxal/ bên dưới thư mục cài đặt gốc.

Xác định định dạng của dữ liệu PCM đã giải mã thông qua siêu dữ liệu

Giao diện SLMetadataExtractionItf là một phần của quy cách tham chiếu. Tuy nhiên, chỉ Android mới có các khoá siêu dữ liệu cho biết định dạng thực tế của dữ liệu PCM đã giải mã. Tệp tiêu đề OpenSLES_AndroidMetadata.h xác định các khoá siêu dữ liệu này. Tệp tiêu đề này nằm dưới thư mục cài đặt gốc của bạn, trong thư mục /sysroot/usr/include/SLES.

Các chỉ mục khoá siêu dữ liệu sẽ có ngay sau khi phương thức Object::Realize() thực thi xong. Tuy nhiên, các giá trị được liên kết sẽ chưa xuất hiện cho đến khi ứng dụng giải mã xong dữ liệu mã hoá đầu tiên. Bạn nên truy vấn các chỉ mục chính trong luồng chính sau khi gọi phương thức Object::Realize, cũng như đọc các giá trị siêu dữ liệu về định dạng PCM trong trình xử lý gọi lại hàng đợi bộ đệm đơn giản Android trong lần đầu gọi phương thức đó. Hãy tham khảo nội dung mã nguồn ví dụ trong gói NDK để nắm được các ví dụ về cách xử lý giao diện này.

Tên khoá siêu dữ liệu có tính ổn định, tuy nhiên các chỉ mục chính không được ghi chép lại và có thể thay đổi. Ứng dụng không nên giả định rằng các chỉ mục vẫn tồn tại qua nhiều lần thực thi và cũng không nên giả định rằng nhiều thực thể đối tượng có chung chỉ mục trong cùng một lần chạy.

Dữ liệu dấu phẩy động

Ứng dụng chạy trên Android 5.0 (API cấp 21) trở lên có thể cung cấp dữ liệu cho AudioPlayer ở định dạng dấu phẩy động đơn chính xác.

Trong mã ví dụ sau đây, phương thức Engine::CreateAudioPlayer() tạo một trình phát âm thanh sử dụng dữ liệu dấu phẩy động:

#include <SLES/OpenSLES_Android.h>
...
SLAndroidDataFormat_PCM_EX pcm;
pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
pcm.numChannels = 2;
pcm.sampleRate = SL_SAMPLINGRATE_44_1;
pcm.bitsPerSample = 32;
pcm.containerSize = 32;
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
...
SLDataSource audiosrc;
audiosrc.pLocator = ...
audiosrc.pFormat = &pcm;
Đọc thêm về âm thanh dấu phẩy động trên trang Lấy mẫu âm thanh.