ffmpeg实战教程(三)音频PCM采样为AAC,视频YUV编码为H264/HEVC

https://blog.csdn.net/King1425/article/details/71180330

音频PCM采样数据编码为压缩码流(MP3,WMA,AAC等)

简单介绍一下流程中各个函数的意义:

av_register_all():注册FFmpeg所有编解码器。avformat_alloc_output_context2():初始化输出码流的AVFormatContext。avio_open():打开输出文件。av_new_stream():创建输出码流的AVStream。avcodec_find_encoder():查找编码器。avcodec_open2():打开编码器。avformat_write_header():写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。avcodec_encode_audio2():编码音频。即将AVFrame(存储PCM采样数据)编码为AVPacket(存储AAC,MP3等格式的码流数据)。av_write_frame():将编码后的视频码流写入文件。av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。

源代码如下:

#include <stdio.h>#define __STDC_CONSTANT_MACROS#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endif
#endifint flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index){int ret;int got_frame;AVPacket enc_pkt;if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &CODEC_CAP_DELAY))return 0;while (1) {enc_pkt.data = NULL;enc_pkt.size = 0;av_init_packet(&enc_pkt);ret = avcodec_encode_audio2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,NULL, &got_frame);av_frame_free(NULL);if (ret < 0)break;if (!got_frame){ret=0;break;}printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size);/* mux encoded frame */ret = av_write_frame(fmt_ctx, &enc_pkt);if (ret < 0)break;}return ret;
}int main(int argc, char* argv[])
{AVFormatContext* pFormatCtx;AVOutputFormat* fmt;AVStream* audio_st;AVCodecContext* pCodecCtx;AVCodec* pCodec;uint8_t* frame_buf;AVFrame* pFrame;AVPacket pkt;int got_frame=0;int ret=0;int size=0;FILE *in_file=NULL;                         //Raw PCM dataint framenum=1000;                          //Audio frame numberconst char* out_file = "ws.aac";          //Output URLint i;in_file= fopen("ws.pcm", "rb");av_register_all();pFormatCtx = avformat_alloc_context();//得到最合适的AVOutputFormat并且返回给avformat_alloc_output_context2(),赋值给刚刚新建的AVFormatContextfmt = av_guess_format(NULL, out_file, NULL);pFormatCtx->oformat = fmt;//Open output URL 创建的AVIOContext结构体 打开输出文件if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0){printf("Failed to open output file!\n");return -1;}//初始化AVFrameaudio_st = avformat_new_stream(pFormatCtx, 0);if (audio_st==NULL){return -1;}pCodecCtx = audio_st->codec;pCodecCtx->codec_id = fmt->audio_codec;pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;pCodecCtx->sample_rate= 44100;pCodecCtx->channel_layout=AV_CH_LAYOUT_STEREO;pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);pCodecCtx->bit_rate = 64000;  /*av_dump_format()是一个手工调试的函数,能使我们看到pFormatCtx->streams里面有什么内容。一般接下来我们使用av_find_stream_info()函数,它的作用是为pFormatCtx->streams填充上正确的信息。*/av_dump_format(pFormatCtx, 0, out_file, 1);pCodec = avcodec_find_encoder(pCodecCtx->codec_id);//查找编码器if (!pCodec){printf("Can not find encoder!\n");return -1;}if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0){//初始化一个编解码器的AVCodecContextprintf("Failed to open encoder!\n");return -1;}pFrame = av_frame_alloc();pFrame->nb_samples= pCodecCtx->frame_size;pFrame->format= pCodecCtx->sample_fmt;size = av_samples_get_buffer_size(NULL, pCodecCtx->channels,pCodecCtx->frame_size,pCodecCtx->sample_fmt, 1);frame_buf = (uint8_t *)av_malloc(size);avcodec_fill_audio_frame(pFrame, pCodecCtx->channels, pCodecCtx->sample_fmt,(const uint8_t*)frame_buf, size, 1);//Write Headeravformat_write_header(pFormatCtx,NULL);av_new_packet(&pkt,size);for (i=0; i<framenum; i++){//Read PCMif (fread(frame_buf, 1, size, in_file) <= 0){printf("Failed to read raw data! \n");return -1;}else if(feof(in_file)){break;}pFrame->data[0] = frame_buf;  //PCM DatapFrame->pts=i*100;got_frame=0;//Encoderet = avcodec_encode_audio2(pCodecCtx, &pkt,pFrame, &got_frame);if(ret < 0){printf("Failed to encode!\n");return -1;}if (got_frame==1){printf("Succeed to encode 1 frame! \tsize:%5d\n",pkt.size);pkt.stream_index = audio_st->index;ret = av_write_frame(pFormatCtx, &pkt);av_free_packet(&pkt);}}//Flush Encoderret = flush_encoder(pFormatCtx,0);if (ret < 0) {printf("Flushing encoder failed\n");return -1;}//Write Trailerav_write_trailer(pFormatCtx);//Cleanif (audio_st){avcodec_close(audio_st->codec);av_free(pFrame);av_free(frame_buf);}avio_close(pFormatCtx->pb);avformat_free_context(pFormatCtx);fclose(in_file);return 0;
}

