音视频入门系列-FFmpeg篇(编码)

日常开发过程中,我们涉及到的主要是解码相关内容,但FFmpeg同样具有编码的能力。今天这篇文章简单介绍下FFmpeg编码的内容。

1.FFmpeg编码视频

使用FFmpeg库编码YUV,代码及调用逻辑如下(文末附代码):

图片

2.FFmpeg编码音频

使用FFmpeg库编码PCM,代码及调用逻辑如下(文末附代码):

图片

 3.编码YUV源码如下:

/* 
  author:八小时码字员 
  file:encode_yuv_to_h264.cpp
 */
 
#include "encode_yuv_to_h264.h"
#include "output_log.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//#define __STDC_CONSTANT_MACROS
extern "C"
{
  #include <libavcodec/avcodec.h>
  #include <libavutil/opt.h>
  #include <libavutil/imgutils.h>
}

static void encode(AVCodecContext* pCodecCtx, AVFrame *pFrame, AVPacket* pPacket, FILE* p_output_f)
{
  int ret;
  ret = avcodec_send_frame(pCodecCtx, pFrame);
  while (ret >= 0)
  {
    ret = avcodec_receive_packet(pCodecCtx, pPacket);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
      return;
    fwrite(pPacket->data, 1, pPacket->size, p_output_f);
    av_packet_unref(pPacket);
  }
}

//codec_name="libx264"
int encode_yuv_to_h264(const char* output_filePath)
{
  AVCodecContext* pCodecCtx = NULL;
  const AVCodec* pCodec = NULL;
  AVPacket* pPacket = NULL;
  AVFrame* pFrame = NULL;
  char codec_name[] = "libx264";
  unsigned char endcode[] = { 0x00, 0x00, 0x01, 0x7b };
  FILE* p_output_f = NULL;
  int i, x, y;
  int ret = 0;

  pCodec = avcodec_find_encoder_by_name(codec_name);
  //pCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
  if (!pCodec)
  {
    output_log(LOG_ERROR, "avcodec_find_encoder_by_name error, codec_name=%s", codec_name);
    ret = -1;
    goto end;
  }    
  pCodecCtx = avcodec_alloc_context3(pCodec);
  if (!pCodecCtx)
  {
    output_log(LOG_ERROR, "avcodec_alloc_context3 error, pCodecCtx is NULL");
    ret = -1;
    goto end;
  }
  pPacket = av_packet_alloc();
  pFrame = av_frame_alloc();

  //set AVCodecContext parameters
  pCodecCtx->bit_rate = 400000;
  pCodecCtx->width = 352;
  pCodecCtx->height = 288;
  pCodecCtx->time_base = { 1, 25 };
  pCodecCtx->framerate = { 25, 1 };
  /* emit one intra frame every ten frames
   * check frame pict_type before passing frame
   * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
   * then gop_size is ignored and the output of encoder
   * will always be I frame irrespective to gop_size
   */
  pCodecCtx->gop_size = 10;
  pCodecCtx->max_b_frames = 1;
  pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
  if (pCodec->id == AV_CODEC_ID_H264)
    av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);

  //open codec
  if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
  {
    ret = -1;
    goto end;
  }

  pFrame->format = pCodecCtx->pix_fmt;
  pFrame->width = pCodecCtx->width;
  pFrame->height = pCodecCtx->height;
  //Allocate new buffer(s) for audio or video data.
  if (av_frame_get_buffer(pFrame, 32) < 0)
  {
    output_log(LOG_ERROR, "av_frame_get_buffer error");
    ret = -1;
    goto end;
  }
  
  //open output_file
  fopen_s(&p_output_f, output_filePath, "wb");
  if (!p_output_f)
  {
    ret = -1;
    goto end;
  }
  
  //encode 5 seconds of video
  for (i = 0; i < 25 * 5; i++)
  {
    fflush(stdout);

    //make sure the frame data is writeable
    if (av_frame_is_writable(pFrame) < 0)
    {
      ret = -1;
      goto end;
    }

    //Y
    for (y = 0; y < pCodecCtx->height; y++)
    {
      for (x = 0; x < pCodecCtx->width; x++)
      {
        pFrame->data[0][y*pFrame->linesize[0] + x] = x + y + i * 3;
      }
    }
    //Y and V
    for (y = 0; y < pCodecCtx->height / 2; y++) 
    {
      for (x = 0; x < pCodecCtx->width / 2; x++) 
      {
        pFrame->data[1][y * pFrame->linesize[1] + x] = 128 + y + i * 2;
        pFrame->data[2][y * pFrame->linesize[2] + x] = 64 + x + i * 5;
      }
    }

    pFrame->pts = i;

    //encode this img
    encode(pCodecCtx, pFrame, pPacket, p_output_f);
  }

  //flush the encoder
  encode(pCodecCtx, NULL, pPacket, p_output_f);
  
  //add sequence end code to have a real MPEG file
  fwrite(endcode, 1, sizeof(endcode), p_output_f);
  
  fclose(p_output_f);

