1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 安卓播放H264/H265实时流(安卓实时预览H264/H265 安卓实时预览AVC/HEVC)

安卓播放H264/H265实时流(安卓实时预览H264/H265 安卓实时预览AVC/HEVC)

时间:2018-09-24 00:42:16

相关推荐

安卓播放H264/H265实时流(安卓实时预览H264/H265 安卓实时预览AVC/HEVC)

实际项目中经常遇到两种场景,第一种从无人机拿H264/H265码流转GB28181等协议,转协议的同时可能还需要实时预览无人机画面; 第二种是安卓接USB外置摄像头, 由于USB2.0传输带宽有限,对于高分辨率图像, 带宽无法满足YUV图像的传输, 摄像头只好先将图像编码成MJPEG,H264或H265等格式再传输。

对于上述两种场景,安卓拿到的都是已编码的H264或H265码流,这用来转GB28181、RTSP、RTMP和录像存储很方便, 但没法直接实时预览, 实时预览需要先解码,再显示. 这样增加了不少开发成本,为了方便使用, 在我的播放器上直接增加了传H264/H265字节流接口,只要把H264/H265数据传给播放器就好,播放器负责解码(软解或硬解)显示。

下面先介绍下H264/H265Annex BByte stream format:

字节流格式:字节流由1到多个字节流nal_unit组成.

字节流nal_unit:{前缀码(0x000001) + nal_unit} 或 {zero_byte(0x00) + 前缀码(0x000001) + nal_unit}.前缀码为网络字节序.

需要注意的是:1.字节流中第一个字节流nal_unit头部可能包含1个或多个leading_zero_8bits(某些安卓硬编码器会出这样的数据); 2.字节流nal_unit单元尾部可能包含一个或多个trailing_zero_8bits.(前后额外加0字节是为了保持字节对齐).

leading_zero_8bits: 一个0x00字节.

trailing_zero_8bits:一个0x00字节.

对于VPS, SPS, PPS前缀应是 0x00000001(H264只有SPS, PPS), Anaccess unit的第一个nal unit的前缀应是0x00000001.

字节流NAL单元语法(H264和H265是一致的):

H264 NAL unit 语法:

常用的h264 nal_unit_type: 5(IDR), 6(SEI), 7(SPS), 8(PPS).

H265NAL unit 语法:

H265的nal_unit_type请参考265文档。

更详细的描述请参考264和265文档,对于如何调用播放器接口,上面的描述基本够用了,播放器接口如下:

/** Copyright (C) 1130758427@. All rights reserved.*//*** 投递视频包给播放器** @param codec_id: 编码id, 当前仅支持H264和H265, 1:H264, 2:H265** @param packet: 视频数据, 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: 可选参数,可传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);

调用代码:

/** Copyright (C) 1130758427@. 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);// 图像等比例缩放或铺满viewlib_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数据给播放器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坑较多,从一个演示版到稳定版周期较长成本较高,直接在现有成熟稳定的播放器SDK上加接口实现AVC/HEVC实时预览更可行些.我在Windows上启动一个内置rtsp server流, 安卓拉rtsp流,然后将H264/H265传给播放器显示,延时非常低(毫秒级),并支持H264和H265码流实时切换预览,分辨率实时切换预览, 也支持解码后YUV/RGB数据回调, 实时截图等功能。另外苹果的VideoToolbox只支持AVCC格式,不支持Annex B格式,需要转换后再输入,更多问题联系qq: 1130758427

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。