? ?實際項目中經常遇到兩種場景,第一種從無人機拿H264/H265碼流轉GB28181等協(xié)議,轉協(xié)議的同時可能還需要實時預覽無人機畫面; 第二種是安卓接USB外置攝像頭, 由于USB2.0傳輸帶寬有限,對于高分辨率圖像, 帶寬無法滿足YUV圖像的傳輸, 攝像頭只好先將圖像編碼成MJPEG,H264或H265等格式再傳輸。
? ?對于上述兩種場景,安卓拿到的都是已編碼的H264或H265碼流,這用來轉GB28181、RTSP、RTMP和錄像存儲很方便, 但沒法直接實時預覽, 實時預覽需要先解碼,再顯示. 這樣增加了不少開發(fā)成本,為了方便使用, 在我的播放器上直接增加了傳H264/H265字節(jié)流接口,只要把H264/H265數(shù)據(jù)傳給播放器就好,播放器負責解碼(軟解或硬解)顯示。
? 下面先介紹下H264/H265?Annex B?Byte stream format:
? 字節(jié)流格式:字節(jié)流由1到多個字節(jié)流nal_unit組成.
??字節(jié)流nal_unit:{前綴碼(0x000001) + nal_unit} 或 {zero_byte(0x00) + 前綴碼(0x000001) + nal_unit}.?前綴碼為網絡字節(jié)序.
? 需要注意的是:?1.字節(jié)流中第一個字節(jié)流nal_unit頭部可能包含1個或多個leading_zero_8bits(某些安卓硬編碼器會出這樣的數(shù)據(jù)); 2.字節(jié)流nal_unit單元尾部可能包含一個或多個trailing_zero_8bits.(前后額外加0字節(jié)是為了保持字節(jié)對齊).
? leading_zero_8bits:? 一個0x00字節(jié).
? trailing_zero_8bits: 一個0x00字節(jié).
? 對于VPS, SPS, PPS前綴應是 0x00000001(H264只有SPS, PPS), An?access unit的第一個nal unit的前綴應是0x00000001.
? 字節(jié)流NAL單元語法(H264和H265是一致的):
? ??H264 NAL unit 語法:
? ? 常用的h264 nal_unit_type: 5(IDR), 6(SEI), 7(SPS), 8(PPS).
? ? H265?NAL unit 語法:
?
? ?H265的nal_unit_type請參考265文檔。
? ?更詳細的描述請參考264和265文檔,對于如何調用播放器接口,上面的描述基本夠用了,播放器接口如下:
/*
* Copyright (C) 1130758427@qq.com. All rights reserved.
*/
/**
* 投遞視頻包給播放器
*
* @param codec_id: 編碼id, 當前僅支持H264和H265, 1:H264, 2:H265
*
* @param packet: 視頻數(shù)據(jù), ByteBuffer必須是DirectBuffer, 包格式請參考H264/H265 Annex B Byte stream format, 例如:
* 0x00000001 nal_unit 0x00000001 ...
* H264 IDR: 0x00000001 sps 0x00000001 pps 0x00000001 IDR_nal_unit .... 或 0x00000001 IDR_nal_unit ....
* H265 IDR: 0x00000001 vps 0x00000001 sps 0x00000001 pps 0x00000001 IDR_nal_unit .... 或 0x00000001 IDR_nal_unit ....
*
* @param offset: 偏移量
* @param size: packet size
* @param timestamp_ms: 時間戳, 單位毫秒
* @param is_timestamp_discontinuity: 是否時間戳間斷,0:未間斷,1:間斷
* @param is_key: 是否是關鍵幀, 0:非關鍵幀, 1:關鍵幀
* @param extra_data: 可選參數(shù),可傳null, 對于H264關鍵幀包, 如果packet不含sps和pps, 可傳0x00000001 sps 0x00000001 pps
* ,對于H265關鍵幀包, 如果packet不含vps,sps和pps, 可傳0x00000001 vps 0x00000001 sps 0x00000001 pps
* @param extra_data_size: extra_data size
* @param width: 圖像寬, 可傳0
* @param height: 圖像高, 可傳0
*
* @return {0} if successful
*/
public native int PostVideoPacketByteBuffer(long handle, int codec_id,
java.nio.ByteBuffer packet, int offset, int size, long timestamp_ms, int is_timestamp_discontinuity, int is_key,
byte[] extra_data, int extra_data_size, int width, int height);
/*
* 請參考 PostVideoPacketByteBuffer說明
*/
public native int PostVideoPacketByteArray(long handle, int codec_id,
byte[] packet, int offset, int size, long timestamp_ms, int is_timestamp_discontinuity, int is_key,
byte[] extra_data, int extra_data_size, int width, int height);
調用代碼:文章來源:http://www.zghlxwxcb.cn/news/detail-609100.html
/*
* Copyright (C) 1130758427@qq.com. All rights reserved.
*/
// 啟動播放器
private long start_play(SmartPlayerJniV2 lib_player, Context context, SurfaceView surface_view, boolean is_hardware_decoder) {
if (null ==lib_player || null == context || null == surface_view)
return 0;
long handle = lib_player.SmartPlayerOpen(context);
if (0 == handle) {
Log.e(TAG, "open player failed");
return 0;
}
// 設置0, 盡可能降低預覽延時
lib_player.SmartPlayerSetBuffer(handle, 0);
lib_player.SmartPlayerSetUrl(handle, "ntexternal://*******************");
lib_player.SmartPlayerSetSurface(handle, surface_view);
// 圖像等比例縮放或鋪滿view
lib_player.SmartPlayerSetRenderScaleMode(handle, 1);
lib_player.SmartPlayerSetFastStartup(handle, 1);
// 不要播放音頻,靜音就好
lib_player.SmartPlayerSetMute(handle, 1);
// 大分辨率可能需要硬解,小分辨率推薦軟解,硬解延時可能大些
if (is_hardware_decoder) {
lib_player.SetSmartPlayerVideoHevcHWDecoder(handle, 1);
lib_player.SetSmartPlayerVideoHWDecoder(handle, 1);
}
// 有些場景可能需要解碼出來的圖像用來做分析或重新編碼
// 這里可以設置yuv或rgb callback, 把圖像給Caller
// lib_player.SmartPlayerSetExternalRender(handle, new RGBAExternalRender());
// lib_player.SmartPlayerSetExternalRender(handle, new I420ExternalRender());
if (0 == lib_player.SmartPlayerStartPlay(handle))
return handle;
lib_player.SmartPlayerClose(handle);
return 0;
}
// 停止播放
private void stop_play(SmartPlayerJniV2 lib_player, long handle) {
if (null == lib_player)
return;
if (0 == handle)
return;
lib_player.SmartPlayerStopPlay(handle);
lib_player.SmartPlayerClose(handle);
}
// 投遞H264或H265數(shù)據(jù)給播放器
public void onVideoDataCallback(int ret, int video_codec_id, int size, int is_key_frame, long timestamp, int width, int height, long presentation_timestamp) {
if (player_handle_ !=0)
lib_player_.PostVideoPacketByteBuffer(player_handle_, video_codec_id, video_buffer_, 0, size, timestamp, 0, is_key_frame, null,0, 0, 0);
}
? ?Android也可以用MediaCodec直接解碼顯示,但MediaCodec坑較多,從一個演示版到穩(wěn)定版周期較長成本較高,直接在現(xiàn)有成熟穩(wěn)定的播放器SDK上加接口實現(xiàn)AVC/HEVC實時預覽更可行些.?我在Windows上啟動一個內置rtsp server流, 安卓拉rtsp流,然后將H264/H265傳給播放器顯示,延時非常低(毫秒級),并支持H264和H265碼流實時切換預覽,分辨率實時切換預覽,? 也支持解碼后YUV/RGB數(shù)據(jù)回調,? 實時截圖等功能。另外蘋果的VideoToolbox只支持AVCC格式,不支持Annex B格式,需要轉換后再輸入,更多問題聯(lián)系qq: 1130758427文章來源地址http://www.zghlxwxcb.cn/news/detail-609100.html
到了這里,關于安卓播放H264/H265實時流(安卓實時預覽H264/H265 安卓實時預覽AVC/HEVC)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!