H264解码原理详解

H.264的编码原理参考文章H.264的编码原理

解码原理

解码器负责将符合H.264码流规范的压缩视频流解码,并进行图像重建。
根据如下图所示的解码器流图,我们可以看出基本的解码流程如下:解码器从网络提取层中接收压缩的比特流,经过对码流进行熵解码和重排序获得量化系数X;这些系数经过反量化和反变换得到残差数据D;解码器使用从码流中解码得到的头信息创建一个预测数据PRED,PRED与残差数据D求和得到图像块数据uF;最后每个uF通过去方块滤波得到重建图像的解码块F。预测数据PRED是由参考图像经过运动估计,或者未经滤波的重建图像经过帧内预测得到。
H264解码原理详解_第1张图片

NAL单元结构

H.264/AVC标准中,NAL(Network Abstract Layer)是以NALU(NAL Unit)为单元来支持编码数据在基于包交换技术网络中传输的。它定义了符合传输层或存储介质要求的数据格式,同时给出各自的头信息,进一步提供了视频编码的接口。
H264解码原理详解_第2张图片
每一个NAL单元都包含两个部分:一个字节的NAL Header和一个原始字节序列载荷RBSP(Raw Byte Sequence Payload)。RBSP可以是一个编码片、A/B/C型数据分片、图像参数集、序列参数集等。NALHeader为一个字节,由定长的三部分组成:隐藏比特位(F)和NAL-REFERENCE-IDC(NRI)、NAL类型(NAL_Type)。

隐藏比特位

在H.264编码中默认置为0,当网络识别到单元中存在比特错误时,可将其置为1。F位主要用于适应不同种类的网络环境。

NRI

NAL单元的优先级。这两个比特位指示了NAL单元的优先级,用于指定NAL单元在传输时的处理顺序。0表示最高优先级,3表示最低优先级。

NAL_Type

取值范围是0~31,标识本单元内的RBSP数据结构的类型

NAL单元解码过程

进行NAL单元解码之前,首先进行RTP解析(采用RTP封装)或者通过起始码检测(采用比特流方式),从传输码流中查找获取NAL单元数据。

NAL单元解码的总体流程是:首先从NAL单元中提取出RBSP语法结构,然后按照流程处理RBSP语法结构。对于NAL单元的解码过程,输入是NAL单元,输出结果是解码后的当前图像(CurrPic)的样点值。

使用ffmpeg进行H.264解码并播放视频

extern "C" {
#include 
#include 
#include 
#include 
}

int main() {
    // 初始化FFmpeg
    av_register_all();

    // 打开输入文件
    AVFormatContext *formatContext = avformat_alloc_context();
    avformat_open_input(&formatContext, "output.h264", nullptr, nullptr);
    avformat_find_stream_info(formatContext, nullptr);

    // 找到视频流
    int videoStreamIndex = -1;
    for (int i = 0; i < formatContext->nb_streams; ++i) {
        if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
            break;
        }
    }

    if (videoStreamIndex == -1) {
        fprintf(stderr, "No video stream found.\n");
        return -1;
    }

    // 打开解码器
    AVCodec *codec = avcodec_find_decoder(formatContext->streams[videoStreamIndex]->codecpar->codec_id);
    AVCodecContext *codecContext = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codecContext, formatContext->streams[videoStreamIndex]->codecpar);
    avcodec_open2(codecContext, codec, nullptr);

    // 创建图像显示窗口
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window *window = SDL_CreateWindow("H.264 Decoding", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                          codecContext->width, codecContext->height, SDL_WINDOW_OPENGL);
    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YUV420P, SDL_TEXTUREACCESS_STREAMING,
                                             codecContext->width, codecContext->height);

    // 解码并显示
    AVPacket packet;
    av_init_packet(&packet);
    SDL_Event event;
    int frameFinished;
    AVFrame *frame = av_frame_alloc();

    while (av_read_frame(formatContext, &packet) >= 0) {
        if (packet.stream_index == videoStreamIndex) {
            avcodec_decode_video2(codecContext, frame, &frameFinished, &packet);
            if (frameFinished) {
                SDL_UpdateYUVTexture(texture, nullptr, frame->data[0], frame->linesize[0],
                                      frame->data[1], frame->linesize[1],
                                      frame->data[2], frame->linesize[2]);
                SDL_RenderClear(renderer);
                SDL_RenderCopy(renderer, texture, nullptr, nullptr);
                SDL_RenderPresent(renderer);
            }
        }
        SDL_PollEvent(&event);
        if (event.type == SDL_QUIT)
            break;

        av_packet_unref(&packet);
    }

    // 释放资源
    avcodec_free_context(&codecContext);
    avformat_close_input(&formatContext);
    av_frame_free(&frame);

    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

你可能感兴趣的:(音视频开发,音视频,linux)