diario de aprendizaje ffmpeg 5 - decodificación h264 usando ffmpeg

diario de aprendizaje ffmpeg 5 - decodificación h264 usando ffmpeg


Cuando determinamos que el formato de codificación de un video está codificado usando h264, entonces se puede realizar la decodificación correspondiente. Después de decodificar el video, los datos decodificados se pueden representar en consecuencia y se pueden agregar efectos especiales. Aprendamos cómo decodificar el video.

Primero aclare algunos términos técnicos:

  • Datos YUV
    YUV: formato de píxel de video Datos YUV: datos de formato de píxel de video
  • H264
    H264: formato de datos de compresión de video

pasos de decodificación

  1. registrar componente
  1. Inicializar el contexto de formato de encapsulación
  1. Obtener el valor del parámetro del atributo del archivo de origen
  1. encontrar codificador
  1. códec de vídeo abierto
  1. decodificar
  1. almacenamiento de datos
  1. liberar códec

explicación de la función

  • AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
    Función: asigne un AVCodecContext y configure sus campos a los valores predeterminados, la estructura resultante debe liberarse Use el
    parámetro avcodec_free_context(): códec Si no es nulo, asigne datos privados e inicialice el códec dado el valor predeterminado para . Entonces llamar avcodec_open2() con un códec diferente es ilegal. Si es NULL, los valores predeterminados específicos del códec no se inicializarán, lo que puede conducir a valores predeterminados subóptimos (esto es principalmente importante para los codificadores, como libx264).
    Valor devuelto: Devuelve un mecanismo AVCodecContext lleno de valores predeterminados, o NULL en caso de falla
  • AVFrame *av_frame_alloc(void);
    Función: Asignar un AVFrame y establecer sus campos a los valores predeterminados. La estructura resultante debe liberarse usando av_frame_free().
    Valor de retorno: un AVFrame lleno de valores predeterminados o NULL en caso de falla.
    Nota: Esto solo asigna el AVFrame en sí, no el búfer de datos. Estos deben asignarse por otros medios, como a través de av_frame_get_buffer() o manualmente.
  • int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
    int *got_picture_ptr,
    const AVPacket *avpkt);
    Función: decodifica un cuadro de datos de video. Ingrese una estructura comprimida y codificada AVPacket y emita una estructura decodificada AVFrame.

Introducción al entorno del proyecto

versión ffmpeg:

>ffmpeg -version
ffmpeg version n4.4-78-g031c0cb0b4-20210628 Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 10-win32 (GCC) 20210408
configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-config=pkg-config --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --enable-gpl --enable-version3 --disable-debug --enable-shared --disable-static --disable-w32threads --enable-pthreads --enable-iconv --enable-libxml2 --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-libvorbis --enable-opencl --enable-libvmaf --enable-vulkan --enable-amf --enable-libaom --enable-avisynth --enable-libdav1d --enable-libdavs2 --disable-libfdk-aac --enable-ffnvcodec --enable-cuda-llvm --enable-libglslang --enable-libgme --enable-libass --enable-libbluray --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvpx --enable-libwebp --enable-lv2 --enable-libmfx --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librav1e --enable-librubberband --enable-schannel --enable-sdl2 --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --enable-libvidstab --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --extra-cflags=-DLIBTWOLAME_STATIC --extra-cxxflags= --extra-ldflags=-pthread --extra-ldexeflags= --extra-libs=-lgomp --extra-version=20210628
libavutil      56. 70.100 / 56. 70.100
libavcodec     58.134.100 / 58.134.100
libavformat    58. 76.100 / 58. 76.100
libavdevice    58. 13.100 / 58. 13.100
libavfilter     7.110.100 /  7.110.100
libswscale      5.  9.100 /  5.  9.100
libswresample   3.  9.100 /  3.  9.100
libpostproc    55.  9.100 / 55.  9.100

qt versión: 5.12.0

Interpretación de errores en la codificación

error 1

$ cat /etc/issue
ubuntu:Ubuntu 20.04.2 LTS \n \l
qt:5.12.3

Para la instalación de ffmpeg, consulte los párrafos en el diario de aprendizaje de ffmpeg 1: introducción básica de ffmpeg (comprensión de conceptos relacionados, recopilación de datos)安装ffmpeg .

ubuntu instalar libx264

$ wget https://johnvansickle.com/ffmpeg/release-source/libx264-git.tar.xz
$ tar -xvf libx264-git.tar.xz
$ cd libx264-git/
$ sudo ./configure --enable-shared
$ sudo make -j4
$ sudo make install

referencia:

Compile libx264 en ffmpeg bajo ubuntu

$ sudo ./configure --enable-shared --prefix=/usr/local/ffmpeg --enable-libx264 --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib --enable-gpl
$ sudo make -j4
$ sudo make install

referencia:

Una vez completada la compilación, ejecute el proyecto y ret = avcodec_decode_video2(pCodecCtx,pFrame,&ret,&packet);la declaración no informa de un error.

error 2

El entorno de compilación después de instalar la nueva versión

versión ffmpeg:

