Tạo đường hầm đa phương tiện

Tính năng tạo đường hầm đa phương tiện cho phép dữ liệu video nén truyền qua một phần cứng bộ giải mã video trực tiếp lên màn hình mà không cần được xử lý bằng mã ứng dụng hoặc Mã khung Android. Mã dành riêng cho thiết bị bên dưới ngăn xếp Android xác định khung hình video để gửi đến màn hình và thời điểm gửi bằng so sánh dấu thời gian trình chiếu khung hình video với một trong các dấu thời gian sau các loại đồng hồ bên trong:

  • Để phát video theo yêu cầu trong Android 5 trở lên, AudioTrack đồng hồ đã đồng bộ hoá với dấu thời gian trình bày âm thanh đã vượt qua trong ứng dụng

  • Để phát nội dung phát sóng trực tiếp trên Android 11 trở lên, đồng hồ tham chiếu chương trình (PCR) hoặc đồng hồ thời gian hệ thống (STC) được điều khiển bởi điều chỉnh

Thông tin khái quát

Phát lại video truyền thống trên Android thông báo ứng dụng khi khung video nén đã được giải mã. Sau đó, ứng dụng bản phát hành khung video đã giải mã lên màn hình để kết xuất tại cùng một đồng hồ hệ thống làm khung âm thanh tương ứng. truy xuất dữ liệu trong quá khứ AudioTimestamps thực thể để tính toán thời gian chính xác.

Do việc phát video trong đường hầm bỏ qua mã ứng dụng và giảm số lần các quá trình tác động lên video, nó có thể kết xuất video hiệu quả hơn tuỳ thuộc vào cách triển khai của OEM (Nhà sản xuất thiết bị gốc). Tính năng này cũng có thể cung cấp video chính xác hơn nhịp độ và đồng bộ với đồng hồ đã chọn (PRC, STC hoặc âm thanh) bằng cách tránh các vấn đề về thời gian do sai lệch tiềm ẩn giữa thời gian của Android để kết xuất video và thời gian thực thi vsync phần cứng. Tuy nhiên, cũng có thể làm giảm khả năng hỗ trợ hiệu ứng GPU như làm mờ hoặc các góc tròn trong cửa sổ hình trong hình (PiP) vì các vùng đệm bỏ qua ngăn xếp đồ hoạ Android.

Sơ đồ dưới đây cho thấy cách đường hầm đơn giản hoá quá trình phát video.

so sánh chế độ truyền thống và chế độ đường hầm

Hình 1. So sánh quy trình phát video truyền thống và qua đường hầm

Dành cho nhà phát triển ứng dụng

Vì hầu hết các nhà phát triển ứng dụng đều tích hợp với thư viện để phát lại trong hầu hết các trường hợp, việc triển khai chỉ yêu cầu định cấu hình lại để phát trong đường hầm. Để triển khai video được tạo đường hầm ở cấp thấp trình phát, hãy làm theo các hướng dẫn sau.

Đối với tính năng phát video theo yêu cầu trên Android 5 trở lên:

  1. Tạo một thực thể SurfaceView.

  2. Tạo một thực thể audioSessionId.

  3. Tạo thực thể AudioTrackMediaCodec bằng audioSessionId thực thể được tạo ở bước 2.

  4. Thêm dữ liệu âm thanh vào danh sách chờ AudioTrack bằng dấu thời gian trình bày của khung âm thanh đầu tiên trong dữ liệu âm thanh.

Để phát sự kiện phát sóng trực tiếp trên Android 11 trở lên, hãy làm như sau:

  1. Tạo một thực thể SurfaceView.

  2. Nhận một thực thể avSyncHwId từ Tuner.

  3. Tạo thực thể AudioTrackMediaCodec bằng thực thể avSyncHwId tạo ở bước 2.

Luồng lệnh gọi API được thể hiện trong các đoạn mã sau:

aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE);

// configure for audio clock sync
aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC);
// or, for tuner clock sync (Android 11 or higher)
new tunerConfig = TunerConfiguration(0, avSyncId);
aab.setTunerConfiguration(tunerConfig);
if (codecName == null) {
  return FAILURE;
}

// configure for audio clock sync
mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId);
// or, for tuner clock sync (Android 11 or higher)
mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId);

Hành vi khi phát video theo yêu cầu

