Bài viết này mô tả một số mẹo và thủ thuật để gỡ lỗi âm thanh trên Android.
Bồn rửa áo phông
"Bếp phát bóng" là tính năng gỡ lỗi AudioFlinger, chỉ có trong các bản dựng tuỳ chỉnh, để giữ lại một đoạn âm thanh ngắn gần đây cho mục đích phân tích sau này. Điều này cho phép so sánh giữa nội dung thực tế được phát hoặc ghi lại so với kỳ vọng.
Để đảm bảo quyền riêng tư, bồn lưu trữ điểm phát sóng được tắt theo mặc định, tại cả thời gian biên dịch và thời gian chạy. Để sử dụng bồn rửa phát bóng, bạn sẽ cần bật bồn rửa bằng cách biên dịch lại, cũng như bằng cách đặt thuộc tính. Hãy nhớ tắt tính năng này sau khi bạn đã gỡ lỗi xong; không nên bật bồn rửa phát bóng trong các bản dựng chính thức.
Hướng dẫn trong phần này dành cho Android 7.x trở lên. Cho Android
5.x và 6.x, thay thế /data/misc/audioserver
bằng
/data/misc/media
Ngoài ra, bạn phải sử dụng tính năng userdebug hoặc
Kỹ thuật Nếu bạn sử dụng bản dựng userdebug, hãy tắt tính năng xác thực bằng:
adb root && adb disable-verity && adb reboot
Thiết lập thời gian biên dịch
cd frameworks/av/services/audioflinger
- Chỉnh sửa
Configuration.h
. - Huỷ nhận xét
#define TEE_SINK
. - Tạo lại
libaudioflinger.so
. adb root
adb remount
- Đẩy hoặc đồng bộ hoá
libaudioflinger.so
mới với/system/lib
của thiết bị.
Thiết lập thời gian chạy
adb shell getprop | grep ro.debuggable
Xác nhận kết quả là:[ro.debuggable]: [1]
adb shell
ls -ld /data/misc/audioserver
Xác nhận kết quả là:
drwx------ media media ... media
Nếu thư mục không tồn tại, hãy tạo thư mục như sau:
mkdir /data/misc/audioserver
chown media:media /data/misc/audioserver
echo af.tee=# > /data/local.prop
Trong đó, giá trịaf.tee
là một số được mô tả dưới đây.chmod 644 /data/local.prop
reboot
Giá trị của thuộc tính af.tee
Giá trị của af.tee
là một số từ 0 đến 7, biểu thị
tổng của một vài bit, một bit cho mỗi đối tượng.
Xem mã tại AudioFlinger::AudioFlinger()
ở AudioFlinger.cpp
để được giải thích riêng từng chút một nhưng ngắn gọn:
- 1 = đầu vào
- 2 = Đầu ra FastMixer
- 4 = Bản ghi âm và Bản âm thanh của mỗi bản nhạc
Chưa có chút dung lượng nào cho bộ đệm sâu hoặc trình trộn thông thường, nhưng bạn có thể nhận được kết quả tương tự khi sử dụng "4".
Kiểm thử và thu thập dữ liệu
- Chạy kiểm tra âm thanh.
adb shell dumpsys media.audio_flinger
- Tìm một dòng trong dữ liệu đầu ra của
dumpsys
, chẳng hạn như:
tee copied to /data/misc/audioserver/20131010101147_2.wav
Đây là tệp .wav PCM. - Sau đó,
adb pull
bất kỳ tệp/data/misc/audioserver/*.wav
nào bạn quan tâm; Xin lưu ý rằng tên tệp kết xuất theo dõi cụ thể sẽ không xuất hiện trong Đầu radumpsys
, nhưng vẫn được lưu vào/data/misc/audioserver
khi kênh đóng. - Xem lại các tệp kết xuất để biết vấn đề về quyền riêng tư trước khi chia sẻ với người khác.
Nội dung đề xuất
Hãy thử các ý tưởng sau để có kết quả hữu ích hơn:
- Tắt âm thanh chạm và phím nhấp để giảm tình trạng gián đoạn trong đầu ra kiểm thử.
- Tối đa hoá tất cả số lượng.
- Vô hiệu hoá các ứng dụng phát ra âm thanh hoặc ghi âm bằng micrô, nếu họ không quan tâm đến thử nghiệm của bạn.
- Tệp kết xuất dành riêng cho kênh chỉ được lưu khi kênh đó đóng; bạn có thể phải buộc đóng ứng dụng để kết xuất dữ liệu theo dõi cụ thể của ứng dụng đó
- Thực hiện
dumpsys
ngay sau khi kiểm thử; thì số lượng không gian ghi bị hạn chế. - Để đảm bảo bạn không mất tệp kết xuất, định kỳ tải chúng lên máy chủ lưu trữ của bạn. Chỉ một số tệp kết xuất hạn chế được giữ nguyên; tệp kết xuất cũ hơn sẽ bị xoá sau khi đạt đến giới hạn đó.
Khôi phục
Như đã lưu ý ở trên, bạn không nên bật tính năng bồn phát bóng. Khôi phục bản dựng và thiết bị như sau:
- Huỷ bỏ các thay đổi đối với mã nguồn về
Configuration.h
. - Tạo lại
libaudioflinger.so
. - Đẩy hoặc đồng bộ hoá
libaudioflinger.so
đã khôi phục vào/system/lib
của thiết bị. adb shell
rm /data/local.prop
rm /data/misc/audioserver/*.wav
reboot
media.log
Macro ALOGx
API ghi nhật ký ngôn ngữ Java chuẩn trong SDK Android là android.util.Log.
API ngôn ngữ C tương ứng trong Android NDK là
__android_log_print
đã khai báo trong <android/log.h>
.
Trong phần gốc của khung Android, chúng tôi
ưu tiên các macro có tên ALOGE
, ALOGW
,
ALOGI
, ALOGV
, v.v. Chúng được khai báo trong
<utils/Log.h>
, và theo mục đích của bài viết này
chúng tôi gọi chung là ALOGx
.
Tất cả các API này đều dễ sử dụng và dễ hiểu nên chúng phổ biến
trên toàn bộ nền tảng Android. Cụ thể, mediaserver
bao gồm cả máy chủ âm thanh AudioFlinger, sử dụng
ALOGx
một cách sâu sắc.
Tuy nhiên, có một số giới hạn đối với ALOGx
và bạn bè:
-
Chúng dễ bị "ghi nhật ký spam": vùng đệm nhật ký là một tài nguyên dùng chung
để quảng cáo có thể dễ dàng bị tràn do các mục nhập nhật ký không liên quan, dẫn đến
thông tin bị thiếu. Biến thể
ALOGV
đã bị vô hiệu hoá tại thời gian biên dịch theo mặc định. Nhưng tất nhiên, ngay cả việc này cũng có thể dẫn đến spam nhật ký nếu được bật. -
Các lệnh gọi hệ thống nhân cơ bản có thể chặn, dẫn đến việc
đảo ngược mức độ ưu tiên cùng với rối loạn đo lường và
những điểm không chính xác. Trong số
đặc biệt chú ý đến các luồng quan trọng về thời gian như
FastMixer
vàFastCapture
. - Nếu một nhật ký cụ thể bị tắt để giảm thư rác nhật ký, thì mọi thông tin mà nhật ký đó đã ghi lại sẽ bị mất. Không thể bật nhật ký cụ thể cho thời gian trở về trước, sau khi rõ ràng rằng nhật ký sẽ thú vị.
NBLOG, media.log và MediaLogService
Các API NBLOG
và media.log
được liên kết
quy trình và MediaLogService
tạo thành một hệ thống ghi nhật ký mới cho nội dung đa phương tiện và đặc biệt
nhằm giải quyết các vấn đề nêu trên. Chúng tôi sẽ thoải mái sử dụng thuật ngữ
"media.log" đề cập đến cả ba, nhưng nói đúng ra thì NBLOG
là
API ghi nhật ký C++, media.log
là tên quy trình của Linux và MediaLogService
là một dịch vụ liên kết của Android để kiểm tra nhật ký.
"Tiến trình" media.log
là một bộ sách
của các mục nhập nhật ký có thứ tự tương đối được giữ nguyên.
Theo quy ước, mỗi luồng phải sử dụng dòng thời gian riêng.
Lợi ích
Hệ thống media.log
mang lại những lợi ích sau đây:
- Không gửi nội dung rác cho nhật ký chính trừ phi và cho đến khi cần đến.
- Bạn có thể kiểm tra ngay cả khi
mediaserver
gặp sự cố hoặc bị treo. - Không chặn trên mỗi dòng thời gian.
- Giúp hiệu suất hoạt động ít bị ảnh hưởng hơn. (Tất nhiên, không có hình thức ghi nhật ký nào là hoàn toàn không xâm phạm.)
Kiến trúc
Sơ đồ dưới đây cho thấy mối quan hệ của quy trình mediaserver
và quy trình init
, trước khi media.log
ra mắt:
Điểm đáng chú ý:
init
phát triển nhánh và bộ phận thực thimediaserver
.init
phát hiện trường hợpmediaserver
chết và phát triển lại nếu cần.- Chế độ ghi nhật ký
ALOGx
không xuất hiện.
Sơ đồ dưới đây thể hiện mối quan hệ mới giữa các thành phần,
sau khi thêm media.log
vào cấu trúc:
Thay đổi quan trọng:
-
Ứng dụng sử dụng API
NBLOG
để tạo các mục nhập nhật ký và thêm các mục đó vào một vùng đệm tròn trong bộ nhớ dùng chung. -
MediaLogService
có thể kết xuất nội dung của vùng đệm tròn bất cứ lúc nào. -
Vùng đệm tròn được thiết kế sao cho mọi hỏng hóc của
bộ nhớ dùng chung sẽ không gặp sự cố
MediaLogService
và vẫn có thể để kết xuất nhiều vùng đệm mà không bị ảnh hưởng bởi hỏng hóc. - Vùng đệm tròn không chặn lẫn nhau và không khoá khi ghi cả các bài viết mới và đọc các bài viết hiện có.
- Không cần lệnh gọi hệ thống nhân hệ điều hành nào để ghi hoặc đọc từ vùng đệm tròn (ngoài dấu thời gian không bắt buộc).
Nền tảng sử dụng
Kể từ Android 4.4, chỉ có một vài điểm nhật ký trong AudioFlinger
sử dụng hệ thống media.log
. Mặc dù các API mới không phải là
dễ sử dụng như ALOGx
, cũng không quá khó.
Bạn nên tìm hiểu hệ thống ghi nhật ký mới dành cho những
khi là không thể thiếu.
Đặc biệt, bạn nên sử dụng cho các luồng AudioFlinger phải
chạy thường xuyên, định kỳ và không bị chặn, chẳng hạn như
Luồng FastMixer
và FastCapture
.
How to use
Thêm nhật ký
Trước tiên, bạn cần thêm nhật ký vào mã của mình.
Trong các luồng FastMixer
và FastCapture
, hãy dùng mã như sau:
logWriter->log("string"); logWriter->logf("format", parameters); logWriter->logTimestamp();
Vì dòng thời gian NBLog
này chỉ được FastMixer
và
FastCapture
chuỗi,
không cần loại trừ lẫn nhau.
Trong các luồng AudioFlinger khác, hãy sử dụng mNBLogWriter
:
mNBLogWriter->log("string"); mNBLogWriter->logf("format", parameters); mNBLogWriter->logTimestamp();
Đối với các luồng không phải FastMixer
và FastCapture
,
cả luồng này đều có thể sử dụng dòng thời gian NBLog
của luồng, và
bằng các phép toán liên kết. NBLog::Writer
không cung cấp bất kỳ
loại trừ lẫn nhau ngầm ẩn cho mỗi dòng thời gian, nên hãy đảm bảo tất cả nhật ký đều xuất hiện
trong ngữ cảnh nơi mLock
mutex của luồng được giữ.
Sau khi bạn thêm nhật ký, hãy tạo lại AudioFlinger.
Thận trọng:
Cần có một tiến trình NBLog::Writer
riêng cho mỗi luồng,
để đảm bảo an toàn cho luồng, vì dòng thời gian bỏ qua các mutex theo thiết kế. Nếu bạn
muốn có nhiều chuỗi sử dụng cùng một dòng thời gian, bạn có thể bảo vệ bằng
mutex hiện có (như mô tả ở trên cho mLock
). Hoặc bạn có thể
hãy sử dụng trình bao bọc NBLog::LockedWriter
thay vì NBLog::Writer
.
Tuy nhiên, điều này phủ nhận một lợi ích chính của API này: tính năng không chặn
hành vi.
API NBLog
đầy đủ có tại frameworks/av/include/media/nbaio/NBLog.h
.
Bật media.log
media.log
bị tắt theo mặc định. Thuộc tính này chỉ hoạt động khi tài sản
ro.test_harness
là 1
. Bạn có thể bật tính năng này bằng cách:
adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot
Kết nối bị mất trong quá trình khởi động lại, do đó:
adb shell
ps media
giờ đây sẽ hiển thị hai quá trình:
- media.log
- máy chủ phương tiện
Ghi lại mã định danh tiến trình của mediaserver
để dùng sau.
Hiển thị dòng thời gian
Bạn có thể yêu cầu kết xuất nhật ký theo cách thủ công bất kỳ lúc nào. Lệnh này hiển thị nhật ký của tất cả các dòng thời gian đang hoạt động và gần đây, sau đó xoá các nhật ký đó:
dumpsys media.log
Xin lưu ý rằng theo tiến trình thiết kế là độc lập, cũng như không có cơ sở để hợp nhất dòng thời gian.
Khôi phục nhật ký sau khi máy chủ đa phương tiện bị gián đoạn
Bây giờ, hãy thử tắt quy trình mediaserver
: kill -9 #
, trong đó # là
mã quy trình mà bạn ghi trước đó. Bạn sẽ thấy một tệp kết xuất từ media.log
trong logcat
chính, cho thấy tất cả nhật ký dẫn đến sự cố.
dumpsys media.log