程序运行完成后,会将跟目录中的一个PCM采样数据文件(ws.pcm)编码为AAC码流文件(ws.aac)。

const char* out_file = "ws.aac";
in_file= fopen("ws.pcm", "rb");

下面我们实现将YUV数据编码为h264/h265

调用libavcodec将YUV数据编码为H.264/HEVC等格式的压缩视频码流。

关键函数: 
avcodec_register_all():注册所有的编解码器。 
avcodec_find_encoder():查找编码器。 
avcodec_alloc_context3():为AVCodecContext分配内存,创建AVCodecContext结构体。 
avcodec_open2():打开编码器。 
avcodec_encode_video2():编码一帧数据。

两个存储数据的结构体如下所列:

AVFrame:存储一帧未编码的像素数据。
AVPacket:存储一帧压缩编码数据。

下面先看看运行效果: 
 
可见运行程序后编码生成了ws.h264和ws.hevc文件,暴风竟然识别hevc文件(⊙o⊙)。

#include <stdio.h>#define __STDC_CONSTANT_MACROS#ifdef _WIN32
//Windows
extern "C"
{
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavutil/imgutils.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#ifdef __cplusplus
};
#endif
#endif//test different codec
#define TEST_H264  0
#define TEST_HEVC  1int main(int argc, char* argv[])
{AVCodec *pCodec;AVCodecContext *pCodecCtx= NULL;int i, ret, got_output;FILE *fp_in;FILE *fp_out;AVFrame *pFrame;AVPacket pkt;int y_size;int framecnt=0;char filename_in[]="ws_output.yuv";#if TEST_HEVCAVCodecID codec_id=AV_CODEC_ID_HEVC;char filename_out[]="ws.hevc";
#elseAVCodecID codec_id=AV_CODEC_ID_H264;char filename_out[]="ws.h264";
#endifint in_w=480,in_h=272;  int framenum=100;   avcodec_register_all();pCodec = avcodec_find_encoder(codec_id);if (!pCodec) {printf("Codec not found\n");return -1;}pCodecCtx = avcodec_alloc_context3(pCodec);if (!pCodecCtx) {printf("Could not allocate video codec context\n");return -1;}pCodecCtx->bit_rate = 400000;pCodecCtx->width = in_w;pCodecCtx->height = in_h;pCodecCtx->time_base.num=1;pCodecCtx->time_base.den=25;pCodecCtx->gop_size = 10;pCodecCtx->max_b_frames = 1;pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;if (codec_id == AV_CODEC_ID_H264)av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {printf("Could not open codec\n");return -1;}pFrame = av_frame_alloc();if (!pFrame) {printf("Could not allocate video frame\n");return -1;}pFrame->format = pCodecCtx->pix_fmt;pFrame->width  = pCodecCtx->width;pFrame->height = pCodecCtx->height;ret = av_image_alloc(pFrame->data, pFrame->linesize, pCodecCtx->width, pCodecCtx->height,pCodecCtx->pix_fmt, 16);if (ret < 0) {printf("Could not allocate raw picture buffer\n");return -1;}//Input raw datafp_in = fopen(filename_in, "rb");if (!fp_in) {printf("Could not open %s\n", filename_in);return -1;}//Output bitstreamfp_out = fopen(filename_out, "wb");if (!fp_out) {printf("Could not open %s\n", filename_out);return -1;}y_size = pCodecCtx->width * pCodecCtx->height;//Encodefor (i = 0; i < framenum; i++) {av_init_packet(&pkt);pkt.data = NULL;    // packet data will be allocated by the encoderpkt.size = 0;//Read raw YUV dataif (fread(pFrame->data[0],1,y_size,fp_in)<= 0||     // Yfread(pFrame->data[1],1,y_size/4,fp_in)<= 0||   // Ufread(pFrame->data[2],1,y_size/4,fp_in)<= 0){   // Vreturn -1;}else if(feof(fp_in)){break;}pFrame->pts = i;/* encode the image */ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_output);if (ret < 0) {printf("Error encoding frame\n");return -1;}if (got_output) {printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size);framecnt++;fwrite(pkt.data, 1, pkt.size, fp_out);av_free_packet(&pkt);}}//Flush Encoderfor (got_output = 1; got_output; i++) {ret = avcodec_encode_video2(pCodecCtx, &pkt, NULL, &got_output);if (ret < 0) {printf("Error encoding frame\n");return -1;}if (got_output) {printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",pkt.size);fwrite(pkt.data, 1, pkt.size, fp_out);av_free_packet(&pkt);}}fclose(fp_out);avcodec_close(pCodecCtx);av_free(pCodecCtx);av_freep(&pFrame->data[0]);av_frame_free(&pFrame);return 0;
}

注意: 
当TEST_H264设置为1的时候,编码H.264文件。 
当TEST_HEVC设置为1的时候,解码HEVC文件。