$ ffmpeg -version
ffmpeg version 4.1 Copyright (c) 2000-2018 the FFmpeg developers
built with gcc 9 (Ubuntu 9.3.0-17ubuntu1~20.04)
configuration: --enable-shared --prefix=/usr/local/ffmpeg --enable-libx264 --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib --enable-gpl
libavutil      56. 22.100 / 56. 22.100
libavcodec     58. 35.100 / 58. 35.100
libavformat    58. 20.100 / 58. 20.100
libavdevice    58.  5.100 / 58.  5.100
libavfilter     7. 40.101 /  7. 40.101
libswscale      5.  3.100 /  5.  3.100
libswresample   3.  3.100 /  3.  3.100
libpostproc    55.  3.100 / 55.  3.100

qt versión: 5.12.2

Nota:
En la versión ffmpeg4.1, se elimina el archivo de encabezado libavcodec/packet.h.

El código completo del proyecto es el siguiente

contenido del archivo profesional:

TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += \
        main.cpp


INCLUDEPATH += /usr/local/ffmpeg/include
LIBS += /usr/local/ffmpeg/lib/libavcodec.so \
        /usr/local/ffmpeg/lib/libavdevice.so \
        /usr/local/ffmpeg/lib/libavfilter.so \
        /usr/local/ffmpeg/lib/libavformat.so \
        /usr/local/ffmpeg/lib/libavutil.so \
        /usr/local/ffmpeg/lib/libpostproc.so \
        /usr/local/ffmpeg/lib/libswresample.so \
        /usr/local/ffmpeg/lib/libswscale.so

El contenido de main.cpp es el siguiente:

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

extern "C"{
#include <stdio.h>
#include <stdlib.h>
#include "libavutil/avutil.h"
#include "libavformat/avformat.h"
//#include "libavcodec/packet.h"
#include "libavcodec/avcodec.h"

}

int main(int argc,char **argv)
{

    cout << "Hello World!" << endl;

    //注册组件

    avcodec_register_all();

    AVCodecID codec_id = AV_CODEC_ID_H264;
    AVCodec *pcodec = NULL;
    AVCodecContext *pCodecCtx = NULL;

    //查找编码器
    pcodec = avcodec_find_decoder(codec_id);
    if(!pcodec){
        av_log(NULL,AV_LOG_ERROR,"no found decoder\n");
        return 0;
    }

    //初始化封装格式上下文
    pCodecCtx = avcodec_alloc_context3(pcodec);
    if(!pCodecCtx){
        av_log(NULL,AV_LOG_ERROR,"avcodec_alloc_context3 is failed\n");
        return 0;
    }

    //初始化参数,下面的参数应该由具体的业务决定

    pCodecCtx->time_base.num = 1;
    pCodecCtx->frame_number = 1;
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecCtx->bit_rate = 0;
    pCodecCtx->time_base.den = 29;
    pCodecCtx->width = 544;
    pCodecCtx->height = 960;

    //打开视频解码器
    if (avcodec_open2(pCodecCtx,pcodec,NULL) < 0){
        av_log(NULL,AV_LOG_ERROR,"avcodec_open2 is failed\n");
        return 0;
    }

    //读取文件数据
    char *buff = new char[1024*1024*4];
    fstream fileio;
    fileio.open("../jk.mp4",ios::binary | ios::in);
    fileio.read(buff,1024*1024*4);
    cout << "read size:" << fileio.gcount() << endl;
    fileio.close();

    //进行解码
    int ret = 0;
    AVFrame *pFrame = NULL;
    pFrame = av_frame_alloc();
    AVPacket packet;
    av_init_packet(&packet);

    ret = avcodec_decode_video2(pCodecCtx,pFrame,&ret,&packet);

    cout << "ret:" << ret << endl;  //-22

    if ((ret < 0)  && (ret != AVERROR(EAGAIN)) && (ret != AVERROR_EOF) ){
        av_log(NULL,AV_LOG_ERROR,"avcodec_send_packet error\n");
        return 0;
    }

    else {
        cout << "start" << endl;
        int picSize = pCodecCtx->height * pCodecCtx->width;
        int newSize = int(picSize * 1.5);

        //申请内存
        unsigned char *buf = new unsigned char[newSize];

        //数据写入
        int a = 0,i = 0;
        for(i = 0;i < pCodecCtx->height;i++){
            memcpy(buf+a,pFrame->data[0] + i*pFrame->linesize[0],pCodecCtx->width );
            a += pCodecCtx->width;
        }

        for (i = 0;i < pCodecCtx->height/2;i++){
            memcpy(buf+a,pFrame->data[1] + i*pFrame->linesize[1],pCodecCtx->width/2 );
            a += pCodecCtx->width/2;
        }

        for (i = 0;i < pCodecCtx->height/2;i++){
            memcpy(buf+a,pFrame->data[2] + i*pFrame->linesize[2],pCodecCtx->width/2 );
            a += pCodecCtx->width/2;
        }

        cout << "data:" << buf << endl;
    }

    return 0;
}

referencia

posdata

おすすめ

転載: blog.csdn.net/bootleader/article/details/122868620