修复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;i<opaque->avctx->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文档说明

在这里插入图片描述
二、NALU类型

在这里插入图片描述
三、获取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博客

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MediaCodec是Android平台提供的一个编码和解码的API,可以用来对音视频数据进行编解码,其中包括H.264视频编解码。下面是一个简单的示例代码,可以用来解码H.264视频数据: ``` MediaCodec codec = MediaCodec.createDecoderByType("video/avc"); MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height); format.setByteBuffer("csd-0", ByteBuffer.wrap(sps)); format.setByteBuffer("csd-1", ByteBuffer.wrap(pps)); codec.configure(format, surface, null, 0); codec.start(); ByteBuffer[] inputBuffers = codec.getInputBuffers(); ByteBuffer[] outputBuffers = codec.getOutputBuffers(); while (!isEOS) { int inIndex = codec.dequeueInputBuffer(10000); if (inIndex >= 0) { ByteBuffer buffer = inputBuffers[inIndex]; buffer.clear(); int sampleSize = extractor.readSampleData(buffer, 0); if (sampleSize < 0) { codec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); isEOS = true; } else { codec.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0); extractor.advance(); } } int outIndex = codec.dequeueOutputBuffer(info, 10000); switch (outIndex) { case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: outputBuffers = codec.getOutputBuffers(); break; case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: format = codec.getOutputFormat(); break; case MediaCodec.INFO_TRY_AGAIN_LATER: break; default: ByteBuffer buffer = outputBuffers[outIndex]; codec.releaseOutputBuffer(outIndex, true); break; } if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { isEOS = true; } } ``` 其中,`sps`和`pps`是H.264视频的序列参数集和图像参数集,`extractor`是用来读取视频数据的`MediaExtractor`对象,`surface`是用来显示解码后的视频的`Surface`对象。这段代码通过不断从`extractor`中读取视频数据,将其放入解码器中进行解码,并将解码后的数据输出到`Surface`中显示。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值