#define TEST_H264  0
#define TEST_HEVC  1  

ffmpeg实战教程(三)音频PCM采样为AAC,视频YUV编码为H264/HEVC相关推荐

  1. FFmpeg —— 12.示例程序(六):视频编码器(YUV编码为H264)

    流程 下面附一张使用FFmpeg编码视频的流程图.使用该流程,不仅可以编码H.264的视频,而且可以编码MPEG4/MPEG2/VP8等等各种FFmpeg支持的视频.图中蓝色背景的函数是实际输出数据的 ...

  2. ffmpeg实战教程(一)Mp4,mkv等格式解码为h264和yuv数据

    FFmpeg有非常强大的功能包括视频采集功能.视频格式转换.视频抓图.给视频加水印等.而网上对这些功能的使用大多是基于命令行的.这不利于我们深入学习定制化ffmpeg,今后我将写一系列的用代码实现这些 ...

  3. ffmpeg实战教程(二)用SDL播放YUV,并结合ffmpeg实现简易播放器

    ffmpeg实战教程(二)用SDL播放YUV,并结合ffmpeg实现简易播放器 https://blog.csdn.net/King1425/article/details/71171142 我们先实 ...

  4. ffmpeg实战教程(十一)手把手教你实现直播功能,不依赖第三方SDK

    直播,2016最火的技术之一了,更多的关于直播的知识:http://blog.csdn.net/king1425/article/details/72489272 -这篇我们就不依赖任何集成好的SDK ...

  5. ffmpeg实战教程(八)Android平台下AVfilter 实现水印,滤镜等特效功能

    ffmpeg实战教程(八)Android平台下AVfilter 实现水印,滤镜等特效功能 ffmpeg实战教程(七)Android CMake avi解码后SurfaceView显示 本篇我们在此基础 ...

  6. ffmpeg实战教程(七)Android CMake avi解码后SurfaceView显示

    ffmpeg实战教程(七)Android CMake avi解码后SurfaceView显示 本篇我们实现Android平台解码avi并用SurfaceView播放. 先上图看效果: 思路:  1.把 ...

  7. ffmpeg实战教程(六)Android CMake实现解码(MP4转YUV)

    ffmpeg实战教程(六)Android CMake实现解码(MP4转YUV) 我们将使用最新版: 最新版ffmpeg ffmpeg3.3  新版Android studio Android stud ...

  8. 昆仑通态人机界面与单片机通信实战教程三:脚本驱动与HDMI工程的关联

    大家好,我是『芯知识学堂』的SingleYork,前面给大家介绍了"昆仑通态人机界面与单片机通信实战教程二:脚本驱动的设计",今天笔者就要来给大家介绍"昆仑通态人机界面与 ...

  9. Android FFmpeg视频播放器三 音频封装格式解码播放

    Android FFmpeg视频播放器一解封装 Android Android FFmpeg视频播放器二 视频封装格式解码播放 视频解封装之后就会得到音频流和视频流,解封状得到的数据是AVPackag ...

最新文章

  1. python调用gitlab api自动合并分支_Python3使用 GitLab API 进行批量合并分支
  2. freemarker 学习笔记
  3. Counting Divisors HDU - 6069
  4. android API 参考大全
  5. JQuery选择器一般方法
  6. 牛客多校 - Minimum-cost Flow(最小费用最大流+贪心)
  7. 微信视频号内容营销方法论
  8. 2020年7月美妆行业抖音小红书营销报告
  9. OpenStack Keystone v3 API新特性
  10. Python 带你来一次说走就走的环球旅行
  11. 注意numpy与pandas里std中的有偏和无偏
  12. http传输字符编码与转义(深度好文)
  13. Vue第三天 v-model与Vue组件化
  14. Mybatis 参考
  15. [转]我在装sql 2000 server 时,系统老提示我的计算机有挂起的
  16. 关于主机的思维导图_思维导图可以整理哪些东西?
  17. [混迹IT职场系列]一、转正的那些事儿
  18. 怎么把cad的图导入ps_CAD图如何导入Photoshop的方法
  19. PQ分区出错,由Ghost来补救的办法(转)
  20. java机票编程_携程预订机票后台java开发编程

热门文章

  1. 【飞控理论】【惯性导航基础】二维平面的旋转如何用代数表示?三维平面的旋转如何用代数表示?什么是四元数?四元数、欧拉角、方向余弦之间有什么关系?
  2. Linux 变量和结构体
  3. ubuntu14.04+ROS indigo+kinectV1 骨骼点检测
  4. 2015大学计算机二级考试,2015年计算机二级考试模拟题(一)
  5. 最长回文子串java_5. 最长回文子串
  6. linux定时任务执行脚本文件找不到,linux中脚本放入计划任务当中为什么没有执行呢?...
  7. 死锁必要条件、解决死锁策略
  8. 【JUC】第五章 JUC 阻塞队列、线程池
  9. 死锁的充分必要条件、死锁预防、死锁避免、死锁检测和解除
  10. Android 使用URLConnection下载音频文件