Vì việc phát video theo yêu cầu được tạo trong đường hầm được liên kết ngầm với AudioTrack thì hành vi phát video qua đường hầm có thể phụ thuộc vào hành vi đó phát lại âm thanh.

  • Theo mặc định, trên hầu hết các thiết bị, khung hình video sẽ không hiển thị cho đến khi có âm thanh sẽ bắt đầu phát. Tuy nhiên, ứng dụng có thể cần kết xuất khung hình video trước bắt đầu phát âm thanh (chẳng hạn như để hiển thị cho người dùng video hiện tại) vị trí trong khi tua.

    • Để báo hiệu rằng khung hình video đầu tiên trong hàng đợi sẽ hiển thị ngay khi mã đó được giải mã, hãy đặt PARAMETER_KEY_TUNNEL_PEEK thành 1. Khi khung hình video nén được sắp xếp lại trong hàng đợi (chẳng hạn như khi Khung B điều này có nghĩa là khung video hiển thị đầu tiên phải luôn là Khung hình chữ I.

    • Nếu bạn không muốn khung hình video đầu tiên trong hàng đợi hiển thị cho đến khi có âm thanh bắt đầu phát, hãy đặt tham số này thành 0.

    • Nếu bạn không đặt thông số này, OEM sẽ xác định hành vi của thiết bị.

  • Khi bạn không cung cấp dữ liệu âm thanh cho AudioTrack và vùng đệm trống (chạy âm thanh), ngăn phát video cho đến khi ghi thêm dữ liệu âm thanh vì đồng hồ âm thanh không còn tua nữa.

  • Trong khi phát, những lần gián đoạn mà ứng dụng không thể khắc phục có thể xuất hiện sau dấu thời gian trình bày âm thanh. Khi điều này xảy ra, OEM sẽ khắc phục các lỗi âm các khoảng trống bằng cách làm gián đoạn khung hình video hiện tại và các khoảng trống tích cực bằng cách giảm khung video hoặc chèn khung âm thanh im lặng (tuỳ thuộc vào OEM (Nhà sản xuất thiết bị gốc) triển khai). Vị trí khung hình AudioTimestamp không tăng đối với đã chèn khung âm thanh im lặng.

Dành cho nhà sản xuất thiết bị

Cấu hình

OEM phải tạo một bộ giải mã video riêng để hỗ trợ phát video theo đường hầm. Bộ giải mã này nên quảng cáo rằng nó có khả năng phát theo đường hầm trong Tệp media_codecs.xml:

<Feature name="tunneled-playback" required="true"/>

Khi một thực thể MediaCodec được tạo trong đường hầm được định cấu hình bằng mã phiên âm thanh, thực thể đó truy vấn AudioFlinger cho mã HW_AV_SYNC này:

if (entry.getKey().equals(MediaFormat.KEY_AUDIO_SESSION_ID)) {
    int sessionId = 0;
    try {
        sessionId = (Integer)entry.getValue();
    }
    catch (Exception e) {
        throw new IllegalArgumentException("Wrong Session ID Parameter!");
    }
    keys[i] = "audio-hw-sync";
    values[i] = AudioSystem.getAudioHwSyncForSession(sessionId);
}

Trong truy vấn này, AudioFlinger truy xuất mã nhận dạng HW_AV_SYNC từ thiết bị âm thanh chính và liên kết nội bộ thiết bị này với âm thanh ID phiên:

audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
char *reply = dev->get_parameters(dev, AUDIO_PARAMETER_HW_AV_SYNC);
AudioParameter param = AudioParameter(String8(reply));
int hwAVSyncId;
param.getInt(String8(AUDIO_PARAMETER_HW_AV_SYNC), hwAVSyncId);

Nếu một thực thể AudioTrack đã được tạo, thì mã nhận dạng HW_AV_SYNC sẽ là được truyền đến luồng đầu ra có cùng mã phiên âm thanh. Nếu chưa được chưa được tạo, thì mã nhận dạng HW_AV_SYNC sẽ được truyền đến luồng đầu ra trong khoảng thời gian Tạo AudioTrack. Điều này được thực hiện bằng cách phát lại chuỗi:

mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC, hwAVSyncId);

Mã nhận dạng HW_AV_SYNC, cho dù tương ứng với luồng đầu ra âm thanh hay Tuner, được chuyển vào thành phần OMX hoặc Codec2 để Mã OEM có thể liên kết bộ mã hoá và giải mã với luồng đầu ra âm thanh tương ứng hoặc luồng bộ dò.

