美文网首页
H264对比H265

H264对比H265

作者: 34sir | 来源:发表于2024-08-21 14:01 被阅读0次

压缩效率

  • H.265
    压缩效率提高了约 50% 同等视觉质量 可以将文件压缩更小
  • H.264
    同等比特率 文件更大或者图像质量更低

算法

  • H.265
    更大的预测块(64*64 像素) 改进的运动补偿和变换
  • H.264
    较小的块(16*16 像素)

图像质量

  • H.265
    低比特率和高分辨率下可以更好的保证图像质量 减少压缩伪影
  • H.264
    相同比特率 会出现更多伪影和细节损失

计算复杂度

  • H.265
    更复杂的编码和解码过程 需要更高的计算资源和处理能力 编码较慢编码较快
  • H.264
    编码和解码复杂度较低 适合处理能力有限的设备

分辨率和帧率

  • H.265
    更高的分辨率(8K) 更高的帧率 适合高质量和高分辨率视频需求
  • H.264
    4K以下分辨率 可以支持高分辨率但性能效率低

兼容性

  • H.265
    旧设备或软件不被支持
  • H.264
    都兼容

如何解析?

H265

编码

try {
    // 创建解码器,指定要使用的解码格式
    MediaCodec codec = MediaCodec.createDecoderByType("video/hevc");
    MediaFormat format = MediaFormat.createVideoFormat("video/hevc", width, height);

   //描述平均位速率(以位/秒为单位)的键。 关联的值是一个整数
    mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height * 3/ 8);
   //描述视频格式的帧速率(以帧/秒为单位)的键。帧率,一般在15至30之内,太小容易造成视 频卡顿。
    mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
    //色彩格式,具体查看相关API,不同设备支持的色彩格式不尽相同
    mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, 
    MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
    //关键帧间隔时间,单位是秒
    mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);
    mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, 
    MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR);
    
    // 设置解码器的一些配置, 如需要的参数
    codec.configure(format, surface, null, 0);
    codec.start();
} catch (IOException e) {
    e.printStackTrace();
}
private void encoder(byte[] data){
        try {
            //拿到输入缓冲区,用于传送数据进行编码
            ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
            //拿到输出缓冲区,用于取到编码后的数据
            ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
            int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
            //当输入缓冲区有效时,就是>=0
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                inputBuffer.clear();
                //往输入缓冲区写入数据
                inputBuffer.put(data);
                //五个参数,第一个是输入缓冲区的索引,第二个数据是输入缓冲区起始索引,第三个是放入的数据大小,第四个是时间戳,保证递增就是
                mediaCodec.queueInputBuffer(inputBufferIndex, 0, data.length, System.nanoTime() / 1000, 0);
            }
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); // 用于存储解码或编码过程中的数据包的相关信息(比如时间戳、数据大小等)
            //拿到输出缓冲区的索引
            int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                byte[] outData = new byte[bufferInfo.size];
                outputBuffer.get(outData);


                // 将编码后的数据写入 MediaMuxer
                if (!muxerStarted) {
                    MediaFormat outputFormat = mediaCodec.getOutputFormat();
                    videoTrackIndex = mediaMuxer.addTrack(outputFormat);
                    mediaMuxer.start();
                    muxerStarted = true;
                }
                mediaMuxer.writeSampleData(videoTrackIndex, outputBuffer, bufferInfo); // MediaMuxer 允许你将音频和视频的编码数据合并为一个文件 比如MP4

                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

    }

BufferInfo 主要字段

  • offset
    数据缓冲区的起始位置 通常是 0 表示从缓冲区的起始位置开始读取数据

  • size
    当前数据块的大小,单位是字节 它指示了数据缓冲区中有效数据的长度

  • presentationTimeUs
    当前数据的展示时间戳 单位是微秒(microseconds) 它是数据应该被显示(或者说播放)时的时间 通常用于音视频同步 这是一个非常重要的参数 尤其是在视频播放、视频文件封装时

  • flags
    用于指示数据块的标志 这个字段表示数据的特性 如是否为关键帧(BUFFER_FLAG_KEY_FRAME)等 它可以包含多个标志位 用来描述缓冲区中的数据

解码