end:
  if (pCodecCtx)
    avcodec_free_context(&pCodecCtx);
  if (pPacket)
    av_packet_free(&pPacket);
  if (pFrame)
    av_frame_free(&pFrame);
  printf("=============== encode_yuv_to_h264 done ===============\n");
  return ret;
}

4.编码PCM源码如下:

/* 
  author:八小时码字员 
  file:encode_pcm_to_pm2.cpp
 */

#include "encode_pcm_to_mp2.h"
#include "output_log.h"

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

extern "C"
{
  #include <libavcodec/avcodec.h>
  #include <libavutil/channel_layout.h>
  #include <libavutil/common.h>
  #include <libavutil/frame.h>
  #include <libavutil/samplefmt.h>
}

/* check that a given sample format is supported by the encoder */
static int check_sample_fmt(const AVCodec *pCodec, enum AVSampleFormat sample_fmt)
{
  const enum AVSampleFormat *p = pCodec->sample_fmts;

  while (*p != AV_SAMPLE_FMT_NONE) 
  {
    if (*p == sample_fmt)
      return 1;
    p++;
  }
  return 0;
}

/* just pick the highest supported samplerate */
static int select_sample_rate(const AVCodec *pCodec)
{
  const int *p;
  int best_samplerate = 0;

  if (!pCodec->supported_samplerates)
    return 44100;

  p = pCodec->supported_samplerates; //< array of supported audio samplerates, or NULL if unknown, array is terminated by 0
  while (*p)
  {
    if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate))
      best_samplerate = *p;
    p++;
  }
  return best_samplerate;
}

/* select layout with the highest channel count */
static int select_channel_layout(const AVCodec *codec)
{
  const uint64_t *p;
  uint64_t best_ch_layout = 0;
  int best_nb_channels = 0;

  if (!codec->channel_layouts)
    return AV_CH_LAYOUT_STEREO;

  p = codec->channel_layouts; //< array of support channel layouts, or NULL if unknown. array is terminated by 0
  while (*p)
  {
    int nb_channels = av_get_channel_layout_nb_channels(*p);

    if (nb_channels > best_nb_channels) 
    {
      best_ch_layout = *p;
      best_nb_channels = nb_channels;
    }
    p++;
  }
  return best_ch_layout;
}

static void encode(AVCodecContext* pCodecCtx, AVFrame *pFrame, AVPacket* pPacket, FILE* p_output_f)
{
  int ret;
  ret = avcodec_send_frame(pCodecCtx, pFrame);
  while (ret >= 0)
  {
    ret = avcodec_receive_packet(pCodecCtx, pPacket);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
      return;
    fwrite(pPacket->data, 1, pPacket->size, p_output_f);
    av_packet_unref(pPacket);
  }
}

