Gỡ lỗi âm thanh

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

  1. cd frameworks/av/services/audioflinger
  2. Chỉnh sửa Configuration.h.
  3. Huỷ nhận xét #define TEE_SINK.
  4. Tạo lại libaudioflinger.so.
  5. adb root
  6. adb remount
  7. Đẩ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

  1. adb shell getprop | grep ro.debuggable
    Xác nhận kết quả là: [ro.debuggable]: [1]
  2. adb shell
  3. 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
    
  4. echo af.tee=# > /data/local.prop
    Trong đó, giá trị af.tee là một số được mô tả dưới đây.
  5. chmod 644 /data/local.prop
  6. 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

  1. Chạy kiểm tra âm thanh.
  2. adb shell dumpsys media.audio_flinger
  3. 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.
  4. 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 ra dumpsys, nhưng vẫn được lưu vào /data/misc/audioserver khi kênh đóng.
  5. 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:

  1. Huỷ bỏ các thay đổi đối với mã nguồn về Configuration.h.
  2. Tạo lại libaudioflinger.so.
  3. Đẩy hoặc đồng bộ hoá libaudioflinger.so đã khôi phục vào /system/lib của thiết bị.
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. 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ư FastMixerFastCapture.
  • 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 NBLOGmedia.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:

Cấu trúc trước media.log

Hình 1. Cấu trúc trước media.log

Điểm đáng chú ý:

  • init phát triển nhánh và bộ phận thực thi mediaserver.
  • init phát hiện trường hợp mediaserver 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:

Cấu trúc sau media.log

Hình 2. Cấu trúc sau media.log

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 FastMixerFastCapture.

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 FastMixerFastCapture, 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 FastMixerFastCapture 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 FastMixerFastCapture, 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_harness1. 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
Lệnh ps media giờ đây sẽ hiển thị hai quy 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