天天看點

Qt基于FFmpeg播放本地 H.264(H264)檔案

最近在弄H264的硬體編解碼,基于DM3730,但是為了調試友善,在小紅帽上用FFmpeg實作了H264的軟體編解碼。現在弄了一個Windows的例子,給需要的同學參考一下,如果大家覺得有幫助,可以小手一抖,幫我點個贊。

這個例子是Qt Mingw版本的,FFmpeg可以去官網下載下傳,也可以自己編譯,編譯方法可以參考我的博文。

【1】Windows 7(Win7)下MinGW+msys編譯ffmpeg,并加入H264編碼支援

【2】linux下編譯ffmpeg,并加入H264編碼支援

【3】 linux下交叉編譯ffmpeg,并加入H264編碼支援

下面是H264解碼類

ch264decoder.h

#ifndef CH264DECODER_H
#define CH264DECODER_H

#include <string.h>
//C++引用C語言的頭檔案
extern "C"
{
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/fifo.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/avstring.h"
#include "libavutil/imgutils.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
}

class CH264Decoder
{
public:
    CH264Decoder();
    ~CH264Decoder();
    /*************************************************
      Function:initial
      Description:初始化
      Input:無
      Output:無
      Return:錯誤代碼
      Others:無
    *************************************************/
    int initial();
    /*************************************************
      Function:decode
      Description:解碼
      Input:pDataIn-待解碼資料,nInSize-待解碼資料長度
      Output:pDataOut-解碼後的資料,nWidth-解碼後的圖像寬度,nHeight-解碼後的圖像高度
      Return:錯誤代碼
      Others:解碼後的資料為RGB16格式
    *************************************************/
    int decode(uint8_t *pDataIn, int nInSize, uint8_t *pDataOut, int *nWidth, int *nHeight);
    /*************************************************
      Function:unInitial
      Description:銷毀
      Input:無
      Output:無
      Return:無
      Others:無
    *************************************************/
    void unInitial();

private:
    void deleteYUVTab();
    void createYUVTab_16();
    void displayYUV_16(unsigned int *pdst, unsigned char *y, unsigned char *u,unsigned char *v,
                       int width, int height, int src_ystride, int src_uvstride, int dst_ystride);
private:
    int *colortab;
    int *u_b_tab;
    int *u_g_tab;
    int *v_g_tab;
    int *v_r_tab;

    unsigned int *rgb_2_pix;
    unsigned int *r_2_pix;
    unsigned int *g_2_pix;
    unsigned int *b_2_pix;

    AVCodec *codec;
    AVCodecContext *context;
    AVFrame *frame;
    AVPacket packet;
};

#endif // CH264DECODER_H
           

ch264decoder.cpp

#include "ch264decoder.h"
#include <QDebug>

CH264Decoder::CH264Decoder()
{
    createYUVTab_16();
}

CH264Decoder::~CH264Decoder()
{
    deleteYUVTab();
}

void CH264Decoder::deleteYUVTab()
{
    av_free(colortab);
    av_free(rgb_2_pix);
}

void CH264Decoder::createYUVTab_16()
{
    int i;
    int u, v;

    colortab = (int *)av_malloc(4*256*sizeof(int));
    u_b_tab = &colortab[0*256];
    u_g_tab = &colortab[1*256];
    v_g_tab = &colortab[2*256];
    v_r_tab = &colortab[3*256];

    for (i=0; i<256; i++)
    {
        u = v = (i-128);

        u_b_tab[i] = (int) ( 1.772 * u);
        u_g_tab[i] = (int) ( 0.34414 * u);
        v_g_tab[i] = (int) ( 0.71414 * v);
        v_r_tab[i] = (int) ( 1.402 * v);
    }

    rgb_2_pix = (unsigned int *)av_malloc(3*768*sizeof(unsigned int));

    r_2_pix = &rgb_2_pix[0*768];
    g_2_pix = &rgb_2_pix[1*768];
    b_2_pix = &rgb_2_pix[2*768];

    for(i=0; i<256; i++)
    {
        r_2_pix[i] = 0;
        g_2_pix[i] = 0;
        b_2_pix[i] = 0;
    }

    for(i=0; i<256; i++)
    {
        r_2_pix[i+256] = (i & 0xF8) << 8;
        g_2_pix[i+256] = (i & 0xFC) << 3;
        b_2_pix[i+256] = (i ) >> 3;
    }

    for(i=0; i<256; i++)
    {
        r_2_pix[i+512] = 0xF8 << 8;
        g_2_pix[i+512] = 0xFC << 3;
        b_2_pix[i+512] = 0x1F;
    }

    r_2_pix += 256;
    g_2_pix += 256;
    b_2_pix += 256;
}

