分析FFMPEG中H264编码流程
/*** 最简单的基于FFmpeg的视频编码器* Simplest FFmpeg Video Encoder* * 雷霄骅 Lei Xiaohua* leixiaohua1020@126.com* 中国传媒大学/数字电视技术* Communication University of China / Digital TV Technology* http://blog.csdn.net/leixiaohua1020* * 本程序实现了YUV像素数据编码为视频码流(H264,MPEG2,VP8等等)。* 是最简单的FFmpeg视频编码方面的教程。* 通过学习本例子可以了解FFmpeg的编码流程。* This software encode YUV420P data to H.264 bitstream.* It's the simplest video encoding software based on FFmpeg. * Suitable for beginner of FFmpeg */#include <stdio.h>
#include <string.h>
#define __STDC_CONSTANT_MACROS#ifdef _WIN32
//Windows
extern "C"
{
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include "libavutil/opt.h"
#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_video2 (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* video_st;AVCodecContext* pCodecCtx;AVCodec* pCodec;AVPacket pkt;uint8_t* picture_buf;AVFrame* pFrame;int picture_size;int framecnt=0;FILE *in_file = fopen("/home/file/ds_480x272.yuv", "rb"); //Input raw YUV dataint in_w=352,in_h=288; //Input data's width and heightint framenum=100; //Frames to encodechar out_file[40];if(argc < 2){printf("error,please input encode file format\n");return -1;}av_register_all();strncpy(out_file,argv[1],sizeof(argv[1]));//Method 2.avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);fmt = pFormatCtx->oformat;//Open output URLif (avio_open2(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE,NULL,NULL) < 0){printf("Failed to open output file! \n");return -1;}pCodec = avcodec_find_encoder(fmt->video_codec);if (!pCodec){printf("Can not find encoder! \n");return -1;}video_st = avformat_new_stream(pFormatCtx, pCodec);if (video_st==NULL){return -1;}video_st->time_base.num = 1; video_st->time_base.den = 30; pCodecCtx = video_st->codec;pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;pCodecCtx->width = in_w; pCodecCtx->height = in_h;pCodecCtx->time_base.num = 1; pCodecCtx->time_base.den = 30; pCodecCtx->bit_rate = 512000; pCodecCtx->gop_size= 60;pCodecCtx->qmin = 10;pCodecCtx->qmax = 51;pCodecCtx->max_b_frames= 0;// Set OptionAVDictionary *param = 0;//H.264if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {av_dict_set(¶m, "preset", "slow", 0);av_dict_set(¶m, "profile", "main", 0);}//H.265if((pCodecCtx->codec_id == AV_CODEC_ID_HEVC)||(pCodecCtx->codec_id == AV_CODEC_ID_H265)){av_dict_set(¶m, "x265-params", "crf=25", 0);av_dict_set(¶m, "preset", "fast", 0);av_dict_set(¶m, "tune", "zero-latency", 0);}if (avcodec_open2(pCodecCtx, NULL,¶m) < 0){printf("Failed to open encoder! \n");return -1;}pFrame = av_frame_alloc();picture_size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);picture_buf = (uint8_t *)av_malloc(picture_size);avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);//Write File Headeravformat_write_header(pFormatCtx,NULL);av_new_packet(&pkt,picture_size);av_dump_format(pFormatCtx, 0, out_file, 1);for (int i=0; i<framenum; i++){//Read raw YUV dataif (fread(picture_buf, 1, picture_size, in_file) <= 0){printf("Failed to read raw data! \n");return -1;}else if(feof(in_file)){break;}//PTSpFrame->pts=i;int got_picture=0;//Encodeint ret = avcodec_encode_video2(pCodecCtx, &pkt,pFrame, &got_picture);if(ret < 0){printf("Failed to encode! \n");return -1;}if (got_picture==1){printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size);framecnt++;pkt.stream_index = video_st->index;ret = av_write_frame(pFormatCtx, &pkt);av_free_packet(&pkt);}}//Flush Encoderint ret = flush_encoder(pFormatCtx,0);if (ret < 0) {printf("Flushing encoder failed\n");return -1;}//Write file trailerav_write_trailer(pFormatCtx);//Cleanif (video_st){avcodec_close(video_st->codec);av_free(pFrame);av_free(picture_buf);}//avio_close(pFormatCtx->pb);avformat_free_context(pFormatCtx);fclose(in_file);return 0;
}
以上代码是在雷神基础上做了少量改动,使用他的程序对编码流程进行分析,原文地址点击打开链接
程序刚开始先调用av_register_all,注册所有的muxer和编解码。av_register_all函数调用register_all()函数开进行注册。
void av_register_all(void)
{static AVOnce control = AV_ONCE_INIT;ff_thread_once(&control, register_all);
}
register_all()在最开始的是调用avcodec_register_all注册编解码器。注册函数根据configure自动生成的config.h中的宏,比如来注册libopenH264编码器。把所有的编解码器注册完成之后。开始注册muxer。
#define CONFIG_LIBOPENH264_ENCODER 1
#define REGISTER_ENCODER(X, x) \{ \extern AVCodec ff_##x##_encoder; \if (CONFIG_##X##_ENCODER) \avcodec_register(&ff_##x##_encoder); \}#define REGISTER_DECODER(X, x) \{ \extern AVCodec ff_##x##_decoder; \if (CONFIG_##X##_DECODER) \avcodec_register(&ff_##x##_decoder); \}
avcodec_register()把所有的编码器添加到last_avcodec链表中。
av_cold void avcodec_register(AVCodec *codec)
{AVCodec **p;avcodec_init();p = last_avcodec;codec->next = NULL;while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, codec))p = &(*p)->next;last_avcodec = &codec->next;if (codec->init_static_data)codec->init_static_data(codec);
}
注册muxer和注册编解码器差不多,也是通过config.h中的宏选择是否注册。然后调用av_register_output_format()函数把所有的muxer.
#define CONFIG_H264_MUXER 1
#define REGISTER_MUXER(X, x) \{ \extern AVOutputFormat ff_##x##_muxer; \if (CONFIG_##X##_MUXER) \av_register_output_format(&ff_##x##_muxer); \}
static void register_all(void)
{avcodec_register_all();/* (de)muxers */REGISTER_MUXER (A64, a64);REGISTER_DEMUXER (AA, aa);
...
}
AVFormatContext *avformat_alloc_context(void)
{AVFormatContext *ic;ic = av_malloc(sizeof(AVFormatContext));if (!ic) return ic;avformat_get_context_defaults(ic);ic->internal = av_mallocz(sizeof(*ic->internal));if (!ic->internal) {avformat_free_context(ic);return NULL;}ic->internal->offset = AV_NOPTS_VALUE;ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;ic->internal->shortest_end = AV_NOPTS_VALUE;return ic;
}
static void avformat_get_context_defaults(AVFormatContext *s)
{memset(s, 0, sizeof(AVFormatContext));s->av_class = &av_format_context_class;s->io_open = io_open_default;s->io_close = io_close_default;av_opt_set_defaults(s);
}
初始化之后,在没有指定输出格式时,会根据你输入的文件名或者文件后缀名在已经注册的muxer中去选择最合适的输出格式。遍历玩链表完后,选择匹配系数最高的muxer。并且赋值给avctx中的oformat。
int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat,const char *format, const char *filename)
{AVFormatContext *s = avformat_alloc_context();int ret = 0;*avctx = NULL;if (!s)goto nomem;if (!oformat) {if (format) {oformat = av_guess_format(format, NULL, NULL);if (!oformat) {av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format);ret = AVERROR(EINVAL);goto error;}} else {oformat = av_guess_format(NULL, filename, NULL);if (!oformat) {ret = AVERROR(EINVAL);av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n",filename);goto error;}}}s->oformat = oformat;if (s->oformat->priv_data_size > 0) {s->priv_data = av_mallocz(s->oformat->priv_data_size);if (!s->priv_data)goto nomem;if (s->oformat->priv_class) {*(const AVClass**)s->priv_data= s->oformat->priv_class;av_opt_set_defaults(s->priv_data);}} elses->priv_data = NULL;if (filename)av_strlcpy(s->filename, filename, sizeof(s->filename));*avctx = s;return 0;
nomem:av_log(s, AV_LOG_ERROR, "Out of memory\n");ret = AVERROR(ENOMEM);
error:avformat_free_context(s);return ret;
}
然后是调用avio_open2,根据你输入的文件名,选择一个合适的协议。
int avio_open2(AVIOContext **s, const char *filename, int flags,const AVIOInterruptCB *int_cb, AVDictionary **options)
{return ffio_open_whitelist(s, filename, flags, int_cb, options, NULL, NULL);
}
ffurl_alloc会根据你输出的文件名在protocol_list中选择使用哪种协议。protocol_list中的协议数目是可以在configure配置,通过以下的选项进行使能和关闭。必要时可以通过关闭不使用的协议来达到精简库大小的目的。
--enable-protocol=NAME enable protocol NAME
--disable-protocol=NAME disable protocol NAME
int ffurl_alloc(URLContext **puc, const char *filename, int flags,const AVIOInterruptCB *int_cb)
{const URLProtocol *p = NULL;p = url_find_protocol(filename);if (p)return url_alloc_for_protocol(puc, p, filename, flags, int_cb);*puc = NULL;if (av_strstart(filename, "https:", NULL))av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with ""openssl, gnutls ""or securetransport enabled.\n");return AVERROR_PROTOCOL_NOT_FOUND;
}
创建一路新的stream,并且根据AVCodec对码流中的codec进行初始化。如果是第一次调用,nb_streams为0,每次调用,都会在基础上累加,最大不超过s->max_streams,max_streams默认是1000。
AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
{AVStream *st;int i;AVStream **streams;if (s->nb_streams >= FFMIN(s->max_streams, INT_MAX/sizeof(*streams))) {if (s->max_streams < INT_MAX/sizeof(*streams))av_log(s, AV_LOG_ERROR, "Number of streams exceeds max_streams parameter (%d), see the documentation if you wish to increase it\n", s->max_streams);return NULL;}streams = av_realloc_array(s->streams, s->nb_streams + 1, sizeof(*streams));if (!streams)return NULL;s->streams = streams;st = av_mallocz(sizeof(AVStream));if (!st)return NULL;if (!(st->info = av_mallocz(sizeof(*st->info)))) {av_free(st);return NULL;}st->info->last_dts = AV_NOPTS_VALUE;#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGSst->codec = avcodec_alloc_context3(c);if (!st->codec) {av_free(st->info);av_free(st);return NULL;}
FF_ENABLE_DEPRECATION_WARNINGS
#endifst->internal = av_mallocz(sizeof(*st->internal));if (!st->internal)goto fail;st->codecpar = avcodec_parameters_alloc();if (!st->codecpar)goto fail;st->internal->avctx = avcodec_alloc_context3(NULL);if (!st->internal->avctx)goto fail;if (s->iformat) {
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS/* no default bitrate if decoding */st->codec->bit_rate = 0;
FF_ENABLE_DEPRECATION_WARNINGS
#endif/* default pts setting is MPEG-like */avpriv_set_pts_info(st, 33, 1, 90000);/* we set the current DTS to 0 so that formats without any timestamps* but durations get some timestamps, formats with some unknown* timestamps have their first few packets buffered and the* timestamps corrected before they are returned to the user */st->cur_dts = RELATIVE_TS_BASE;} else {st->cur_dts = AV_NOPTS_VALUE;}st->index = s->nb_streams;st->start_time = AV_NOPTS_VALUE;st->duration = AV_NOPTS_VALUE;st->first_dts = AV_NOPTS_VALUE;st->probe_packets = MAX_PROBE_PACKETS;st->pts_wrap_reference = AV_NOPTS_VALUE;st->pts_wrap_behavior = AV_PTS_WRAP_IGNORE;st->last_IP_pts = AV_NOPTS_VALUE;st->last_dts_for_order_check = AV_NOPTS_VALUE;for (i = 0; i < MAX_REORDER_DELAY + 1; i++)st->pts_buffer[i] = AV_NOPTS_VALUE;st->sample_aspect_ratio = (AVRational) { 0, 1 };#if FF_API_R_FRAME_RATEst->info->last_dts = AV_NOPTS_VALUE;
#endifst->info->fps_first_dts = AV_NOPTS_VALUE;st->info->fps_last_dts = AV_NOPTS_VALUE;st->inject_global_side_data = s->internal->inject_global_side_data;st->internal->need_context_update = 1;s->streams[s->nb_streams++] = st;return st;
fail:free_stream(&st);return NULL;
}
初始化编码器,在初始化之前,可以通过av_dict_set(¶m, "profile", "main", 0);设置一些编码器参数,比如bitrate。
// Set Option
AVDictionary *param = 0;
//H.264
if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {av_dict_set(¶m, "preset", "slow", 0);av_dict_set(¶m, "profile", "main", 0);
}
if (avcodec_open2(pCodecCtx, NULL,¶m) < 0){printf("Failed to open encoder! \n");return -1;
}
获取一帧图片的需要多少内存
int avpicture_get_size(enum AVPixelFormat pix_fmt, int width, int height)
{return av_image_get_buffer_size(pix_fmt, width, height, 1);
}
把pFrame里面的data绑定给picture_buf,根据pix_fmt,width,height来分配y,u,v各个通道的地址和大小
avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],const uint8_t *src, enum AVPixelFormat pix_fmt,int width, int height, int align)
{int ret, i;ret = av_image_check_size(width, height, 0, NULL);if (ret < 0)return ret;ret = av_image_fill_linesizes(dst_linesize, pix_fmt, width);if (ret < 0)return ret;for (i = 0; i < 4; i++)dst_linesize[i] = FFALIGN(dst_linesize[i], align);return av_image_fill_pointers(dst_data, pix_fmt, height, (uint8_t *)src, dst_linesize);
}
根据输入的pix_fmt来填充yuv每个分量的行长(跨距)
int av_image_fill_linesizes(int linesizes[4], enum AVPixelFormat pix_fmt, int width)
{int i, ret;const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);int max_step [4]; /* max pixel step for each plane */int max_step_comp[4]; /* the component for each plane which has the max pixel step */memset(linesizes, 0, 4*sizeof(linesizes[0]));if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)return AVERROR(EINVAL);av_image_fill_max_pixsteps(max_step, max_step_comp, desc);for (i = 0; i < 4; i++) {if ((ret = image_get_linesize(width, i, max_step[i], max_step_comp[i], desc)) < 0)return ret;linesizes[i] = ret;}return 0;
}
根据height和linesize来计算以及pix_fmt来计算每个元素的大小,并把data指针指向ptr,返回一帧输入图片的总大小
int av_image_fill_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt, int height,uint8_t *ptr, const int linesizes[4])
{int i, total_size, size[4] = { 0 }, has_plane[4] = { 0 };const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);memset(data , 0, sizeof(data[0])*4);if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)return AVERROR(EINVAL);data[0] = ptr;if (linesizes[0] > (INT_MAX - 1024) / height)return AVERROR(EINVAL);size[0] = linesizes[0] * height;if (desc->flags & AV_PIX_FMT_FLAG_PAL ||desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) {data[1] = ptr + size[0]; /* palette is stored here as 256 32 bits words */return size[0] + 256 * 4;}for (i = 0; i < 4; i++)has_plane[desc->comp[i].plane] = 1;total_size = size[0];for (i = 1; i < 4 && has_plane[i]; i++) {int h, s = (i == 1 || i == 2) ? desc->log2_chroma_h : 0;data[i] = data[i-1] + size[i-1];h = (height + (1 << s) - 1) >> s;if (linesizes[i] > INT_MAX / h)return AVERROR(EINVAL);size[i] = h * linesizes[i];if (total_size > INT_MAX - size[i])return AVERROR(EINVAL);total_size += size[i];}return total_size;
}
avformat_write_header函数初始化了muxer。在初始化muxer的时候,因为H264支持重排,所以它的pts和dts可以不一样。然后调用了write_header_internal函数,这个函数会去检验创建了几条码流。如果是保存到文件,只能允许创建一路码流。
int avformat_write_header(AVFormatContext *s, AVDictionary **options)
{int ret = 0;int already_initialized = s->internal->initialized;int streams_already_initialized = s->internal->streams_initialized;if (!already_initialized)if ((ret = avformat_init_output(s, options)) < 0)return ret;if (!(s->oformat->check_bitstream && s->flags & AVFMT_FLAG_AUTO_BSF)) {ret = write_header_internal(s);if (ret < 0)goto fail;}if (!s->internal->streams_initialized) {if ((ret = init_pts(s)) < 0)goto fail;if (s->avoid_negative_ts < 0) {av_assert2(s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO);if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) {s->avoid_negative_ts = 0;} elses->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE;}}return streams_already_initialized;fail:if (s->oformat->deinit)s->oformat->deinit(s);return ret;
}
创建一个picture_size大小的包
av_new_packet(&pkt,picture_size);
因为H264muxer的flags只有AVFMT_NOTIMESTAMPS,所以是调用write_packet(s, pkt);去把码流写到文件中,虽然最后write_packet也是调用muxer里write_packet
int av_write_frame(AVFormatContext *s, AVPacket *pkt)
{int ret;ret = prepare_input_packet(s, pkt);if (ret < 0)return ret;if (!pkt) {if (s->oformat->flags & AVFMT_ALLOW_FLUSH) {if (!s->internal->header_written) {ret = s->internal->write_header_ret ? s->internal->write_header_ret : write_header_internal(s);if (ret < 0)return ret;}ret = s->oformat->write_packet(s, NULL);flush_if_needed(s);if (ret >= 0 && s->pb && s->pb->error < 0)ret = s->pb->error;return ret;}return 1;}ret = do_packet_auto_bsf(s, pkt);if (ret <= 0)return ret;#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTXret = compute_muxer_pkt_fields(s, s->streams[pkt->stream_index], pkt);if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))return ret;
#endifret = write_packet(s, pkt);if (ret >= 0 && s->pb && s->pb->error < 0)ret = s->pb->error;if (ret >= 0)s->streams[pkt->stream_index]->nb_frames++;return ret;
}
int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt)
{avio_write(s->pb, pkt->data, pkt->size);return 0;
}
h264的write_packet函数可以选择直接写到文件也可以先写到数组里,等数组满了再写到文件。如果想要直接写到文件,在avio_open2函数里flag参数要加上AVIO_FLAG_DIRECT
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
{if (s->direct && !s->update_checksum) {avio_flush(s);writeout(s, buf, size);return;}while (size > 0) {int len = FFMIN(s->buf_end - s->buf_ptr, size);memcpy(s->buf_ptr, buf, len);s->buf_ptr += len;if (s->buf_ptr >= s->buf_end)flush_buffer(s);buf += len;size -= len;}
}
最后是flush_encode,这个不是必须的。比如libopenh264的capabilities没有CODEC_CAP_DELAY,所以不需要flush_encode
AVCodec ff_libopenh264_encoder = {.name = "libopenh264",.long_name = NULL_IF_CONFIG_SMALL("OpenH264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),.type = AVMEDIA_TYPE_VIDEO,.id = AV_CODEC_ID_H264,.priv_data_size = sizeof(SVCContext),.init = svc_encode_init,.encode2 = svc_encode_frame,.close = svc_encode_close,.capabilities = AV_CODEC_CAP_AUTO_THREADS,.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P,AV_PIX_FMT_NONE },.priv_class = &class,
};
分析FFMPEG中H264编码流程相关推荐
- 【Codecs系列】HEVC-SCC(五):HM+SCM中IBC编码流程和实现分析
Date:2022.3.31 文章目录 1.参考 2.函数调用流程 3.关键函数分析 4.SCC编码流程 4.1.PU模式选取过程 4.2.基于hash搜索的IBC模式选取 HEVC-SCC扩展系列文 ...
- ffmpeg设置h264编码IDR间隔
在ffmpeg中,编码h264时,想强制刷出一个I帧,比较容易,但是想强制刷出IDR帧,却没有明确的说明. 而在x264的头文件中,可以看到一个参数说明: int i_keyint_max; /* F ...
- ffmpeg进行h264编码
首先需要穿件编码的上下文以及指定编码器代码如下 //视频编码上下文 - (AVCodecContext *)get_avcodecContext_fv{AVCodecContext *c_ctx = ...
- 带你从源码角度分析ViewGroup中事件分发流程
序言 这篇博文不是对事件分发机制全面的介绍,只是从源码的角度分析ACTION_DOWN.ACTION_MOVE.ACTION_UP事件在ViewGroup中的分发逻辑,了解各个事件在ViewGroup ...
- ffmpeg的H264编码 视频逐渐模糊
万能的互联网,又一次在临危之时解决了我的问题 最近在看ffmpeg里的H264编码,然后试着去做了一下.但是目前发现一个问题,就是编码写入的.264文件,用播放器(VLC,暴风都试过)播放后,发现编码 ...
- Hisi3516交叉编译ffmpeg支持h264编码
前言 Hi3516是海思半导体针对高清IPCamera产品应用开发的一款专业高端SOC芯片,具有1080P@30fps H264多码流编码性能.而ffmpeg是众多播放器的底层解.编码库,x264是一 ...
- h264编码流程分析
H.264/AVC作为新一代视频编解码标准,提供了以往视频算法没有的新特性.比如多帧参考,以更灵活的方式使用更多的重建帧作为参考帧.在某些情况下,可以使用最多32个参考帧(在以往标准中,P帧的参考帧数 ...
- 使用FFMpeg进行H264编码
使用FFMpeg可以很方便的对音视频进行编码,并且写文件. 下面的代码是将5幅1280*720大小的图片进行编码,并且写到文件中. 代码有些乱,但希望能抛砖引玉,对学习这方面的朋友有帮助. [cpp] ...
- 【开源项目】使用FFMPEG解析H264编码为YUV格式
头文件 #pragma once#ifndef _VIDEO_DECODING_HEADER_ #define _VIDEO_DECODING_HEADER_#define INBUF_SIZE 40 ...
- 流媒体开发中H264编码NALU结构介绍与I帧判断方法
背景分析 随着互联网基础设施建设的发展,4G/5G/NB-IoT各种网络技术的大规模商用,视频随时随地可看.可控的诉求越来越多,互联网思维.架构和技术引入进传统监控行业里,成为新形势下全终端监控的基础 ...
最新文章
- (转载)linux下输入输出重定向和管道符
- phpstudy mysql创建表_MySQL_Mysql入门基础 数据库创建篇,1.创建数据表---基础(高手跳 - phpStudy...
- ie8浏览器自定义工具栏设置教程
- 动态规划 - 最长递增子序列LIS
- Redis基础数据结构
- (组合数学笔记)Pólya计数理论_Part.9_Pólya定理的推广——De Bruijn定理
- ZigBee TI ZStack CC2530 8.4 如何用高版本IAR打开低版本协议栈
- 古代的酒到底多少度,为何古人动不动喝好几坛都不会醉呢?
- paip.c++ 开发 api 手册文档总结
- QGraphicsObject Error: Class declarations lacks Q_OBJECT macro.
- 思科笔记-Four,ospf协议实现全网互通,spf算法,简短总结篇
- centos 测试get请求_centos网站服务器简单性能测试命令
- 《魔鬼搭讪学》《魔鬼约会学》读后感
- 成功路上并不拥挤 坚持就是胜利
- php面向对象开源_回到学校:5个面向学生和老师的开源程序
- highlight代码高亮
- cmd chcp命令切换字符格式
- Android新技术——探秘微信小程序
- 使用 Clipper 库的一些问题记录
- 亿赛通文档安全云服务正式启动