mediaCodec = MediaCodec.createDecoderByType(TYPE_HEVC);
mediaFormat = MediaFormat.createVideoFormat(TYPE_HEVC, mOriginalWidth, mOriginalHeight);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
public void putData(byte[] data, long timestamp) {
        if (data == null || data.length == 0) {
            Log.e(TAG, "rtsp拉流无效的输入数据");
            return;
        }
        if (queue.size() >= MAX_QUEUE_SIZE) {
            queue.clear();
        }

        RtspDecoderData decoderData = new RtspDecoderData();
        decoderData.data = data;
        decoderData.timestamp = timestamp;
        queue.offer(decoderData);


    }

    private void tryToFeedDecoder(){
        try {
            if (!mAvailableBuffers.isEmpty() && !queue.isEmpty() && mediaCodec != null) {
                RtspDecoderData input = queue.poll();
                if (input != null) {
                    int bufferIndex = mAvailableBuffers.remove(0);
                    ByteBuffer decoderInputBuffer = mediaCodec.getInputBuffer(bufferIndex);
                    decoderInputBuffer.clear();
                    decoderInputBuffer.put(input.data, 0, input.data.length);
                    mediaCodec.queueInputBuffer(bufferIndex, 0, input.data.length, input.timestamp, 0);
                }

            }
        } catch (Exception exception) {
            Log.d(TAG, "tryToFeedDecoder: " + exception.getMessage());
        }

    }



    public void decode() {
        mediaCodec.setCallback(new MediaCodec.Callback() {
            @Override
            public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
                if (mDebug) {
                    Log.d(TAG, "onInputBufferAvailable index: " + index);
                }
                mAvailableBuffers.add(index);
            }

            @Override
            public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
                if (mDebug) {
                    Log.d(TAG, "onOutputBufferAvailable index: " + index + " time: " + System.currentTimeMillis());
                }

                if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                    codec.releaseOutputBuffer(index, false);
                    return; // 处理流结束
                }

                if (info.size <= 0) {
                    Log.w(TAG, "检测到损坏的帧,跳过");
                    codec.releaseOutputBuffer(index, false);
                    return; // 跳过损坏的帧
                }

                ByteBuffer outputBuffer = codec.getOutputBuffer(index);
                if (outputBuffer == null) {
                    Log.e(TAG, "输出缓冲区为空");
                    codec.releaseOutputBuffer(index, false);
                    return;// 如果输出缓冲区无效,则跳过处理
                }
                mLastBuffer = outputBuffer;

                if (mFishEyeCorrectionTag) {
                    mResultBuffer.clear();
                    outputBuffer.position(0);
                    if (mIsCut) {
                        long before = System.currentTimeMillis();
                        if (mDebug) {
                            Log.d(TAG, "fisheyeCutNV12ToNv12ByCl start");
                        }
                        fishEyeCorrectionNative.fisheyeCutNV12ToNv12ByCl(outputBuffer, mResultBuffer, FishEyeCorrectionManager.getInstance().mPitch, 0);
                        if (mDebug) {
                            Log.d(TAG, "fisheyeCutNV12ToNv12ByCl end=" + (System.currentTimeMillis() - before));
                        }
                    } else {
                        long before = System.currentTimeMillis();
                        if (mDebug) {
                            Log.d(TAG, "fisheyeRemapNV12ToNv12ByCl start");
                        }
                        fishEyeCorrectionNative.fisheyeRemapNV12ToNv12ByCl(outputBuffer, mResultBuffer, FishEyeCorrectionManager.getInstance().mPitch, 0);
                        if (mDebug) {
                            Log.d(TAG, "fisheyeRemapNV12ToNv12ByCl end=" + (System.currentTimeMillis() - before));
                        }
                    }

                    mResultBuffer.get(resultData);


                    if (callBack != null) {
                        callBack.callBack(resultData, true);
                    }
                    mediaCodec.releaseOutputBuffer(index, false);
                } else {
                    outputBuffer.get(mYuvData);
                    if (callBack != null) {
                        callBack.callBack(mYuvData, false);
                    }
                    mediaCodec.releaseOutputBuffer(index, false);
                }


            }

            @Override
            public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {
                Log.d(TAG, "onError e: " + e.getMessage());
                mediaCodec.reset();
                mAvailableBuffers.clear();

            }

            @Override
            public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {

            }
        });
        mediaCodec.configure(mediaFormat, null, null, 0);
        mediaCodec.start();

H264

// 初始化解码器
MediaCodec codec = MediaCodec.createDecoderByType("video/avc");

// 创建视频格式
MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);

// 配置解码器,指定输出 Surface
Surface surface = surfaceView.getHolder().getSurface();
codec.configure(format, surface, null, 0);
codec.start();

// 送入数据
int inputBufferIndex = codec.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
    ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferIndex);
    inputBuffer.clear();
    inputBuffer.put(h264Data);  // h264Data 是你的 H.264 编码数据
    codec.queueInputBuffer(inputBufferIndex, 0, h264Data.length, 0, 0);
}

// 获取并渲染输出数据
int outputBufferIndex = codec.dequeueOutputBuffer(info, -1);
if (outputBufferIndex >= 0) {
    codec.releaseOutputBuffer(outputBufferIndex, true);  // 渲染到 Surface
}

// 清理资源
codec.stop();
codec.release();

相关文章

网友评论

      本文标题:H264对比H265

      本文链接:https://www.haomeiwen.com/subject/iogbkjtx.html