void CH264Decoder::displayYUV_16(unsigned int *pdst, unsigned char *y, unsigned char *u, unsigned char *v, int width, int height, int src_ystride, int src_uvstride, int dst_ystride)
{
    int i, j;
    int r, g, b, rgb;

    int yy, ub, ug, vg, vr;

    unsigned char* yoff;
    unsigned char* uoff;
    unsigned char* voff;

    int width2 = width/2;
    int height2 = height/2;

    for(j=0; j<height2; j++)
    {
        yoff = y + j * 2 * src_ystride;
        uoff = u + j * src_uvstride;
        voff = v + j * src_uvstride;

        for(i=0; i<width2; i++)
        {
            yy  = *(yoff+(i<<1));
            ub = u_b_tab[*(uoff+i)];
            ug = u_g_tab[*(uoff+i)];
            vg = v_g_tab[*(voff+i)];
            vr = v_r_tab[*(voff+i)];

            b = yy + ub;
            g = yy - ug - vg;
            r = yy + vr;

            rgb = r_2_pix[r] + g_2_pix[g] + b_2_pix[b];

            yy = *(yoff+(i<<1)+1);
            b = yy + ub;
            g = yy - ug - vg;
            r = yy + vr;

            pdst[(j*dst_ystride+i)] = (rgb)+((r_2_pix[r] + g_2_pix[g] + b_2_pix[b])<<16);

            yy = *(yoff+(i<<1)+src_ystride);
            b = yy + ub;
            g = yy - ug - vg;
            r = yy + vr;

            rgb = r_2_pix[r] + g_2_pix[g] + b_2_pix[b];

            yy = *(yoff+(i<<1)+src_ystride+1);
            b = yy + ub;
            g = yy - ug - vg;
            r = yy + vr;

            pdst [((2*j+1)*dst_ystride+i*2)>>1] = (rgb)+((r_2_pix[r] + g_2_pix[g] + b_2_pix[b])<<16);
        }
    }
}
int CH264Decoder::initial()
{
    avcodec_register_all();
    av_init_packet(&packet);

    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec)
    {
        printf("avcodec_find_encoder failed");
        return -1;
    }

    context = avcodec_alloc_context3(codec);
    if (!context)
    {
        printf("avcodec_alloc_context3 failed");
        return -2;
    }

    context->codec_type = AVMEDIA_TYPE_VIDEO;
    context->pix_fmt = AV_PIX_FMT_YUV420P;

    if (avcodec_open2(context, codec, NULL) < 0)
    {
        printf("avcodec_open2 failed");
        return -3;
    }

    frame = av_frame_alloc();
    if (!frame)
    {
        return -4;
    }

    return 0;
}

void CH264Decoder::unInitial()
{
    avcodec_close(context);
    av_free(context);
    av_frame_free(&frame);
}

int CH264Decoder::decode(uint8_t *pDataIn, int nInSize, uint8_t *pDataOut,int *nWidth, int *nHeight)
{
    av_init_packet(&packet);
    packet.size = nInSize;
    packet.data = pDataIn;

    if (packet.size > 0)
    {
        int got_picture=0;
        int ret= avcodec_decode_video2(context, frame, &got_picture, &packet);
        if (ret < 0)
        {
            printf("avcodec_encode_video2 failed");
            return -2;
        }

        if (got_picture)
        {
            *nWidth = context->width;
            *nHeight = context->height;

             displayYUV_16((unsigned int*)pDataOut, frame->data[0], frame->data[1],frame->data[2],
                     *nWidth,*nHeight,frame->linesize[0],frame->linesize[2],*nWidth);
        }
    }
    else
    {
        printf("no data to decode");
        return -1;
    }

    return 0;
}
           

使用方法,先調用initial()進行初始化,然後讀取本地H264檔案,調用decode()函數進行解碼,退出時調用unInitial()函數釋放資源。

需要注意的是,解碼後的資料是RGB16格式,轉換為QImage時,最後一個參數要用QImage::Format_RGB16,例如QImage image= QImage(outBuf, width, height, QImage::Format_RGB16);

下圖是播放效果:

Qt基于FFmpeg播放本地 H.264(H264)檔案

示範用的H264檔案下載下傳連結:http://download.csdn.net/detail/caoshangpa/9492803

源碼下載下傳連結:見http://blog.csdn.net/caoshangpa/article/details/51953208的評論

原創不易,轉載請标明出處:https://blog.csdn.net/caoshangpa/article/details/51953208

繼續閱讀