Trong quá trình định cấu hình thành phần, thành phần OMX hoặc Codec2 phải trả về một trình xử lý băng tần phụ có thể dùng để liên kết bộ mã hoá và giải mã với một Trình soạn thảo phần cứng (HWC). Khi ứng dụng liên kết một nền tảng với MediaCodec, băng tần phụ này tên người dùng được truyền xuống HWC thông qua SurfaceFlinger, giúp định cấu hình xếp lớp dưới dạng Lớp side Band.

err = native_window_set_sideband_stream(nativeWindow.get(), sidebandHandle);
if (err != OK) {
  ALOGE("native_window_set_sideband_stream(%p) failed! (err %d).", sidebandHandle, err);
  return err;
}

HWC chịu trách nhiệm nhận vùng đệm hình ảnh mới từ đầu ra của bộ mã hoá và giải mã tại thời gian thích hợp, được đồng bộ hoá với luồng đầu ra âm thanh liên quan hoặc đồng hồ tham chiếu chương trình bộ chỉnh, kết hợp vùng đệm với nội dung của các lớp khác và hiển thị hình ảnh thu được. Điều này xảy ra độc lập với chu kỳ chuẩn bị và thiết lập thông thường. Cuộc gọi chuẩn bị và thiết lập chỉ xảy ra khi các lớp khác thay đổi hoặc khi thuộc tính của lớp băng tần phụ (chẳng hạn như vị trí hoặc kích thước).

OMX

Thành phần bộ giải mã được tạo đường hầm phải hỗ trợ những nội dung sau:

  • Đã mở rộng việc thiết lập OMX.google.android.index.configureVideoTunnelMode tham số này, sử dụng cấu trúc ConfigureVideoTunnelModeParams để truyền trong mã nhận dạng HW_AV_SYNC liên kết với thiết bị đầu ra âm thanh.

  • Định cấu hình tham số OMX_IndexConfigAndroidTunnelPeek cho biết bộ mã hoá và giải mã kết xuất hoặc không kết xuất khung video được giải mã đầu tiên, bất kể đã bắt đầu phát âm thanh hay chưa.

  • Gửi sự kiện OMX_EventOnFirstTunnelFrameReady khi đường hầm đầu tiên khung video đã được giải mã và sẵn sàng để được hiển thị.

Phương thức triển khai AOSP định cấu hình chế độ đường hầm trong ACodec qua OMXNodeInstance như minh hoạ trong đoạn mã sau:

OMX_INDEXTYPE index;
OMX_STRING name = const_cast<OMX_STRING>(
        "OMX.google.android.index.configureVideoTunnelMode");

OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);

ConfigureVideoTunnelModeParams tunnelParams;
InitOMXParams(&tunnelParams);
tunnelParams.nPortIndex = portIndex;
tunnelParams.bTunneled = tunneled;
tunnelParams.nAudioHwSync = audioHwSync;
err = OMX_SetParameter(mHandle, index, &tunnelParams);
err = OMX_GetParameter(mHandle, index, &tunnelParams);
sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow;

Nếu thành phần hỗ trợ cấu hình này, thành phần nên phân bổ băng tần phụ xử lý đến bộ mã hoá và giải mã này rồi truyền lại qua thành phần pSidebandWindow để mà HWC có thể xác định bộ mã hoá và giải mã được liên kết. Nếu thành phần này không hỗ trợ cấu hình này, thì tệp sẽ đặt bTunneled thành OMX_FALSE.

Bộ mã hoá và giải mã 2

