#include "./rtmp_source.hpp"
#include "iostream"
using namespace std;
#define V_WIDTH 640
#define V_HEIGHT 480static AVFormatContext *open_device()
{int ret = 0;char errors[1024] = {0};// 创建输出的缓冲区AVFormatContext *fmt_ctx = NULL;AVDictionary *options = NULL;char *device_name = "/dev/video0";// 注设备信息avdevice_register_all();AVInputFormat *ifromat = av_find_input_format("video4linux2");av_dict_set(&options, "video_size", "640x480", 0);av_dict_set(&options, "framerate", "30", 0);av_dict_set(&options, "pixel_format", "nv12", 0);if ((ret = avformat_open_input(&fmt_ctx, device_name, ifromat, &options)) < 0){av_strerror(ret, errors, 1024);fprintf(stderr, "failede to open video device[%d]%s", ret, errors);return NULL;}return fmt_ctx;
}// 打开编码器
static void open_coder(int width, int height, AVCodecContext **enc_ctx)
{int ret = 0;char errors[1024];AVCodec *codec = NULL;// 找到对应的编码器codec = avcodec_find_encoder_by_name("libx264");if (!codec){fprintf(stderr, "codec libx264 not foundtion!\n");exit(-1);}// 申请一个AVcodec并且将其绑定到的AVCodecContext中*enc_ctx = avcodec_alloc_context3(codec);if (!*enc_ctx){av_strerror(ret, errors, 1024);fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);exit(-1);}// 设置sps pps 相关信息(*enc_ctx)->profile = FF_PROFILE_H264_HIGH_444;(*enc_ctx)->level = 5.0;(*enc_ctx)->width = V_WIDTH;(*enc_ctx)->height = V_HEIGHT;(*enc_ctx)->gop_size = 12;(*enc_ctx)->keyint_min = 5;(*enc_ctx)->max_b_frames = 3;(*enc_ctx)->has_b_frames = 1;// 设置参考帧的数量(*enc_ctx)->refs = 3;// 设置输入视频的格式(*enc_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;// 设置对用的码率(*enc_ctx)->bit_rate = 600000;// 设置帧率(*enc_ctx)->time_base = AVRational{1, 25};(*enc_ctx)->framerate = AVRational{25, 1};// 打开对应的编码器数量ret = avcodec_open2(*enc_ctx, codec, NULL);if (ret < 0){av_strerror(ret, errors, 1024);fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);exit(-1);}
}// 打开对应的frame
static AVFrame *creat_frame(int width, int height)
{int ret = 0;char errors[1024];AVFrame *frame = NULL;if (!frame){av_strerror(ret, errors, 1024);fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);goto _errors;}frame->width = width;frame->height = height;frame->format = AV_PIX_FMT_YUV420P;// 分配一个缓冲区ret = av_frame_get_buffer(frame, 32);if (ret < 0){av_strerror(ret, errors, 1024);fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);goto _errors;}return frame;
_errors:if (frame){av_frame_free(&frame);}return NULL;
}static void encode(AVCodecContext *enc_ctx,AVFrame *frame,AVPacket *newpacket,FILE *outfile)
{int ret = 0;char errors[1024];if (!frame){printf("send frame to encoder ,pts=%lld", (long long)frame->pts);}// 将原始数据给的编码器进行编码ret = avcodec_send_frame(enc_ctx, frame);if (ret < 0){av_strerror(ret, errors, 1024);fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);exit(-1);}// 从编码器获取编码号的输入while (ret >= 0){ret = avcodec_receive_packet(enc_ctx, newpacket);if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)){return;}else if (ret < 0){printf("Error,failed to encode!\n");exit(1);}fwrite(newpacket->data, 1, newpacket->size, outfile);av_packet_unref(newpacket);}
}static void rev_video()
{int ret = 0;int base = 0;char errors[1024];AVPacket pkt;AVFormatContext *fmt_ctx = NULL;AVCodecContext *enc_ctx = NULL;av_log_set_level(AV_LOG_ERROR);char *in_file = "/home/zhao/learn/ffmpeg_learn/source/sample_vide.yuv";char *out_file = "/home/zhao/learn/ffmpeg_learn/source/sample_vide.h264";FILE *yuv_out_file = fopen(in_file, "wb+");FILE *h264_out_file = fopen(out_file, "wb+");// 建立上下文信息fmt_ctx = open_device();//打开编码器上下文open_coder(V_WIDTH, V_HEIGHT, &enc_ctx);// 创建frame缓冲区AVFrame *frame = creat_frame(V_WIDTH, V_HEIGHT);// 创建编码后的输出的packetAVPacket *newpkt = av_packet_alloc();if (!newpkt){av_strerror(ret, errors, 1024);fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);goto _errors;}while ((ret = av_read_frame(fmt_ctx, &pkt)) == 0){int i = 0;av_log(NULL, AV_LOG_INFO, "packet size is %d\n", pkt.size);// nv12 YYYYYYYYUVVU// YUV420 YYYYYYYYUUVVmemcpy(frame->data[0], pkt.data, 307200);for (i = 0; i < 307200; i++){frame->data[1][i] = pkt.data[307200 + 2 * i];frame->data[2][i] = pkt.data[307200 + 2 * i + 1];}fwrite(frame->data[0], 1, 307200, yuv_out_file);fwrite(frame->data[1], 1, 307200 / 4, yuv_out_file);fwrite(frame->data[2], 1, 307200 / 4, yuv_out_file);frame->pts = base++;encode(enc_ctx, frame, newpkt, h264_out_file);av_packet_unref(&pkt);}encode(enc_ctx, NULL, newpkt, h264_out_file);_errors:if (yuv_out_file){fclose(yuv_out_file);}if (fmt_ctx){avformat_close_input(&fmt_ctx);}av_log(NULL, AV_LOG_INFO, "finished ");return;
}
int main(int argc, char *argv[])
{rev_video();return 0;
}