int encode_pcm_to_mp2(const char* output_filepath)
{
  AVCodecContext* pCodecCtx = NULL;
  const AVCodec* pCodec = NULL;
  AVPacket* pPacket = NULL;
  AVFrame* pFrame = NULL;
  FILE* p_output_f = NULL;
  int i, j, k, ret = 0;
  uint16_t *samples;
  float t, tincr;

  pCodec = avcodec_find_encoder(AV_CODEC_ID_MP2);
  if (!pCodec)
  {
    output_log(LOG_ERROR, "avcodec_find_encoder(AV_CODEC_ID_MP2) error");
    ret = -1;
    goto end;
  }
  pCodecCtx = avcodec_alloc_context3(pCodec);
  pPacket = av_packet_alloc();
  if (!pPacket)
  {
    output_log(LOG_ERROR, "av_packet_alloc error");
    ret = -1;
    goto end;
  }
  pFrame = av_frame_alloc();
  if (!pFrame)
  {
    output_log(LOG_ERROR, "av_frame_alloc error");
    ret = -1;
    goto end;
  }

  //set AVCodecContext parameters
  pCodecCtx->bit_rate = 64000;
  /* check that the encoder supports s16 pcm input */
  pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
  if (!check_sample_fmt(pCodec, pCodecCtx->sample_fmt))
  {
    output_log(LOG_ERROR, "check_sample_fmt error");
    ret = -1;
    goto end;
  }
  /* select other audio parameters supported by the encoder */
  pCodecCtx->sample_rate = select_sample_rate(pCodec);
  pCodecCtx->channel_layout = select_channel_layout(pCodec);
  pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);

  //open codec
  if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
  {
    ret = -1;
    goto end;
  }

  //set AVFrame parameters
  pFrame->nb_samples = pCodecCtx->frame_size;
  pFrame->format = pCodecCtx->sample_fmt;
  pFrame->channel_layout = pCodecCtx->channel_layout;
  if (av_frame_get_buffer(pFrame, 0) < 0)
  {
    output_log(LOG_ERROR, "av_frame_get_buffer error");
    ret = -1;
    goto end;
  }

  fopen_s(&p_output_f, output_filepath, "wb");
  if (!p_output_f)
  {
    output_log(LOG_ERROR, "fopen_s error");
    ret = -1;
    goto end;
  }

  //encode a single tone sound
  t = 0;
  tincr = 2 * M_PI * 440.0 / pCodecCtx->sample_rate;
  for (i = 0; i < 200; i++)
  {
    /* make sure the frame is writable -- makes a copy if the encoder
    * kept a reference internally */
    if (av_frame_make_writable(pFrame) < 0)
    {
      output_log(LOG_ERROR, "av_frame_make_writable error");
      ret = -1;
      goto end;
    }
    samples = (uint16_t*)pFrame->data[0];

    for (j = 0; j < pCodecCtx->frame_size; j++)
    {
      samples[2 * j] = (int)(sin(t) * 10000);

      for (k = 1; k < pCodecCtx->channels; k++)
      {
        samples[2 * j + k] = samples[2 * j];
      }
      t += tincr;
    }
    encode(pCodecCtx, pFrame, pPacket, p_output_f);
  }

  //flush the encoder 
  encode(pCodecCtx, NULL, pPacket, p_output_f);

  fclose(p_output_f);

end:
  if (pCodecCtx)
    avcodec_free_context(&pCodecCtx);
  if (pPacket)
    av_packet_free(&pPacket);
  if (pFrame)
    av_frame_free(&pFrame);
  printf("=============== encode_pcm_to_mp2 done ===============\n");
  return ret;
}

音视频入门系列文章已同步在微信公众号(可扫下方二维码关注):八小时码字员

音视频入门系列,同步录制了学习视频,已上传至bilibili(八小时码字员):音视频入门系列(图像、音频、字幕、视频封装格式,FFmpeg、ffplay源码分析,解码、编码、转码,流媒体协议,服务器部署)_哔哩哔哩_bilibili

