/*** 最简单的基于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(&param, "preset", "slow", 0);av_dict_set(&param, "profile", "main", 0);}//H.265if((pCodecCtx->codec_id == AV_CODEC_ID_HEVC)||(pCodecCtx->codec_id == AV_CODEC_ID_H265)){av_dict_set(&param, "x265-params", "crf=25", 0);av_dict_set(&param, "preset", "fast", 0);av_dict_set(&param, "tune", "zero-latency", 0);}if (avcodec_open2(pCodecCtx, NULL,&param) < 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);
...
}
avformat_alloc_output_context2这个函数先申请内存,并根据av_format_context_class这里面的avformat_options对ic进行初始化
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(&param, "profile", "main", 0);设置一些编码器参数,比如bitrate。

// Set Option
AVDictionary *param = 0;
//H.264
if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {av_dict_set(&param, "preset", "slow", 0);av_dict_set(&param, "profile", "main", 0);
}
if (avcodec_open2(pCodecCtx, NULL,&param) < 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编码流程相关推荐

  1. 【Codecs系列】HEVC-SCC(五):HM+SCM中IBC编码流程和实现分析

    Date:2022.3.31 文章目录 1.参考 2.函数调用流程 3.关键函数分析 4.SCC编码流程 4.1.PU模式选取过程 4.2.基于hash搜索的IBC模式选取 HEVC-SCC扩展系列文 ...

  2. ffmpeg设置h264编码IDR间隔

    在ffmpeg中,编码h264时,想强制刷出一个I帧,比较容易,但是想强制刷出IDR帧,却没有明确的说明. 而在x264的头文件中,可以看到一个参数说明: int i_keyint_max; /* F ...

  3. ffmpeg进行h264编码

    首先需要穿件编码的上下文以及指定编码器代码如下 //视频编码上下文 - (AVCodecContext *)get_avcodecContext_fv{AVCodecContext *c_ctx = ...

  4. 带你从源码角度分析ViewGroup中事件分发流程

    序言 这篇博文不是对事件分发机制全面的介绍,只是从源码的角度分析ACTION_DOWN.ACTION_MOVE.ACTION_UP事件在ViewGroup中的分发逻辑,了解各个事件在ViewGroup ...

  5. ffmpeg的H264编码 视频逐渐模糊

    万能的互联网,又一次在临危之时解决了我的问题 最近在看ffmpeg里的H264编码,然后试着去做了一下.但是目前发现一个问题,就是编码写入的.264文件,用播放器(VLC,暴风都试过)播放后,发现编码 ...

  6. Hisi3516交叉编译ffmpeg支持h264编码

    前言 Hi3516是海思半导体针对高清IPCamera产品应用开发的一款专业高端SOC芯片,具有1080P@30fps H264多码流编码性能.而ffmpeg是众多播放器的底层解.编码库,x264是一 ...

  7. h264编码流程分析

    H.264/AVC作为新一代视频编解码标准,提供了以往视频算法没有的新特性.比如多帧参考,以更灵活的方式使用更多的重建帧作为参考帧.在某些情况下,可以使用最多32个参考帧(在以往标准中,P帧的参考帧数 ...

  8. 使用FFMpeg进行H264编码

    使用FFMpeg可以很方便的对音视频进行编码,并且写文件. 下面的代码是将5幅1280*720大小的图片进行编码,并且写到文件中. 代码有些乱,但希望能抛砖引玉,对学习这方面的朋友有帮助. [cpp] ...

  9. 【开源项目】使用FFMPEG解析H264编码为YUV格式

    头文件 #pragma once#ifndef _VIDEO_DECODING_HEADER_ #define _VIDEO_DECODING_HEADER_#define INBUF_SIZE 40 ...

  10. 流媒体开发中H264编码NALU结构介绍与I帧判断方法

    背景分析 随着互联网基础设施建设的发展,4G/5G/NB-IoT各种网络技术的大规模商用,视频随时随地可看.可控的诉求越来越多,互联网思维.架构和技术引入进传统监控行业里,成为新形势下全终端监控的基础 ...

最新文章

  1. (转载)linux下输入输出重定向和管道符
  2. phpstudy mysql创建表_MySQL_Mysql入门基础 数据库创建篇,1.创建数据表---基础(高手跳 - phpStudy...
  3. ie8浏览器自定义工具栏设置教程
  4. 动态规划 - 最长递增子序列LIS
  5. Redis基础数据结构
  6. (组合数学笔记)Pólya计数理论_Part.9_Pólya定理的推广——De Bruijn定理
  7. ZigBee TI ZStack CC2530 8.4 如何用高版本IAR打开低版本协议栈
  8. 古代的酒到底多少度,为何古人动不动喝好几坛都不会醉呢?
  9. paip.c++ 开发 api 手册文档总结
  10. QGraphicsObject Error: Class declarations lacks Q_OBJECT macro.
  11. 思科笔记-Four,ospf协议实现全网互通,spf算法,简短总结篇
  12. centos 测试get请求_centos网站服务器简单性能测试命令
  13. 《魔鬼搭讪学》《魔鬼约会学》读后感
  14. 成功路上并不拥挤 坚持就是胜利
  15. php面向对象开源_回到学校:5个面向学生和老师的开源程序
  16. highlight代码高亮
  17. cmd chcp命令切换字符格式
  18. Android新技术——探秘微信小程序
  19. 使用 Clipper 库的一些问题记录
  20. 亿赛通文档安全云服务正式启动

热门文章

  1. Vue----组件注册
  2. 复位电路加二极管的作用
  3. wordpress 漂亮的Cosy主题
  4. iOS 1 到 iOS 10 ,我都快老了
  5. Java 社区平台 - Sym 1.7.0 发布
  6. 郭霖老师的组合模式讲解
  7. 成品系统搭建 一周就可以上线运营
  8. 教你如何进行嵌入式网络模块的联网操作
  9. stripe 海外支付
  10. UE4 坐标系坐标轴旋转轴