在h264流中,有两种NALU极其的重要,序列参数集(Sequence Paramater Set,SPS)和图像参数集(Picture ParamaterSet,PPS

 SPS中的信息至关重要,记录了编码的prfile、level、图像宽高等,如果其中的数据丢失或出现错误,那么解码过程很可能会失败。每一帧编码后数据所依赖的参数保存于PPS中

一般情况SPS和PPS的NAL Unit通常位于整个码流的起始位置。封装文件一般进保存一次,位于文件头部,sps/sps再整个解码过程中复用,不发生变化。然而对于实时流,通常是从流中间开始解码,因此需要在每个I帧前添加SPS和PPS;如果编码器在编码过程中改变了码流参数(如分辨率),需要重新调整SPS和PPS数据。

ffmpeg中SPSPPS数据从输入流中解析时,位于AVFormatContext->streams[video_index]->codecpar->extradata中。编码时,sps/pps数据存放于编码器上下文AVCodecContext->extradata中,但是该对象通常是空指针,还需要进行额外设置。

通常我们编码保存裸流时,仅第一个I帧前有SPS/PPS数据(见后续代码示例分析)。但是,如果需要做实时流传输,必须要在每一个I帧前添加SPS和PPS。其中SPS和PPS分别是14/5个字节

#include "./rtmp_source.hpp"
#include "iostream"
#include "signal.h"using namespace std;
#define V_WIDTH 640
#define V_HEIGHT 480
bool bRuning = true;
void sig_handlr(int sig_num)
{bRuning = false;
}
int main(int argc, char const *argv[])
{signal(SIGINT, sig_handlr);int ret = 0;char errors[1024] = {0};avdevice_register_all();AVDictionary *options = NULL;AVFormatContext *fmt_ctx = NULL;char *device_name = "/dev/video0";AVInputFormat *ifromat = av_find_input_format("video4linux2");av_dict_set(&options, "video_size", "640x480", 0);av_dict_set(&options, "framerate", "25", 0);av_dict_set(&options, "pixel_format", "nv12", 0);if ((ret = avformat_open_input(&fmt_ctx, device_name, ifromat, &options)) < 0){av_strerror(ret, errors, 1024);fprintf(stderr, "failede to open video device[%d]%s", ret, errors);return ret;}if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0){av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");return ret;}av_dump_format(fmt_ctx, 0, device_name, 0);if ((av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, NULL)) < 0){av_strerror(ret, errors, 1024);fprintf(stderr, "failede to open video device[%d]%s", ret, errors);avformat_close_input(&fmt_ctx);return ret;}int video_stream_index = ret;// 初始化解码器AVCodecParameters *codepar = fmt_ctx->streams[video_stream_index]->codecpar;AVCodec *video_codec = avcodec_find_encoder(codepar->codec_id);if (!video_codec){av_log(NULL, AV_LOG_ERROR, "Can't find decoer\n");return -1;}// 视频编码器的上下文信息AVCodecContext *video_decoder_ctx = avcodec_alloc_context3(video_codec);if (!video_decoder_ctx){av_log(NULL, AV_LOG_ERROR, "Could not allocate a decoding context\n");avformat_close_input(&fmt_ctx);return AVERROR(ENOMEM);}// 配置解码器上下文if ((ret = avcodec_parameters_to_context(video_decoder_ctx, codepar)) < 0){avformat_close_input(&fmt_ctx);avcodec_free_context(&video_decoder_ctx);return ret;}// 打开解码器if ((ret = avcodec_open2(video_decoder_ctx, video_codec, NULL)) < 0){avformat_close_input(&fmt_ctx);avcodec_free_context(&video_decoder_ctx);return ret;}// 编码器的数量AVCodec *enc = avcodec_find_encoder_by_name("libx264");AVCodecContext *enc_ctx = avcodec_alloc_context3(enc);if (!enc_ctx){av_log(NULL, AV_LOG_ERROR, "Could not allocate a decoding context\n");return AVERROR(ENOMEM);}enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;enc_ctx->width = 640;enc_ctx->height = 480;int fps = 25; // 降低便于查看enc_ctx->framerate = {fps, 1};enc_ctx->time_base = {1, fps};enc_ctx->gop_size = fps;enc_ctx->bit_rate = 2000000; // 2M// 解码并保存到文件uint32_t frameCnt = 0;AVPacket *pkt = av_packet_alloc(); // 分配一个AVPactet对象,用于管理其缓冲区AVFrame *frame = av_frame_alloc(); // 分配一个AVFrame对象,用于管理其缓冲区AVPacket *enc_pkt = av_packet_alloc();FILE *f264 = fopen("out.h264", "wb");while (bRuning){ret = av_read_frame(fmt_ctx, pkt); // 循环从输入获取一帧压缩编码数据,分配pkt缓冲区if (ret < 0){break;}// 仅处理视频码流if (pkt->stream_index != video_stream_index)continue;ret = avcodec_send_packet(video_decoder_ctx, pkt); // 送一帧到解码器while (ret >= 0){ret = avcodec_receive_frame(video_decoder_ctx, frame); // 尝试获取解码数据,分配frame缓冲区if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){break;}else if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n");goto end;}// 解码的视频数据处理printf("\rSucceed to decode frame %d\n", frameCnt++);// 编码ret = avcodec_send_frame(enc_ctx, frame); // 送一帧到解码器while (ret >= 0){ret = avcodec_receive_packet(enc_ctx, enc_pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){break;}else if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n");goto end;}// 判断当前数据包的前四个字节判断类型,if ((enc_pkt->data[4] & 0x1f) == 5){fwrite(enc_ctx->extradata, 1, enc_ctx->extradata_size, f264);}printf("\rSucceed to encode frame %d\n", frameCnt);fwrite(enc_pkt->data, 1, enc_pkt->size, f264);av_packet_unref(enc_pkt);}av_frame_unref(frame); // 释放frame缓冲区数据}av_packet_unref(pkt); // 释放pkt缓冲区数据}end:// 关闭输入avformat_close_input(&fmt_ctx);av_packet_free(&pkt);av_frame_free(&frame);av_dict_free(&options);fclose(f264);return 0;
}

ffmpeg之视频的编码手动添加SPS以及PPS相关推荐

  1. 利用VS2015与FFmpeg实现视频解编码的基本过程

    利用VS2015与FFmpeg实现视频解编码的基本过程 最近接到一个任务,是有关于视频的网络传输的技术应用,因为在传输的过程中用到了视频的编解码技术,所以近期对其进行了一系列学习.现将最近的学习成果即 ...

  2. iOS 使用FFmpeg实现视频H264编码

    本文借鉴:https://www.jianshu.com/p/70b0af4d0ec7   以及 https://www.jianshu.com/p/31d1ca4999c6 ffmpeg 相关命令行 ...

  3. ffmpeg解封装出来的h264裸流设置SPS、PPS

    注:本文转自https://blog.csdn.net/qingkongyeyue/article/details/54023323 SPS:H.264码流第一个 NALU是 SPS(序列参数集Seq ...

  4. H.264中SPS、PPS和IDR

    1.简介 H.264码流第一个 NALU是 SPS(序列参数集Sequence Parameter Set),对应H264标准文档 7.3.2.1 序列参数集的语法进行解析. H.264码流第二个 N ...

  5. H.264中的SPS和PPS

    参考使用FFMPEG类库分离出多媒体文件中的H.264码流_雷霄骅的博客-CSDN博客 H.264码流第一个 NALU是 SPS(序列参数集Sequence Parameter Set) 对应H264 ...

  6. sps和pps一篇好的解释

    H264码流中SPS PPS详解<转> 转载地址:https://zhuanlan.zhihu.com/p/27896239 1 SPS和PPS从何处而来? 2 SPS和PPS中的每个参数 ...

  7. H264码流中SPS、PPS详解

    1 SPS和PPS从何处而来? 2 SPS和PPS中的每个参数起什么作用? 3 如何解析SDP中包含的H.264的SPS和PPS串? 1 客户端抓包 在做客户端视频解码时,一般都会使用Wireshar ...

  8. 视频【编码】原理(H.264 librtmp推流),图像编码中sps ,pps ,nalu ,frame ,silce ect

    视频编码格式:H264, VC-1, MPEG-2, MPEG4-ASP (Divx/Xvid), VP8, MJPEG 等.  音频编码格式:AAC, AC3, DTS(-HD), TrueHD, ...

  9. 利用ffmpeg提供的库(API)进行音频与视频的编码并生成文件

    Output example.c 目录 [隐藏] 1 概述 2 音频输出 2.1 add_audio_stream 2.2 open_audio 2.3 get_audio_frame 2.4 wri ...

最新文章

  1. Ubuntu输入正确的用户名密码不能进入系统的原因和解决方法
  2. 4G EPS 的架构模型
  3. Oracle实战笔记(第二天)
  4. 为一路通(16tone)开博
  5. 计算机出现黑屏问题方法派出,电脑重装系统开机常见黑屏问题的解决方法
  6. 通俗理解Meanshift均值漂移算法
  7. GIS案例练习-----------第一天
  8. angularjs 资源集合
  9. 最好用图像处理库CxImage入门
  10. 因接外包坐牢456天,我都经历了什么?
  11. 如何查看本机的内网IP
  12. 我的Android进阶之旅------报 error: Apostrophe not preceded by \ 的错误解决办法
  13. ccf计算机认证考试题集,【计算机本科补全计划】CCF计算机职业资格认证 2017-03 试题初试...
  14. 我已经可以想象,疫情结束后全国男生会……
  15. android bp文件_Android 基础 | Android.bp 语法浅析
  16. 在线ARM仿真器知识(嵌入式系统设计师必备)
  17. 「小程序JAVA实战」小程序的个人信息作品,收藏,关注(66)
  18. 读保哥《ASP.NET MVC2开发实战》第二回(Model)
  19. 调音台基础使用说明-功能分布、输入输出连接、通道条使用
  20. 记录 —— lammps

热门文章

  1. 简单使用github上的节操播放器
  2. jsp观影平台/影视观看系统/视频网站
  3. 二进制反码求和java_简单又复杂的“整数类型”
  4. 安装MySQL时出现不兼容的解决办法conflicts with file from package
  5. Math.cos()
  6. 给女生说一些关于软件测试职场的一些真实的建议~
  7. 危鸡之夜服务器维护,2018热门对战竞技游戏大盘点
  8. 【Java】棋盘覆盖问题
  9. 如何将excel表格导入word_word办公技巧:如何让Excel与Word文档数据同步
  10. 云服务器怎么弄mac系统,mac如何开启云服务器配置