修复ijkplayer没有正确处理sps导致的MediaCodec解码失败问题

播放HLS时,部分TS分片的sps排列是:
0 0 0 1 9 240 0 0 0 1 103 。。。 0 0 0 1 104。。。
其对应的内容是:

  1. 0 0 0 1 9 240 :表示AUD
  2. 接着的 0 0 0 1 :表示 start code
  3. 接着的103 :表示sps类型
  4. 接着的就是sps的数据
  5. 最后的0 0 0 1 104 :104表示是pps数据
  6. 接着的就是pps的数据

这里针对这种情况做了处理,提取出其中的sps数据给到MediaCodec,避免MediaCodec初始化失败。

需要这种视频链接的,我可以发出来,不过视频链接只有24小时有效。

issues如下:

  1. android k0.8.4 某些手机播放视频有声音,无画面(即透明,非黑屏) #4177
  2. 请教下Android播放视频只有声音没有画面? #3544

  
ijkmedia/ijkplayer/android/pipeline/ffpipenode_android_mediacodec_vdec.c
@@ -245,24 +245,23 @@ static int recreate_format_l(JNIEnv *env, IJKFF_Pipenode *node)
            SDL_AMediaFormat_setBuffer(opaque->input_aformat, "csd-0", convert_buffer, esds_size);
            free(convert_buffer);
        } else {
            //先只考虑h264的情况,H265这种情况我也没见过
            if(opaque->avctx->codec_id == AV_CODEC_ID_H264  && opaque->avctx->extradata_size > 6){
                //参考 https://blog.csdn.net/chinabinlang/article/details/78181110
                //参考 https://blog.csdn.net/dxpqxb/article/details/7631644
                //这种视频是:AUD+SPS+PPS的情况
           if(opaque->codecpar->codec_id == AV_CODEC_ID_H264  && opaque->codecpar->extradata_size > 6){
                // https://blog.csdn.net/chinabinlang/article/details/78181110
                // https://blog.csdn.net/dxpqxb/article/details/7631644
                // AUD+SPS+PPS的情况
                // 103 代表sps
                // 104 代表pps
                int spsIndex = -1;
                int ppsIndex = -1;
                if(opaque->avctx->extradata[0] == 0 && opaque->avctx->extradata[1] == 0 && opaque->avctx->extradata[2] == 0
                   && opaque->avctx->extradata[3] == 1 && opaque->avctx->extradata[4] == 9 && opaque->avctx->extradata[5] == 240){
                if(opaque->codecpar->extradata[0] == 0 && opaque->codecpar->extradata[1] == 0 && opaque->codecpar->extradata[2] == 0
                   && opaque->codecpar->extradata[3] == 1 && opaque->codecpar->extradata[4] == 9 && opaque->codecpar->extradata[5] == 240){
                    for(int i=6;iavctx->extradata_size-4;i++){
                        if(opaque->avctx->extradata[i] == 0 && opaque->avctx->extradata[i+1] == 0 && opaque->avctx->extradata[i+2] == 0
                           && opaque->avctx->extradata[i+3] == 1){
                            if(opaque->avctx->extradata[i+4] == 103){
                        if(opaque->codecpar->extradata[i] == 0 && opaque->codecpar->extradata[i+1] == 0 && opaque->codecpar->extradata[i+2] == 0
                           && opaque->codecpar->extradata[i+3] == 1){
                            if(opaque->codecpar->extradata[i+4] == 103){
                                spsIndex = i;
                                ALOGE("AMediaFormat find sps = %d",spsIndex);
                            }else if(opaque->avctx->extradata[i+4] == 104){
                            }else if(opaque->codecpar->extradata[i+4] == 104){
                                ppsIndex = i;
                                ALOGE("AMediaFormat find pps = %d",ppsIndex);
                            }
@@ -275,11 +274,11 @@ static int recreate_format_l(JNIEnv *env, IJKFF_Pipenode *node)
                    if(ppsIndex > 0 && spsIndex < ppsIndex){
                        length = ppsIndex-spsIndex;
                    }else{
                        length = opaque->avctx->extradata_size-spsIndex;
                        length = opaque->codecpar->extradata_size-spsIndex;
                    }

                    uint8_t *convert_buffer = (uint8_t *)calloc(1, length);
                    memcpy( convert_buffer, opaque->avctx->extradata+spsIndex, length);
                    memcpy( convert_buffer, opaque->codecpar->extradata+spsIndex, length);

                    SDL_AMediaFormat_setBuffer(opaque->input_aformat, "csd-0", convert_buffer, length);
                    free(convert_buffer);

获取MediaCodec的SPS和PPS

一、SPS和PPS文档说明

修复ijkplayer没有正确处理sps导致的MediaCodec解码失败问题_第1张图片
二、NALU类型

修复ijkplayer没有正确处理sps导致的MediaCodec解码失败问题_第2张图片
三、获取SPS、PPS两种方式 1、同步方式

ByteBuffer spsb = videoEncodec.getOutputFormat().getByteBuffer("csd-0");
byte[] sps = new byte[spsb.remaining()];
spsb.get(sps, 0, sps.length);

ByteBuffer ppsb = videoEncodec.getOutputFormat().getByteBuffer("csd-1");
byte[] pps = new byte[ppsb.remaining()];
ppsb.get(pps, 0, pps.length);


2、异步方式

@Override
public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
    MediaFormat outputFormat = codec.getOutputFormat();
    SPS = outputFormat.getByteBuffer("csd-0").array();
    PPS = outputFormat.getByteBuffer("csd-1").array();
    Log.e(TAG," onOutputFormatChanged  SPS  "+ CommonUtils.byteToHex(SPS));
    Log.e(TAG," onOutputFormatChanged  PPS  "+CommonUtils.byteToHex(PPS));
}

四、对应的sps、pps、I帧、P帧等数据

1、sps、pps
sps:000000016742ffffffc01effffffda05ffffffc145ffffff97ffffff8078402150
pps:0000000168ffffffce3cffffff80
2、I帧
data:0000000165ffffffb80010ffffffa413ffffffffffffffe5ffffffc3ffffffd31239695affffffabffffffb0340a
3、P帧
data:0000000161ffffffe9ffffffab13ffffffcd72ffffffdbffffffe5ffffffe25f5d7e64ffffff9ffffffff33affffffebffffffbd



总结:编码器编出来的数据首帧、sps、pps、I帧、P帧等顺序
 

利用MediaCodec 获取视频的sps pps 和视频数据_we1less的博客-CSDN博客

你可能感兴趣的:(ijkplayer,流媒体,java,开发语言)