音视频学习交流QQ群:693316541

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
|---------H.264encode(h264编码代码) |---------h264的编解码库和头文件,采用ffmpeg和x264编译(h264交叉编译ffmpeg) |--------- H264Player.rar(H264播放器源代码纯C++) |--------- |--------- H264的经典算法优化 |--------- JM H264解码程序 |--------- h264的编解码库和头文件, |--------- 采用ffmpeg和x264编译 h264和mepg4协议标准 |--------- H264流媒体源代码和相关资料.rar9 |--------- h264实时分析工具H264Visa |--------- h264视频采集 |--------- h264相关资料和源代码 |--------- ,decode264是一个使用ffmpeg sdk开发h264解码的例子 |--------- H264资料打包(二) |--------- H264资料打包(一) |--------- x264视频编解码在linux下实现的源码 |--------- [pdf文档] h264中文协议(中英文对照)9 |--------- T-REC-H.264 |--------- H264新手入门(H264乐园聊天记录 |--------- h24-tw(台湾教程) |--------- h264解码之CAVLC编码实例 |--------- h264_encoder(一个最经典的c语言的源代码) |--------- PDFH.264_GraphShow 2.linux_ffmpeg_h264视频音频编码界面(不断更新更新的内容通过邮箱发布)----- |---------linux环境下视频编程流程 |--------- 基于Linux高清视频无线传输系统设计研究 |--------- 基于嵌入式Linux的远程网络视频监控系统 |--------- 基于linux_V4L视频采集摄像程序 |--------- 基于Linux的视频传输系统(完整方案) |---------嵌入式Linux应用开发完全手册视频源码 |---------arm-linux平台的视频监控程序 |---------交叉编译ffmpeg_+_x264_编码H264_(arm_Linux 3.android视频音频_h264_ffmpeg------------------------ |---------JVT-G050 |--------- FFMPEG移植到Android |--------- 视频的源码,很详细并有注释可用于学习 Android视频采集和实现 |--------- ,采用h264视频编码,3gp格式 android中调用FFMPEG4解码方法与so文 |--------- jm15.0 |--------- ffmpeg for android library anroid手机上的一个音视频编解码库 |--------- |--------- 利用Android 浏览器引擎Webkit 加载具有视频监控功能的插件,以此插件为核心模块扩展浏览器的功能,从而实现手机监控 |--------- ffmpeg H264学习指南介绍 |--------- android opencore框架下的视频编码源码实现 android 视频监控的,开启本地摄像机,设置好端口即可 |--------- android 视频实时采集并上传到服务器上 android的视频监视器,捕捉接收远程的监视视频并显示在手机上
目录 第一章 多媒体概念介绍 6 1.1视频格式 6 1.1.1常见格式 6 1.2音频格式 9 1.2.1常见格式 9 1.2.2比较 15 1.3字幕格式 15 1.3.1外挂字幕与内嵌字幕的阐述 15 1.3.2外挂字幕视频与内嵌字幕视频的画面比较 15 1.3.3外挂字幕的三种格式 15 1.4采集录制和播放渲染 16 1.4.1视频采集 16 1. 4.2视频录制 17 1.4.3视频渲染 17 1.5编解码器 18 1.6容器和协议 19 1.6.1容器格式和编码格式 19 1.6.2协议 26 1.6.2.1 视频协议 26 1.6.2.2 音频协议. 26 1.6.2.3 上层通讯协议 27 1.7常用概念介绍 27 1.7.1硬解 27 1.7.2 IBP帧 28 1.7.3 DTS和PTS 31 1.7.4 分辨率 31 1.7.5 码率 32 1.7.6 帧率 32 1.7.7 RGB和YUV 32 1.7.8 实时和非实时 32 1.7.9 复合视频和s-video 32 1.7.10 硬件加速 32 1.7.11 FFmpeg Device 32 第二章 FFmpeg框架 34 2.1 FFmpeg概述 34 2.1.1简介 34 2.1.2功能 34 2.1.3模块组成 35 2.1.4命令集 35 2.2 媒体播放器三大底层框架 37 第三章 编译及简单应用 41 3.1 FFmpeg库编译和入门介绍 41 41 3.2 流媒体数据流程讲解 49 3.3 简单应用 51 3.4 SDL( Simple Direct Layer) 55 3.4.1 SDL显示视频 55 3.4.2 SDL显示音频 55 3.5 ffmpeg程序的使用(ffmpeg.exe,ffplay.exe,ffprobe.exe) 56 3.5.1 ffmpeg.exe 56 3.5.2 ffplay.exe 56 3.5.3 ffprobe.exe 56 第四章 数据结构 57 4.1 AVCodec结构体 59 4.2 AVCodecContext结构体 59 4.3 AVInputFormat结构体 60 4.4 AVFormatContext结构体 61 4.5 MovContext结构体 62 4.6 URLProtocol结构体 62 4.7 URLContext结构体 63 4.8 AVIOContext结构体(老版本为:ByteIOContext) 63 4.9 AVStream结构体 64 4.10 MOVStreamContext 结构体 65 4.11 AVPacket 结构体 66 4.12 AVPacketList 结构体 67 4.13 AVFrame结构体 67 第五章 重要模块 76 5.1 libavutil公共模块 76 1 文件列表 76 2 common.h 文件 76 3 bswap.h 文件 78 4 rational.h 文件 79 5 mathematics.h 文件 80 6 avutil.h 文件 80 5.2 libavcodec编解码模块 82 1 文件列表 82 2 avcodec.h 文件 82 3 allcodec.c 文件 87 4 dsputil.h 文件 87 5 dsputil.c 文件 88 6 utils_codec.c 文件 88 7 imgconvert_template.h 文件 99 8 imgconvert.c 文件 121 9 msrle.c 文件 164 10 turespeech_data.h 文件 171 11 turespeech.c 文件 174 5.3 libavformat容器模块 184 1 文件列表 184 2 avformat.h 文件 184 3 allformat.c 文件 190 4 cutils.c 文件 190 5 file.c 文件 192 6 avio.h 文件 194 7 avio.c 文件 196 8 aviobuf.c 文件 200 9 utils_format.c 文件 209 10 avidec.c 文件 220 5.4 libswscale视频色彩空间转换 243 5.5 libswresample音频重采样 243 5.6 libavfilter音视频滤器 243 5.7 libavdevice设备输入和输出容器 243 5.8 libpostproc视频后期处理 243 第六章 播放器 243 6.1 视频播放器 243 6.1.1 ffmpeg库的配置 243 6.1.2 一个简单的视频播放器 244 6.2 音频播放器 247 6.3 一个完整的播放器--ffplay 253 6.3.1 ffplay流程图 253 6.3.2 ffplay源码剖析 254 第七章 应用开发 275 7.1 ffmpeg库的使用:编码 275 第八章 关键函数介绍 280 8.1 avformat_open_input 280 8.2 avcodec_register_all() 281 8.3 av_read_frame() 283 8.4 avcodec_decode_video2() 283 8.5 transcode_init() 283 8.6 transcode() 294 第九章 ffmpeg相关工程 301 9.1 ffdshow 301 ffdshow 源代码分析1 : 整体结构 302 ffdshow 源代码分析 2: 位图覆盖滤镜(对话框部分Dialog) 304 ffdshow 源代码分析 3: 位图覆盖滤镜(设置部分Settings) 312 ffdshow 源代码分析 4: 位图覆盖滤镜(滤镜部分Filter) 317 ffdshow 源代码分析 5: 位图覆盖滤镜(总结) 322 ffdshow 源代码分析 6: 对解码器的dll的封装(libavcodec) 322 ffdshow 源代码分析 8: 视频解码器类(TvideoCodecDec) 344 ffdshow 源代码分析 9: 编解码器有关类的总结 352 9.2 LAV filters 357 LAV Filter 源代码分析 1: 总体结构 357 LAV Filter 源代码分析 2: LAV Splitter 358 LAV Filter 源代码分析 3: LAV Video (1) 382 LAV Filter 源代码分析 4: LAV Video (2) 400 9.3 MPlayer 427 9.3.1 Mplayer支持的格式 427 9.3.2 Mplayer 中头文件的功能分析 427 9.3.3 MPlayer.main 主流程简要说明 428 9.3.4 Mplayer源码分析 429 第十章 开发实例 436 第十一章 mp4文件封装协议分析 436 11.1 概述 436 11.2 mp4的物理结构 436 11.3 数据的组织结构 437 11.4 mp4的时间结构 437 11.5 文件结构分析 438 11.5.1 File Type Box(ftyp) 438 11.5.2 Movie Box(moov) 438 第十二章 flv 文件格式分析 457 12.1 概述 457 12.2 文件总体结构 457 12.3 文件结构分析 458 12.3.1 flv文件头的结构 458 12.3.2 body主体结构 459 附录A:常见问题 465 1 ffmpeg 从内存中读取数据 465 2 MFC中使用SDL播放音频没有声音的解决方法 465 附录B:经典代码示例 466 附录C:ffmpeg参数中文详细解释 477 附录D:ffplay的快捷键以及选项 479 附录E: ffmpeg处理rtmp流媒体 481

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值