Trên Android 11 trở lên, Codec2 hỗ trợ tính năng phát theo đường hầm. Bộ giải mã thành phần phải hỗ trợ những nội dung sau:

  • Định cấu hình C2PortTunneledModeTuning để định cấu hình chế độ đường hầm và truyền trong HW_AV_SYNC được truy xuất từ thiết bị đầu ra âm thanh hoặc cấu hình bộ dò.

  • Truy vấn C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE để phân bổ và truy xuất thanh điều khiển bên cạnh cho HWC.

  • Xử lý C2_PARAMKEY_TUNNEL_HOLD_RENDER khi được đính kèm với C2Work. hướng dẫn bộ mã hoá và giải mã giải mã và báo hiệu về việc hoàn thành công việc nhưng không hiển thị vùng đệm đầu ra cho đến khi 1) bộ mã hoá và giải mã được hướng dẫn kết xuất hoặc 2) bắt đầu phát âm thanh.

  • Xử lý C2_PARAMKEY_TUNNEL_START_RENDER để hướng dẫn bộ mã hoá và giải mã ngay lập tức hiển thị khung được đánh dấu bằng C2_PARAMKEY_TUNNEL_HOLD_RENDER, ngay cả khi quá trình phát âm thanh chưa bắt đầu.

  • Hãy để debug.stagefright.ccodec_delayed_params chưa định cấu hình (nên dùng). Nếu bạn định cấu hình nó, đặt thành false.

Phương thức triển khai AOSP định cấu hình chế độ đường hầm trong CCodec thông qua C2PortTunnelModeTuning, như minh hoạ trong đoạn mã sau:

