天天看點

FFmpeg4.0 實作H264視訊解碼器

     FFmpeg相信做流媒體的都不陌生,這就不累述了。最近在各方資源裡we聞到有人說”FFmpeg是做音視訊編解碼的“,說的太狹隘了,了解FFmpeg的一般也都知道VLC,VLC是一套以FFmpeg為基礎的完整流媒體解決方案,行内稱:VLC是汽車,FFMpeg就是發動機”,這就很形象的描述了FFmpeg的功能。FFmpeg是集音視訊采集、音視訊示範資料處理、解複用、編碼解碼、渲染等完整流媒體架構,内合入ffplay可實作渲染播放。webrtc是功能上與之并駕齊驅的一套架構,它合入一些新的編碼标準,如音頻的OPUS标準,SIP信令協定等,後續再以webrtc為架構寫一些blog。

      不說廢話,先上代碼。程式中将視訊流decode成YUV420P格式,合入SDL2渲染播放。雖然是完整代碼,目的為學術交流和技術共享,無意培養伸手黨。代碼中解決ffmpeg中新老接口對接,其它都是按decode的一般流程解碼,如有不妥之處,歡迎指正。技術交流群:479245235,歡迎各界精英論道。

#include "stdio.h"
#include "iostream"
#include "stdlib.h"
#include <string.h>
#include <math.h>
#include <chrono> //C++11時間類标準模闆庫
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavfilter/avfilter.h"
#include "libavutil/fifo.h"
#include "libavutil/samplefmt.h"
#include "libavutil/time.h"
#include "libavutil/timestamp.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libswresample/swresample.h"
#include "SDL2\SDL.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libswresample/swresample.h>
#include <SDL2/SDL.h>
#ifdef __cplusplus
};
#endif
#endif
using namespace std;
#define  _CRT_SECURE_NO_WARNINGS
int main()
{
    const char *fname;
    char errbuf[256] = { 0 };
    int iRes = 0;
    int vindex = -1;
    AVFormatContext *fctx = NULL;
    AVCodecContext *cctx = NULL;
    AVCodec *c = NULL;
    AVPacket *pkt = NULL;
    AVFrame *fr = NULL;
    AVFrame *yuv = NULL;
    uint8_t *buf = NULL;
    int vsize;
    struct SwsContext *imgCtx = NULL;
    SDL_Window *sw = NULL;
    SDL_Renderer *sr = NULL;
    SDL_Texture *ste = NULL;
    SDL_Rect srect = { 0 };
//  av_register_all();  //ffmpeg 4.0 After no
    if (SDL_Init(SDL_INIT_VIDEO) != 0)
    {
        cout << "SDL init failed!" << endl;
        return -1;
    }
        
    fctx = avformat_alloc_context();
    if ((iRes = avformat_open_input(&fctx, fname, NULL, NULL)) != 0)
    {
        cout << "File open failed!" << endl;
        return -1;
    }
        
    if (avformat_find_stream_info(fctx, NULL) < 0)
    {
        cout << "Stream find failed!\n";
        return -1;
    }
    av_dump_format(fctx, -1, fname, NULL);
    for (int i = 0; i < fctx->nb_streams; i++)
    {
        if (fctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            vindex = i;
    }
    if (vindex == -1)
    {
        cout << "Codec find failed!" << endl;
        return -1;
    }
    cctx = avcodec_alloc_context3(NULL);
    if (avcodec_parameters_to_context(cctx, fctx->streams[vindex]->codecpar) < 0)
    {
        cout << "Copy stream failed!" << endl;
        return -1;
    }
    c = avcodec_find_decoder(cctx->codec_id);
    if (!c) {
        cout << "Find Decoder failed!" << endl;
        return -1;
    }
    if (avcodec_open2(cctx, c, NULL) != 0) {
        cout << "Open codec failed!" << endl;
        return -1;
    }
    sw = SDL_CreateWindow("video", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 680, 540, SDL_WINDOW_OPENGL);
    sr = SDL_CreateRenderer(sw, -1, 0);
    ste = SDL_CreateTexture(sr, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, cctx->width, cctx->height);
    if (!sw || !sr || !ste) {
        cout << "Create SDL windows failed!" << endl;
        return -1;
    }
    srect.w = cctx->width;
    srect.h = cctx->height;
    imgCtx = sws_getContext(cctx->width, cctx->height, cctx->pix_fmt, cctx->width, cctx->height, AV_PIX_FMT_YUV420P,
        SWS_BICUBIC, NULL, NULL, NULL);
    if (!imgCtx) {
        cout << "Get swscale context failed!" << endl;
        return -1;
    }
    pkt = av_packet_alloc();
    fr = av_frame_alloc();
    yuv = av_frame_alloc();
    vsize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, cctx->width, cctx->height, 1);
    buf = (uint8_t *)av_malloc(vsize);
    av_image_fill_arrays(yuv->data, yuv->linesize,buf, AV_PIX_FMT_YUV420P, cctx->width, cctx->height, 1);
    while (av_read_frame(fctx, pkt) >= 0) {
        if (pkt->stream_index == vindex) {
            if ((iRes = avcodec_send_packet(cctx, pkt)) != 0)
            {
                cout << "Send video stream packet failed!" << endl;
                av_strerror(iRes, errbuf, 256);
                return -5;
            }
            if ((iRes = avcodec_receive_frame(cctx, fr)) != 0)
            {
                cout << "Receive video frame failed!\n";
                av_strerror(iRes, errbuf, 256);
                return -6;
            }
            sws_scale(imgCtx, fr->data, fr->linesize, 0, cctx->height, yuv->data, yuv->linesize);
            SDL_UpdateTexture(ste, &srect, yuv->data[0], yuv->linesize[0]);
            SDL_RenderClear(sr);
            SDL_RenderCopy(sr, ste, NULL, NULL);
            SDL_RenderPresent(sr);
        }
    }
    av_free(buf);
    av_frame_free(&yuv);
    av_frame_free(&fr);
    av_packet_free(&pkt);
    sws_freeContext(imgCtx);
    SDL_DestroyTexture(ste);
    SDL_DestroyRenderer(sr);
    SDL_DestroyWindow(sw);
    SDL_Quit();
    avcodec_free_context(&cctx);
    avformat_close_input(&fctx);
    avformat_free_context(fctx);
    return 0;
}      
FFmpeg4.0 實作H264視訊解碼器

最後,我司專注于流媒體和AI技術的研發與拓展,歡迎各界朋友莅臨指導或技術交流,QQ:2470425326。

繼續閱讀