Đường hầm đa phương tiện cho phép dữ liệu video nén được truyền qua bộ giải mã video phần cứng trực tiếp đến màn hình mà không bị mã ứng dụng hoặc mã khung Android xử lý. Mã dành riêng cho thiết bị bên dưới ngăn xếp Android xác định khung video nào sẽ gửi đến màn hình và thời điểm gửi chúng bằng cách so sánh dấu thời gian trình bày khung video với một trong các loại đồng hồ nội bộ sau:
Để phát lại video theo yêu cầu trong Android 5 trở lên, đồng hồ
AudioTrack
được đồng bộ hóa với dấu thời gian trình bày âm thanh mà ứng dụng chuyển vàoĐể phát lại chương trình phát sóng trực tiếp trong 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 bộ dò
Tiểu sử
Tính năng phát lại video truyền thống trên Android sẽ thông báo cho ứng dụng khi khung video nén đã được giải mã. Sau đó, ứng dụng phát hành khung video đã giải mã đến màn hình để hiển thị cùng thời gian đồng hồ hệ thống với khung âm thanh tương ứng, truy xuất các AudioTimestamps
lịch sử để tính toán thời gian chính xác.
Vì phát lại video theo đường hầm bỏ qua mã ứng dụng và giảm số lượng quy trình hoạt động trên video, nên nó có thể cung cấp kết xuất video hiệu quả hơn tùy thuộc vào việc triển khai OEM. Nó cũng có thể cung cấp nhịp video chính xác hơn và đồng bộ hóa 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 gây ra bởi sự chênh lệch tiềm ẩn giữa thời gian yêu cầu Android hiển thị video và thời gian của phần cứng thực sự vsync. Tuy nhiên, việc đào đường hầm cũng có thể làm giảm sự hỗ trợ đối với các hiệu ứng GPU như làm mờ hoặc làm tròn các góc trong cửa sổ hình trong ảnh (PiP), vì các bộ đệm bỏ qua ngăn xếp đồ họa Android.
Sơ đồ sau đây cho thấy cách đào đường hầm đơn giản hóa quá trình phát lại video.
Hình 1. So sánh quy trình phát lại video truyền thống và truyền thống
Dành cho các nhà phát triển ứng dụng
Bởi vì hầu hết các nhà phát triển ứng dụng tích hợp với một thư viện để triển khai 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 cấu hình lại thư viện đó để phát lại theo đường hầm. Để triển khai cấp thấp của trình phát video có đường hầm, hãy sử dụng các hướng dẫn sau.
Để phát lại video theo yêu cầu trong Android 5 trở lên:
Tạo một
SurfaceView
.Tạo một phiên bản
audioSessionId
.Tạo phiên bản
AudioTrack
vàMediaCodec
với phiên bảnaudioSessionId
đã tạo ở bước 2.Xếp hàng đợi dữ liệu âm thanh vào
AudioTrack
với dấu thời gian trình bày cho khung âm thanh đầu tiên trong dữ liệu âm thanh.
Để phát lại chương trình phát sóng trực tiếp trong Android 11 trở lên:
Tạo một
SurfaceView
.Nhận một phiên bản
avSyncHwId
từTuner
.Tạo phiên bản
AudioTrack
vàMediaCodec
với phiên bảnavSyncHwId
đã tạo ở bước 2.
Luồng cuộc gọi API được hiển thị 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 phát lại video theo yêu cầu
Bởi vì phát lại video theo yêu cầu trong đường hầm được liên kết ngầm với phát lại AudioTrack
, hành vi phát lại video theo đường hầm có thể phụ thuộc vào hành vi phát lại âm thanh.
Trên hầu hết các thiết bị, theo mặc định, khung video không được hiển thị cho đến khi bắt đầu phát lại âm thanh. Tuy nhiên, ứng dụng có thể cần hiển thị khung video trước khi bắt đầu phát lại âm thanh, chẳng hạn như để hiển thị cho người dùng vị trí video hiện tại trong khi tìm kiếm.
Để báo hiệu rằng khung video được xếp hàng đợi đầu tiên sẽ được hiển thị ngay sau khi nó được giải mã, hãy đặt thông số
PARAMETER_KEY_TUNNEL_PEEK
thành1
. Khi khung video nén được sắp xếp lại trong hàng đợi (chẳng hạn như khi có khung B ), điều này có nghĩa là khung video được hiển thị đầu tiên phải luôn là khung hình I.Nếu bạn không muốn hiển thị khung video xếp hàng đầu tiên cho đến khi bắt đầu phát lại âm thanh, hãy đặt thông số này thành
0
.Nếu thông số này không được đặt, OEM sẽ xác định hành vi của thiết bị.
Khi dữ liệu âm thanh không được cung cấp cho
AudioTrack
và bộ đệm trống (âm thanh đang chạy), việc phát lại video sẽ dừng lại cho đến khi có nhiều dữ liệu âm thanh hơn do đồng hồ âm thanh không còn tiếp tục.Trong khi phát lại, các lỗi gián đoạn mà ứng dụng không sửa được có thể xuất hiện trong dấu thời gian của bản trình bày âm thanh. Khi điều này xảy ra, OEM sẽ sửa các khoảng trống tiêu cực bằng cách làm ngưng trệ khung 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 (tùy thuộc vào việc triển khai OEM). Vị trí khung
AudioTimestamp
không tăng đối với các khung âm thanh im lặng được chèn.
Đối với các nhà sản xuất thiết bị
Cấu hình
OEM nên tạo một bộ giải mã video riêng biệt để hỗ trợ phát lại video theo đường hầm. Bộ giải mã này sẽ quảng cáo rằng nó có khả năng phát lại trong đường hầm trong tệp media_codecs.xml
:
<Feature name="tunneled-playback" required="true"/>
Khi một phiên bản MediaCodec
đường hầm được định cấu hình với ID phiên âm thanh, nó sẽ truy vấn AudioFlinger
cho ID 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 quá trình truy vấn này, AudioFlinger
truy xuất ID HW_AV_SYNC
từ thiết bị âm thanh chính và liên kết nội bộ nó với ID phiên âm thanh:
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 phiên bản AudioTrack
đã được tạo, ID HW_AV_SYNC
được chuyển đến luồng đầu ra với cùng một ID phiên âm thanh. Nếu nó chưa được tạo, thì ID HW_AV_SYNC
sẽ được chuyển đến luồng đầu ra trong quá trình tạo AudioTrack
. Điều này được thực hiện bởi chuỗi phát lại :
mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC, hwAVSyncId);
ID HW_AV_SYNC
, cho dù nó tương ứng với luồng đầu ra âm thanh hay cấu hình Tuner
, được chuyển vào thành phần OMX hoặc Codec2 để mã OEM có thể kết hợp codec với luồng đầu ra âm thanh hoặc luồng bộ chỉnh tương ứng.
Trong quá trình cấu hình thành phần, thành phần OMX hoặc Codec2 sẽ trả về một tay cầm dải biên có thể được sử dụng để liên kết codec với lớp Trình soạn phần cứng (HWC). Khi ứng dụng liên kết một bề mặt với MediaCodec
, bộ xử lý dải bên này được chuyển xuống HWC thông qua SurfaceFlinger
, nó định cấu hình lớp dưới dạng lớp dải bên .
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 bộ đệm hình ảnh mới từ đầu ra codec vào thời điểm thích hợp, được đồng bộ hóa với luồng đầu ra âm thanh được liên kết hoặc đồng hồ tham chiếu chương trình bộ chỉnh, kết hợp bộ đệm với nội dung hiện tại của các lớp khác và hiển thị hình ảnh kết quả. Điều này xảy ra độc lập với chu trình chuẩn bị và thiết lập thông thường. Lệnh chuẩn bị và thiết lập chỉ xảy 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 biên (chẳng hạn như vị trí hoặc kích thước) thay đổi.
OMX
Thành phần bộ giải mã đường hầm phải hỗ trợ những điều sau:
Đặt thông số mở rộng
OMX.google.android.index.configureVideoTunnelMode
, sử dụng cấu trúcConfigureVideoTunnelModeParams
để chuyển vào IDHW_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 codec hiển thị hoặc không hiển thị khung video được giải mã đầu tiên, bất kể phát lại âm thanh đã bắt đầu hay chưa.Gửi sự kiện
OMX_EventOnFirstTunnelFrameReady
khi khung video đầu tiên được giải mã và sẵn sàng hiển thị.
Triển khai AOSP định cấu hình chế độ đường hầm trong ACodec
thông qua OMXNodeInstance
như được hiển thị 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, nó sẽ phân bổ một bộ xử lý dải biên cho codec này và chuyển nó trở lại thông qua thành viên pSidebandWindow
để HWC có thể xác định codec liên quan. Nếu thành phần không hỗ trợ cấu hình này, nó phải đặt bTunneled
thành OMX_FALSE
.
Codec2
Trong Android 11 trở lên, Codec2
hỗ trợ phát lại theo đường hầm. Thành phần bộ giải mã phải hỗ trợ những điều sau:
Cấu hình
C2PortTunneledModeTuning
, cấu hình chế độ đường hầm và chuyển trongHW_AV_SYNC
được truy xuất từ thiết bị đầu ra âm thanh hoặc cấu hình bộ chỉnh.Truy vấn
C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE
, để cấp phát và truy xuất xử lý dải biên cho HWC.Xử lý
C2_PARAMKEY_TUNNEL_HOLD_RENDER
khi được đính kèm vàoC2Work
, hướng dẫn codec giải mã và báo hiệu hoàn thành công việc, nhưng không hiển thị bộ đệm đầu ra cho đến khi 1) codec sau đó được hướng dẫn để hiển thị nó hoặc 2) phát lại âm thanh bắt đầu.Xử lý
C2_PARAMKEY_TUNNEL_START_RENDER
, hướng dẫn codec kết xuất ngay khung được đánh dấu bằngC2_PARAMKEY_TUNNEL_HOLD_RENDER
, ngay cả khi phát lại âm thanh chưa bắt đầu.Không định cấu hình
debug.stagefright.ccodec_delayed_params
(được khuyến nghị). Nếu bạn cấu hình nó, hãy đặt thànhfalse
.
Triển khai AOSP định cấu hình chế độ đường hầm trong CCodec
thông qua C2PortTunnelModeTuning
, như được hiển thị 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, ¶ms);
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, nó phải phân bổ một bộ xử lý dải biên cho codec này và chuyển nó trở lại qua C2PortTunnelHandlingTuning
để HWC có thể xác định codec liên quan.
HAL âm thanh
Để phát lại video theo yêu cầu, Audio HAL nhận các dấu thời gian trình bày âm thanh cùng dòng với dữ liệu âm thanh ở định dạng big-endian bên trong tiêu đề được tìm thấy ở đầ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 hiển thị khung video đồng bộ với khung âm thanh tương ứng, Audio HAL phải phân tích cú pháp tiêu đề đồng bộ hóa và sử dụng dấu thời gian của bản trình bày để đồng bộ hóa lại đồng hồ phát lại với kết xuất âm thanh. Để đồng bộ hóa lại khi âm thanh nén đang được phát, Audio HAL có thể cần phải 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 lại của nó.
Tạm dừng hỗ trợ
Android 5 trở xuống không bao gồm hỗ trợ tạm dừng. Bạn có thể tạm dừng phát lại trong đường hầm chỉ khi A / V đói, nhưng nếu bộ đệm bên trong dành cho video lớn (ví dụ: có một giây dữ liệu trong thành phần OMX), thì việc tạm dừng có vẻ không phản hồi.
Trong Android 5.1 trở lên, AudioFlinger
hỗ trợ tạm dừng và tiếp tục cho các đầu ra âm thanh trực tiếp (đường hầm). Nếu HAL thực hiện tạm dừng và tiếp tục, theo dõi tạm dừng và tiếp tục được chuyển tiếp đến HAL.
Chuỗi cuộc gọi tạm dừng, xả, tiếp tục được tuân thủ bằng cách thực hiện các lệnh gọi HAL trong chuỗi phát lại (giống như giảm tải).
Đề xuất triển khai
HAL âm thanh
Đối với Android 11, ID đồng bộ hóa HW từ PCR hoặc STC có thể được sử dụng để đồng bộ hóa A / V, do đó, luồng chỉ video được hỗ trợ.
Đối với Android 10 trở xuống, các thiết bị hỗ trợ phát lại video đường hầm phải có ít nhất một cấu hình luồng đầu ra âm thanh với cờ FLAG_HW_AV_SYNC
và AUDIO_OUTPUT_FLAG_DIRECT
trong tệp audio_policy.conf
của nó. Các cờ này được sử dụng để đặt đồng hồ hệ thống từ đồng hồ âm thanh.
OMX
Các nhà sản xuất thiết bị nên có một thành phần OMX riêng biệt để phát lại video trong đường hầm (các nhà sản xuất có thể có các thành phần OMX bổ sung cho các kiểu phát lại âm thanh và video khác, chẳng hạn như phát lại an toàn). Thành phần đường hầm nên:
Chỉ định 0 bộ đệm (
nBufferCountMin
,nBufferCountActual
) trên cổng đầu ra của nó.Triển khai tiện ích mở rộng
OMX.google.android.index.prepareForAdaptivePlayback setParameter
.Chỉ định các khả năng của nó trong tệp
media_codecs.xml
và khai báo tính năng phát lại trong đường hầm. Nó cũng sẽ làm rõ bất kỳ giới hạn nào về kích thước khung hình, căn chỉnh hoặc tốc độ bit. Một ví dụ đã được biểu diễn ở dưới:<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 cùng một thành phần OMX được sử dụng để hỗ trợ giải mã theo đường hầm và không có đường hầm, nó nên để tính năng phát lại đường hầm là không bắt buộc. Cả hai bộ giải mã đường hầm và không đường hầm đều có những hạn chế về khả năng giống nhau. Một ví dụ đã được biểu diễ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>
Phần cứng Composer (HWC)
Khi có một lớp đường hầm (một lớp có HWC_SIDEBAND
compositionType
trên màn hình, sidebandStream
của lớp là tay cầm biên được phân bổ bởi thành phần video OMX.
HWC đồng bộ hóa các khung video đã giải mã (từ thành phần OMX đường hầm) với bản âm thanh được liên kết (với ID audio-hw-sync
). Khi một khung video mới trở thành hiện tại, HWC sẽ 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 lần gọi chuẩn bị hoặc đặt cuối cùng và hiển thị hình ảnh kết quả. Lệnh chuẩn bị hoặc thiết lập chỉ xảy 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 biên (chẳng hạn như vị trí hoặc kích thước) thay đổi.
Hình sau đại diện cho HWC làm việc với bộ đồng bộ hóa phần cứng (hoặc nhân hoặc trình điều khiển), để kết hợp khung video (7b) với thành phần mới nhất (7a) để hiển thị vào đúng thời điểm, dựa trên âm thanh (7c).
Hình 2. Bộ đồng bộ hóa phần cứng (hoặc hạt nhân hoặc trình điều khiển) HWC