if (msg->findInt32("audio-hw-sync", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::AUDIO_HW_SYNC;
} else if (msg->findInt32("hw-av-sync-id", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::HW_AV_SYNC;
} else {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::REALTIME;
    tunneledPlayback->setFlexCount(0);
}
c2_status_t c2err = comp->config({ tunneledPlayback.get() }, C2_MAY_BLOCK,
        failures);
std::vector<std::unique_ptr<C2Param>> params;
c2err = comp->query({}, {C2PortTunnelHandleTuning::output::PARAM_TYPE},
        C2_DONT_BLOCK, &params);
if (c2err == C2_OK && params.size() == 1u) {
    C2PortTunnelHandleTuning::output *videoTunnelSideband =
            C2PortTunnelHandleTuning::output::From(params[0].get());
    return OK;
}

Nếu thành phần hỗ trợ cấu hình này, thành phần nên phân bổ băng tần phụ xử lý đến bộ mã hoá và giải mã này rồi truyền lại qua C2PortTunnelHandlingTuning để mà HWC có thể xác định bộ mã hoá và giải mã được liên kết.

Lớp trừu tượng phần cứng (HAL) cho âm thanh

Để phát video theo yêu cầu, HAL âm thanh nhận bản trình bày âm thanh dấu thời gian cùng dòng với dữ liệu âm thanh ở định dạng Big-endian bên trong một tiêu đề ở đầu mỗi khối dữ liệu âm thanh mà ứng dụng ghi:

struct TunnelModeSyncHeader {
  // The 32-bit data to identify the sync header (0x55550002)
  int32 syncWord;
  // The size of the audio data following the sync header before the next sync
  // header might be found.
  int32 sizeInBytes;
  // The presentation timestamp of the first audio sample following the sync
  // header.
  int64 presentationTimestamp;
  // The number of bytes to skip after the beginning of the sync header to find the
  // first audio sample (20 bytes for compressed audio, or larger for PCM, aligned
  // to the channel count and sample size).
  int32 offset;
}

Để HWC kết xuất khung hình video đồng bộ với các khung âm thanh tương ứng, HAL âm thanh phải phân tích cú pháp tiêu đề đồng bộ hoá và sử dụng dấu thời gian trình bày để đồng bộ hoá lại đồng hồ phát với kết xuất âm thanh. Để đồng bộ hoá lại khi đang phát âm thanh nén, HAL âm thanh có thể cần phân tích cú pháp siêu dữ liệu bên trong dữ liệu âm thanh nén để xác định thời lượng phát.

Tạm dừng hỗ trợ

Android 5 trở xuống không có tính năng hỗ trợ tạm dừng. Bạn có thể tạm dừng đường hầm chỉ phát bằng tình trạng thiếu A/V, nhưng nếu bộ đệm nội bộ cho video lớn (ví dụ: có 1 giây dữ liệu trong thành phần OMX), thành phần này sẽ tạm dừng trông không thích ứng.

Trên Android 5.1 trở lên, AudioFlinger hỗ trợ tính năng tạm dừng và tiếp tục để quay video trực tiếp đầu ra âm thanh (phát âm thanh). Nếu HAL (Lớp trừu tượng phần cứng) triển khai thao tác tạm dừng và tiếp tục, hãy theo dõi lượt tạm dừng và sơ yếu lý lịch sẽ được chuyển tiếp đến HAL.

Trình tự cuộc gọi tạm dừng, xả nước, tiếp tục được tuân thủ bằng cách thực thi lệnh gọi HAL trong luồng phát (tương tự như giảm tải).

Đề xuất triển khai

Lớp trừu tượng phần cứng (HAL) cho âm thanh

Đối với Android 11, bạn có thể dùng mã nhận dạng đồng bộ hoá HW từ PCR hoặc STC để đồng bộ hoá A/V, vì vậy chỉ hỗ trợ luồng video.

Đối với Android 10 trở xuống, các thiết bị hỗ trợ tính năng phát video theo đường hầm phải: ít nhất một cấu hình luồng đầu ra âm thanh có FLAG_HW_AV_SYNCAUDIO_OUTPUT_FLAG_DIRECT gắn cờ trong tệp audio_policy.conf của nó. Những cờ này được dùng để đặt xung nhịp hệ thống từ đồng hồ âm thanh.

OMX

Nhà sản xuất thiết bị nên có một thành phần OMX riêng cho video được tạo đường hầm phát video (nhà sản xuất có thể sử dụng thêm các thành phần OMX cho các loại phát âm thanh và video, chẳng hạn như phát lại an toàn). Thành phần được tạo đường hầm nên:

  • Chỉ định 0 vùng đệm (nBufferCountMin, nBufferCountActual) trên dữ liệu đầu ra cổng.

  • Triển khai phần mở rộng OMX.google.android.index.prepareForAdaptivePlayback setParameter.

  • Chỉ định chức năng của lớp đó trong tệp media_codecs.xml rồi khai báo tính năng phát theo đường hầm. Nội dung này cũng nêu rõ mọi giới hạn về khung hình kích thước, căn chỉnh hoặc tốc độ bit. Sau đây là ví dụ minh hoạ:

    <MediaCodec name="OMX.OEM_NAME.VIDEO.DECODER.AVC.tunneled"
    type="video/avc" >
        <Feature name="adaptive-playback" />
        <Feature name="tunneled-playback" required=”true” />
        <Limit name="size" min="32x32" max="3840x2160" />
        <Limit name="alignment" value="2x2" />
        <Limit name="bitrate" range="1-20000000" />
            ...
    </MediaCodec>
    

Nếu thành phần OMX đó được dùng để hỗ trợ giải mã theo đường hầm và không theo đường hầm, thì tính năng phát theo đường hầm sẽ là không bắt buộc. Cả đường hầm và thì bộ giải mã không truyền dữ liệu có cùng những giới hạn về khả năng. Ví dụ: được hiển thị bên dưới:

<MediaCodec name="OMX._OEM\_NAME_.VIDEO.DECODER.AVC" type="video/avc" >
    <Feature name="adaptive-playback" />
    <Feature name="tunneled-playback" />
    <Limit name="size" min="32x32" max="3840x2160" />
    <Limit name="alignment" value="2x2" />
    <Limit name="bitrate" range="1-20000000" />
        ...
</MediaCodec>

Trình soạn phần cứng (HWC)

Khi có một lớp được tạo đường hầm (một lớp có HWC_SIDEBAND compositionType) đang bật một màn hình, sidebandStream của lớp là xử lý băng tần cạnh được phân bổ bởi thành phần video OMX.

HWC đồng bộ hoá các khung video đã giải mã (từ thành phần OMX được tạo đường hầm) đến bản âm thanh được liên kết (có mã audio-hw-sync). Khi một khung hình video mới trở nên hiện hành, HWC kết hợp nó với nội dung hiện tại của tất cả các lớp nhận được trong cuộc gọi chuẩn bị hoặc đặt lịch gần nhất và hiển thị hình ảnh thu được. Lệnh gọi chuẩn bị hoặc lệnh gọi chỉ diễn ra khi các lớp khác thay đổi hoặc khi các thuộc tính của lớp dải bên (chẳng hạn như vị trí hoặc kích thước) thay đổi.

Hình sau đây biểu thị HWC hoạt động với phần cứng (hoặc kernel hay trình điều khiển) để kết hợp các khung hình video (7b) với cấu trúc mới nhất (7a) để hiển thị đúng thời điểm, dựa trên âm thanh (7c).

HWC kết hợp các khung hình video dựa trên âm thanh

Hình 2. Bộ đồng bộ hoá phần cứng (hoặc nhân hệ điều hành) hoặc trình điều khiển của HWC