FFmpeg4.0 实现H264视频解码器

      FFmpeg对于流媒体技术的开发者来说,都不陌生,这里开源我们一项基于FFmpeg4的H264视频解码器源码。因为贸易战的原因,我们暂停了一段时间的开源推进,后续将推出基于FFmpeg5的开源,欢迎各界精英交流指导。 FFmpeg是集音视频采集、音视频演示数据处理、解复用、编码解码、渲染等完整流媒体框架,内合入ffplay可实现渲染播放。webrtc是功能上与之并驾齐驱的一套架构,它合入一些新的编码标准,如音频的OPUS标准,SIP信令协议等,后续再以webrtc为框架写一些blog。

      代码中将H264视频流解码成YUV420P原始流,对于流媒体直播或监控来说,合入SDL2渲染,就能高质量播放出实时视频;如今,AR/VR、AI深度学习、MetaVerse的相继蓬勃发展,需要在原始流上做图像处理技术,之后进行渲染播放。说到了这,简单介绍下视频图像处理技术,因为解码后的视频流是按一个个的数据包存储在临时缓存区,而且每个数据包的第一帧都是I帧(完整图片),所以一般是先用openGL家族的引擎做相应的处理后,导入到相应的框架中做其他处理。我们在代码中解决ffmpeg中新老函数交替,和其他一些代码上的优化,我司在这里主要是做API开发,所以没采用标准的C++模板。详见代码:


#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;
}

作者:辰尘

来源:公众号-绿视信息

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论