From 7b4f04aa279ef61fea51d702966827e76508095f Mon Sep 17 00:00:00 2001 From: chodison Date: Thu, 29 Oct 2015 23:43:48 +0800 Subject: [PATCH] android/mediacodec: add function to support H265/HEVC. Signed-off-by: Zhang Rui --- .../ffpipenode_android_mediacodec_vdec.c | 50 +++++++-- .../ijkplayer/android/pipeline/hevc_nal.h | 106 ++++++++++++++++++ 2 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 ijkmedia/ijkplayer/android/pipeline/hevc_nal.h diff --git a/ijkmedia/ijkplayer/android/pipeline/ffpipenode_android_mediacodec_vdec.c b/ijkmedia/ijkplayer/android/pipeline/ffpipenode_android_mediacodec_vdec.c index 0f5c7891..ff93d76a 100644 --- a/ijkmedia/ijkplayer/android/pipeline/ffpipenode_android_mediacodec_vdec.c +++ b/ijkmedia/ijkplayer/android/pipeline/ffpipenode_android_mediacodec_vdec.c @@ -32,6 +32,7 @@ #include "ijksdl/android/ijksdl_vout_android_nativewindow.h" #include "ijksdl/android/ijksdl_vout_overlay_android_mediacodec.h" #include "h264_nal.h" +#include "hevc_nal.h" #define AMC_USE_AVBITSTREAM_FILTER 0 #ifndef AMCTRACE @@ -324,6 +325,8 @@ static int feed_input_buffer(JNIEnv *env, IJKFF_Pipenode *node, int64_t timeUs, goto fail; } + opaque->avctx = opaque->decoder->avctx; + if (!d->packet_pending || d->queue->serial != d->pkt_serial) { #if AMC_USE_AVBITSTREAM_FILTER #else @@ -399,6 +402,7 @@ static int feed_input_buffer(JNIEnv *env, IJKFF_Pipenode *node, int64_t timeUs, d->pkt_temp.data[6], d->pkt_temp.data[7]); #endif + if (opaque->avctx->codec_id == AV_CODEC_ID_H264 || opaque->avctx->codec_id == AV_CODEC_ID_HEVC) { convert_h264_to_annexb(d->pkt_temp.data, d->pkt_temp.size, opaque->nal_size, &convert_state); int64_t time_stamp = d->pkt_temp.pts; if (!time_stamp && d->pkt_temp.dts) @@ -408,6 +412,7 @@ static int feed_input_buffer(JNIEnv *env, IJKFF_Pipenode *node, int64_t timeUs, } else { time_stamp = 0; } + } #if 0 AMCTRACE("input[%d][%d][%lld,%lld (%d, %d) -> %lld] %02x%02x%02x%02x%02x%02x%02x%02x", (int)d->pkt_temp.size, (int)opaque->nal_size, @@ -996,8 +1001,13 @@ IJKFF_Pipenode *ffpipenode_create_video_decoder_from_android_mediacodec(FFPlayer opaque->mcc.profile = opaque->avctx->profile; opaque->mcc.level = opaque->avctx->level; break; + case AV_CODEC_ID_HEVC: + strcpy(opaque->mcc.mime_type, SDL_AMIME_VIDEO_HEVC); + opaque->mcc.profile = opaque->avctx->profile; + opaque->mcc.level = opaque->avctx->level; + break; default: - ALOGE("%s:create: not H264\n", __func__); + ALOGE("%s:create: not H264 or H265/HEVC, codec_id:%d \n", __func__, opaque->avctx->codec_id); goto fail; } @@ -1022,12 +1032,21 @@ IJKFF_Pipenode *ffpipenode_create_video_decoder_from_android_mediacodec(FFPlayer ALOGI("AMediaFormat: %s, %dx%d\n", opaque->mcc.mime_type, opaque->avctx->width, opaque->avctx->height); opaque->input_aformat = SDL_AMediaFormatJava_createVideoFormat(env, opaque->mcc.mime_type, opaque->avctx->width, opaque->avctx->height); if (opaque->avctx->extradata && opaque->avctx->extradata_size > 0) { - if (opaque->avctx->codec_id == AV_CODEC_ID_H264 && opaque->avctx->extradata[0] == 1) { + if ((opaque->avctx->codec_id == AV_CODEC_ID_H264 || opaque->avctx->codec_id == AV_CODEC_ID_HEVC) + && opaque->avctx->extradata[0] == 1) { #if AMC_USE_AVBITSTREAM_FILTER - opaque->bsfc = av_bitstream_filter_init("h264_mp4toannexb"); - if (!opaque->bsfc) { - ALOGE("Cannot open the h264_mp4toannexb BSF!\n"); - goto fail; + if (opaque->avctx->codec_id == AV_CODEC_ID_H264) { + opaque->bsfc = av_bitstream_filter_init("h264_mp4toannexb"); + if (!opaque->bsfc) { + ALOGE("Cannot open the h264_mp4toannexb BSF!\n"); + goto fail; + } + } else { + opaque->bsfc = av_bitstream_filter_init("hevc_mp4toannexb"); + if (!opaque->bsfc) { + ALOGE("Cannot open the hevc_mp4toannexb BSF!\n"); + goto fail; + } } opaque->orig_extradata_size = opaque->avctx->extradata_size; @@ -1049,11 +1068,20 @@ IJKFF_Pipenode *ffpipenode_create_video_decoder_from_android_mediacodec(FFPlayer ALOGE("%s:sps_pps_buffer: alloc failed\n", __func__); goto fail; } - if (0 != convert_sps_pps(opaque->avctx->extradata, opaque->avctx->extradata_size, - convert_buffer, convert_size, - &sps_pps_size, &opaque->nal_size)) { - ALOGE("%s:convert_sps_pps: failed\n", __func__); - goto fail; + if (opaque->avctx->codec_id == AV_CODEC_ID_H264) { + if (0 != convert_sps_pps(opaque->avctx->extradata, opaque->avctx->extradata_size, + convert_buffer, convert_size, + &sps_pps_size, &opaque->nal_size)) { + ALOGE("%s:convert_sps_pps: failed\n", __func__); + goto fail; + } + } else { + if (0 != convert_hevc_nal_units(opaque->avctx->extradata, opaque->avctx->extradata_size, + convert_buffer, convert_size, + &sps_pps_size, &opaque->nal_size)) { + ALOGE("%s:convert_hevc_nal_units: failed\n", __func__); + goto fail; + } } SDL_AMediaFormat_setBuffer(opaque->input_aformat, "csd-0", convert_buffer, sps_pps_size); for(int i = 0; i < sps_pps_size; i+=4) { diff --git a/ijkmedia/ijkplayer/android/pipeline/hevc_nal.h b/ijkmedia/ijkplayer/android/pipeline/hevc_nal.h new file mode 100644 index 00000000..6bffca81 --- /dev/null +++ b/ijkmedia/ijkplayer/android/pipeline/hevc_nal.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015 Chodison Chen + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "libavcodec/avcodec.h" +#include "ijksdl/ijksdl_log.h" + +/* Inspired by libavcodec/hevc.c */ +int convert_hevc_nal_units(const uint8_t *p_buf,uint32_t i_buf_size, + uint8_t *p_out_buf,uint32_t i_out_buf_size, + uint32_t *p_sps_pps_size,uint32_t *p_nal_size) +{ + int i, num_arrays; + const uint8_t *p_end = p_buf + i_buf_size; + uint32_t i_sps_pps_size = 0; + + if( i_buf_size <= 3 || ( !p_buf[0] && !p_buf[1] && p_buf[2] <= 1 ) ) + return -1; + + if( p_end - p_buf < 23 ) + { + ALOGE( "Input Metadata too small" ); + return -1; + } + + p_buf += 21; + + if( p_nal_size ) + *p_nal_size = (*p_buf & 0x03) + 1; + p_buf++; + + num_arrays = *p_buf++; + + for( i = 0; i < num_arrays; i++ ) + { + int type, cnt, j; + + if( p_end - p_buf < 3 ) + { + ALOGE( "Input Metadata too small" ); + return -1; + } + type = *(p_buf++) & 0x3f; + (void)(type); + + cnt = p_buf[0] << 8 | p_buf[1]; + p_buf += 2; + + for( j = 0; j < cnt; j++ ) + { + int i_nal_size; + + if( p_end - p_buf < 2 ) + { + ALOGE( "Input Metadata too small" ); + return -1; + } + + i_nal_size = p_buf[0] << 8 | p_buf[1]; + p_buf += 2; + + if( i_nal_size < 0 || p_end - p_buf < i_nal_size ) + { + ALOGE( "NAL unit size does not match Input Metadata size" ); + return -1; + } + + if( i_sps_pps_size + 4 + i_nal_size > i_out_buf_size ) + { + ALOGE( "Output buffer too small" ); + return -1; + } + + p_out_buf[i_sps_pps_size++] = 0; + p_out_buf[i_sps_pps_size++] = 0; + p_out_buf[i_sps_pps_size++] = 0; + p_out_buf[i_sps_pps_size++] = 1; + + memcpy(p_out_buf + i_sps_pps_size, p_buf, i_nal_size); + p_buf += i_nal_size; + + i_sps_pps_size += i_nal_size; + } + } + + *p_sps_pps_size = i_sps_pps_size; + + return 0; +} -- GitLab