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)

相关推荐

  • H264视频解码算法概述

    为什么要有视频编码 视频其实是连续的图片,一般30帧每秒,以1分钟的1080p视频举例。 每个像素点的大小比如说是1.5字节(YUV420格式),一张图片的大小是: 1080*72…

    2024年3月15日
  • H.264学习笔记

    MPEG MPEG是动态图像专家组(Moving Picture Experts Group)的简称,它可以指: 一个成立于1988年的,研究视频和音频编码标准的组织 一系列音视频…

  • 解析H.264码流中的SPS帧和PPS帧

    H.264 是被广泛使用的视频压缩编码标准,无论视频文件还是网络直播,H.264标准都占据着可观的市场份额。 之所以H.264取得了巨大的成功,是因为它提出了一个新概念,把标准框架…

    2024年5月24日
  • 音视频编解码–H264 帧内预测

    帧内预测 最近看书学习过程中做了一些笔记,分别和大家一起分享一下,今天首先分享的是H264的帧内预测。 H.264/AVC 标准中规定的 4×4 亮度块的帧内预测样本预测…

    2023年10月11日
  • x264 如何提升 1‰ 的转码性能

    在8K视频编解码特别是解码部分,我做了一些优化工作,转码速度提升了50%以上。专家们评价曰:“主要围绕算法并行度的优化,属于算法性能优化的常规手段,在创新性和技术难度方面的体现较为…

    2024年3月22日
  • 实现一个h264编码器前期准备

    前言: H264是新一代的编码标准,以高压缩高质量和支持多种网络的流媒体传输著称,在编码方面,我理解的他的理论依据是:参照一段时间内图像的统计结果表明,在相邻几幅图像画面中,一般有…

    2023年10月22日

发表回复

登录后才能评论