KCF算法 c语言实现 pdf可以通过FPGA实现吗

GPUImage-3-2、对视频编码 - 简书
GPUImage-3-2、对视频编码
在上一节中,我们使用GPUImage录制美颜视频,但是录制完成后,1、视频的体积很大,不利于传输;2、并且当我们点击第二次录制的时候,会抛出异常。 对于问题2,我们可以采用单独写一个vc来处理视频录制功能,就可以解决。
1、为什么要对视频进行压缩编码?
要解释这个问题,我们首先要明白,视频是由一帧一帧的画面组成,当视频中1秒能播放16帧的画面时候,人眼就不会感觉到卡顿(一般影院中的电影是1秒30帧),假设我们不对视频进行压缩编码,举例来说,播放一个1280720分辨率的视频,1s的内容需要占据的存储空间是:12807203060 = 1620M,所以不经过编码的视频根本没有办法保存,更何况是网络传输。
2、为什么视频可以压缩编码
视频中存在许多冗余的信息,比如说:空间冗余、时间冗余、时间冗余
3、视频编码遵守的标准
如果每个人按照自己的方式去编码,那么当我们需要还原原始数据时,很难知道对方是如何编码的,所以视频编码肯定有套大家都遵守的标准
现在H264标准是被大多数人遵守的标准之一,h264是由ITU(国际电传视讯联盟)主导制定的一套标准,它以高压缩高质量和支持多种网络的流媒体传输著称。除了H264标准外,H265、VP8、VP9也是比较热门的。
H264中定义三种帧:
I帧:完整编码的帧叫I帧
P帧:参考之前的I帧生成的只包含差异部分编码的帧叫P帧
B帧:参考前后的帧编码的帧叫B帧
H264采用的核心算法是帧内压缩和帧间压缩
H264的压缩方法:
分组:把几帧图像分为一组(GOP)也称一个序列,为防止运动变化,帧数不宜取多
定义帧:将每组内各帧图像定位为三种类型,即I帧、B帧、P帧
预测帧:以I帧作为基础,以I帧预测P帧,再由I帧和P帧预测B帧
数据传输:最后将I帧数据与预测的差值进行存储和传输
序列(GOP)group of picture
在H264中图像以序列为单位进行组织,一个序列是一段图像编码后的数据流。
一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。
H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列
这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。
一个序列就是一段内容差异不太大的图像编码后生成的一串数据流
当运动变化比较少时,一个序列可以很长,因为运动变化少就代表图像画面的内容变动很小,所以就可以编一个I帧,然后一直P帧、B帧了。
硬编码:使用非CPU进行编码,如显卡GPU、专用的DSP、FPGA、ASIC芯片等;在iOS8之后,苹果开放了接口,并且封装了VideoToolBox&AudioToolbox两个框架,分别用于对视频&音频进行硬编码
软编码: 使用CPU进行编码,软编码通常使用:ffmpeg+x264
首先是对视频进行硬编码
使用的系统的VideoToolBox
LYVideoEncoder.h
视频处理-硬编码
Created by liyang on 16/12/29.
Copyright (C) 2016年 liyang. All rights reserved.
#import &UIKit/UIKit.h&
#import &VideoToolbox/VideoToolbox.h&
@interface LYVideoEncoder : NSObject
- (void)encodeSampleBuffer:(CMSampleBufferRef)sampleB
- (void)endE
LYVideoEncoder.m
视频处理-硬编码
Created by liyang on 16/12/29.
Copyright (C) 2016年 liyang. All rights reserved.
#import "LYVideoEncoder.h"
@interface LYVideoEncoder ()
/** 记录当前的帧数 */
@property (nonatomic, assign) NSInteger frameID;
/** 编码会话 */
@property (nonatomic, assign) VTCompressionSessionRef compressionS
/** 文件写入对象 */
@property (nonatomic, strong) NSFileHandle *fileH
@implementation LYVideoEncoder
- (instancetype)init {
if (self = [super init]) {
// 1.初始化写入文件的对象(NSFileHandle用于写入二进制文件)
[self setupFileHandle];
// 2.初始化压缩编码的会话
[self setupVideoSession];
- (void)setupFileHandle {
// 1.获取沙盒路径
NSString *file = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"liyang_demo.h264"];
// 2.如果原来有文件,则删除
[[NSFileManager defaultManager] removeItemAtPath:file error:nil];
[[NSFileManager defaultManager] createFileAtPath:file contents:nil attributes:nil];
// 3.创建对象
self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:file];
- (void)setupVideoSession {
// 1.用于记录当前是第几帧数据(画面帧数非常多)
self.frameID = 0;
// 2.录制视频的宽度&高度
int width = [UIScreen mainScreen].bounds.size.
int height = [UIScreen mainScreen].bounds.size.
// 3.创建CompressionSession对象,该对象用于对画面进行编码
// kCMVideoCodecType_H264 : 表示使用h.264进行编码
// didCompressH264 : 当一次编码结束会在该函数进行回调,可以在该函数中将数据,写入文件中
VTCompressionSessionCreate(NULL, width, height, kCMVideoCodecType_H264, NULL, NULL, NULL, didCompressH264, (__bridge void *)(self),
&_compressionSession);
// 4.设置实时编码输出(直播必然是实时输出,否则会有延迟)
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue);
// 5.设置期望帧率(每秒多少帧,如果帧率过低,会造成画面卡顿)
int fps = 30;
CFNumberRef
fpsRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &fps);
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_ExpectedFrameRate, fpsRef);
// 6.设置码率(码率: 编码效率, 码率越高,则画面越清晰, 如果码率较低会引起马赛克 --& 码率高有利于还原原始画面,但是也不利于传输)
int bitRate = 800*1024;
CFNumberRef bitRateRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bitRate);
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_AverageBitRate, bitRateRef);
NSArray *limit = @[@(bitRate * 1.5/8), @(1)];
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_DataRateLimits, (__bridge CFArrayRef)limit);
// 7.设置关键帧(GOPsize)间隔
int frameInterval = 30;
CFNumberRef
frameIntervalRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &frameInterval);
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_MaxKeyFrameInterval, frameIntervalRef);
// 8.基本设置结束, 准备进行编码
VTCompressionSessionPrepareToEncodeFrames(self.compressionSession);
// 编码完成回调
void didCompressH264(void *outputCallbackRefCon, void *sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer) {
// 1.判断状态是否等于没有错误
if (status != noErr) {
// 2.根据传入的参数获取对象
LYVideoEncoder* encoder = (__bridge LYVideoEncoder*)outputCallbackRefC
// 3.判断是否是关键帧
bool isKeyframe = !CFDictionaryContainsKey( (CFArrayGetValueAtIndex(CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true), 0)), kCMSampleAttachmentKey_NotSync);
// 判断当前帧是否为关键帧
// 获取sps & pps数据
if (isKeyframe)
// 获取编码后的信息(存储于CMFormatDescriptionRef中)
CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer);
// 获取SPS信息
size_t sparameterSetSize, sparameterSetC
const uint8_t *sparameterS
CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sparameterSet, &sparameterSetSize, &sparameterSetCount, 0 );
// 获取PPS信息
size_t pparameterSetSize, pparameterSetC
const uint8_t *pparameterS
CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0 );
// 装sps/pps转成NSData,以方便写入文件
NSData *sps = [NSData dataWithBytes:sparameterSet length:sparameterSetSize];
NSData *pps = [NSData dataWithBytes:pparameterSet length:pparameterSetSize];
// 写入文件
[encoder gotSpsPps:sps pps:pps];
// 获取数据块
CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
size_t length, totalL
char *dataP
OSStatus statusCodeRet = CMBlockBufferGetDataPointer(dataBuffer, 0, &length, &totalLength, &dataPointer);
if (statusCodeRet == noErr) {
size_t bufferOffset = 0;
static const int AVCCHeaderLength = 4; // 返回的nalu数据前四个字节不是0001的startcode,而是大端模式的帧长度length
// 循环获取nalu数据
while (bufferOffset & totalLength - AVCCHeaderLength) {
uint32_t NALUnitLength = 0;
// Read the NAL unit length
memcpy(&NALUnitLength, dataPointer + bufferOffset, AVCCHeaderLength);
// 从大端转系统端
NALUnitLength = CFSwapInt32BigToHost(NALUnitLength);
NSData* data = [[NSData alloc] initWithBytes:(dataPointer + bufferOffset + AVCCHeaderLength) length:NALUnitLength];
[encoder gotEncodedData:data isKeyFrame:isKeyframe];
// 移动到写一个块,转成NALU单元
// Move to the next NAL unit in the block buffer
bufferOffset += AVCCHeaderLength + NALUnitL
- (void)gotSpsPps:(NSData*)sps pps:(NSData*)pps
// 1.拼接NALU的header
const char bytes[] = "\x00\x00\x00\x01";
size_t length = (sizeof bytes) - 1;
NSData *ByteHeader = [NSData dataWithBytes:bytes length:length];
// 2.将NALU的头&NALU的体写入文件
[self.fileHandle writeData:ByteHeader];
[self.fileHandle writeData:sps];
[self.fileHandle writeData:ByteHeader];
[self.fileHandle writeData:pps];
- (void)gotEncodedData:(NSData*)data isKeyFrame:(BOOL)isKeyFrame
NSLog(@"gotEncodedData %d", (int)[data length]);
if (self.fileHandle != NULL)
const char bytes[] = "\x00\x00\x00\x01";
size_t length = (sizeof bytes) - 1; //string literals have implicit trailing '\0'
NSData *ByteHeader = [NSData dataWithBytes:bytes length:length];
[self.fileHandle writeData:ByteHeader];
[self.fileHandle writeData:data];
- (void)encodeSampleBuffer:(CMSampleBufferRef)sampleBuffer {
// 1.将sampleBuffer转成imageBuffer
CVImageBufferRef imageBuffer = (CVImageBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
// 2.根据当前的帧数,创建CMTime的时间
CMTime presentationTimeStamp = CMTimeMake(self.frameID++, 1000);
VTEncodeInfoF
// 3.开始编码该帧数据
OSStatus statusCode = VTCompressionSessionEncodeFrame(self.compressionSession,
imageBuffer,
presentationTimeStamp,
kCMTimeInvalid,
NULL, (__bridge void * _Nullable)(self), &flags);
if (statusCode == noErr) {
NSLog(@"H264: VTCompressionSessionEncodeFrame Success");
- (void)endEncode {
VTCompressionSessionCompleteFrames(self.compressionSession, kCMTimeInvalid);
VTCompressionSessionInvalidate(self.compressionSession);
CFRelease(self.compressionSession);
self.compressionSession = NULL;
对视频进行软编码
使用的ffmpeg和x264
1、编译FFmpeg所需要的脚本文件gas-preprocessor.pl
首先下载编译FFmpeg所需要的脚本文件gas-preprocessor.pl
复制gas-preprocessor.pl文件到 /usr/local/bin 目录下
修改文件权限:chmod 777 /usr/local/bin/gas-preprocessor.pl
cp 文件绝对路径 空格 目标目录下
cp /Users/liyang/Downloads/gas-preprocessor-master/gas-preprocessor.pl /usr/local/bin
chmod 777 /usr/local/bin/gas-preprocessor.pl
Paste_Image.png
2、编译FFmpeg脚本
解压,执行脚本命名
./build-ffmpeg.sh
./build-ffmpeg.sh arm64
./build-ffmpeg.sh armv7 x86_64
./build-ffmpeg.sh lipo
可能需要消耗点时间,完成后这样的,红色框中的文件就是我们需要的
Paste_Image.png
3、编译X264
,x264官网 下载x264源码,将其文件夹名称改为x264
下载gas-preprocessor(FFmpeg编译时已经下载过)
下载build-x264.sh 将文件build-x264.sh放在x264同一级目录里面,注意不是放在x264文件夹里面。
修改权限/执行脚本
sudo chmod u+x build-x264.sh
sudo ./build-x264.sh
4、iOS项目中集成FFmpeg
libiconv.dylib/libz.dylib/libbz2.dylib/CoreMedia.framework/AVFoundation.framework
X264Manager.h
02-Encode(FFmpeg+x264)
Created by liyang on 16/12/15.
Copyright (C) 2016年 liyang. All rights reserved.
#import &Foundation/Foundation.h&
#import &CoreMedia/CoreMedia.h&
@interface X264Manager : NSObject
* 设置编码后文件的保存路径
- (void)setFileSavedPath:(NSString *)
* 设置X264
* 0: 成功; -1: 失败
* width: 视频宽度
* height: 视频高度
* bitrate: 视频码率,码率直接影响编码后视频画面的清晰度, 越大越清晰,但是为了便于保证编码后的数据量不至于过大,以及适应网络带宽传输,就需要合适的选择该值
- (int)setX264ResourceWithVideoWidth:(int)width height:(int)height bitrate:(int)
* 将CMSampleBufferRef格式的数据编码成h264并写入文件
- (void)encoderToH264:(CMSampleBufferRef)sampleB
* 释放资源
- (void)freeX264R
X264Manager.m
02-Encode(FFmpeg+x264)
Created by liyang on 16/12/15.
Copyright (C) 2016年 liyang
All rights reserved.
#import "X264Manager.h"
#ifdef __cplusplus
extern "C" {
#include &libavutil/opt.h&
#include &libavcodec/avcodec.h&
#include &libavformat/avformat.h&
#include &libswscale/swscale.h&
#ifdef __cplusplus
编码之后&解码之前的画面: AVFrame --& 内容写入文件
编码之前&解码之后的画面: AVPackage --& 解码之后, 使用OpenGLES渲染
@implementation X264Manager
AVFormatContext
AVOutputFormat
AVCodecContext
encoder_h264_frame_ // 编码的图像宽度
encoder_h264_frame_ // 编码的图像高度
* 设置编码后文件的文件名,保存路径
- (void)setFileSavedPath:(NSString *)
out_file = [self nsstring2char:path];
* 将路径转成C语言字符串(传入路径为C字符串)
- (char*)nsstring2char:(NSString *)path
NSUInteger len = [path length];
char *filepath = (char*)malloc(sizeof(char) * (len + 1));
[path getCString:filepath maxLength:len + 1 encoding:[NSString defaultCStringEncoding]];
- (int)setX264ResourceWithVideoWidth:(int)width height:(int)height bitrate:(int)bitrate
// 1.默认从第0帧开始(记录当前的帧数)
framecnt = 0;
// 2.记录传入的宽度&高度
encoder_h264_frame_width =
encoder_h264_frame_height =
// 3.注册FFmpeg所有编解码器(无论编码还是解码都需要该步骤)
av_register_all();
// 4.初始化AVFormatContext: 用作之后写入视频帧并编码成 h264,贯穿整个工程当中(释放资源时需要销毁)
pFormatCtx = avformat_alloc_context();
// 5.设置输出文件的路径
fmt = av_guess_format(NULL, out_file, NULL);
pFormatCtx-&oformat =
// 6.打开文件的缓冲区输入输出,flags 标识为
AVIO_FLAG_READ_WRITE ,可读写
if (avio_open(&pFormatCtx-&pb, out_file, AVIO_FLAG_READ_WRITE) & 0){
printf("Failed to open output file! \n");
return -1;
// 7.创建新的输出流, 用于写入文件
video_st = avformat_new_stream(pFormatCtx, 0);
// 8.设置 20 帧每秒 ,也就是 fps 为 20
video_st-&time_base.num = 1;
video_st-&time_base.den = 25;
if (video_st==NULL){
return -1;
// 9.pCodecCtx 用户存储编码所需的参数格式等等
// 9.1.从媒体流中获取到编码结构体,他们是一一对应的关系,一个 AVStream 对应一个
AVCodecContext
pCodecCtx = video_st-&
// 9.2.设置编码器的编码格式(是一个id),每一个编码器都对应着自己的 id,例如 h264 的编码 id 就是 AV_CODEC_ID_H264
pCodecCtx-&codec_id = fmt-&video_
// 9.3.设置编码类型为 视频编码
pCodecCtx-&codec_type = AVMEDIA_TYPE_VIDEO;
// 9.4.设置像素格式为 yuv 格式
pCodecCtx-&pix_fmt = PIX_FMT_YUV420P;
// 9.5.设置视频的宽高
pCodecCtx-&width = encoder_h264_frame_
pCodecCtx-&height = encoder_h264_frame_
// 9.6.设置帧率
pCodecCtx-&time_base.num = 1;
pCodecCtx-&time_base.den = 25;
// 9.7.设置码率(比特率)
pCodecCtx-&bit_rate =
// 9.8.视频质量度量标准(常见qmin=10, qmax=51)
pCodecCtx-&qmin = 10;
pCodecCtx-&qmax = 51;
// 9.9.设置图像组层的大小(GOP--&两个I帧之间的间隔)
pCodecCtx-&gop_size = 30;
// 9.10.设置 B 帧最大的数量,B帧为视频图片空间的前后预测帧, B 帧相对于 I、P 帧来说,压缩率比较大,也就是说相同码率的情况下,
// 越多 B 帧的视频,越清晰,现在很多打视频网站的高清视频,就是采用多编码 B 帧去提高清晰度,
// 但同时对于编解码的复杂度比较高,比较消耗性能与时间
pCodecCtx-&max_b_frames = 5;
// 10.可选设置
AVDictionary *param = 0;
if(pCodecCtx-&codec_id == AV_CODEC_ID_H264) {
// 通过--preset的参数调节编码速度和质量的平衡。
av_dict_set(&param, "preset", "slow", 0);
// 通过--tune的参数值指定片子的类型,是和视觉优化的参数,或有特别的情况。
// zerolatency: 零延迟,用在需要非常低的延迟的情况下,比如视频直播的编码
av_dict_set(&param, "tune", "zerolatency", 0);
// 11.输出打印信息,内部是通过printf函数输出(不需要输出可以注释掉该局)
av_dump_format(pFormatCtx, 0, out_file, 1);
// 12.通过 codec_id 找到对应的编码器
pCodec = avcodec_find_encoder(pCodecCtx-&codec_id);
if (!pCodec) {
printf("Can not find encoder! \n");
return -1;
// 13.打开编码器,并设置参数 param
if (avcodec_open2(pCodecCtx, pCodec,&param) & 0) {
printf("Failed to open encoder! \n");
return -1;
// 13.初始化原始数据对象: AVFrame
pFrame = av_frame_alloc();
// 14.通过像素格式(这里为 YUV)获取图片的真实大小,例如将 480 * 720 转换成 int 类型
avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx-&pix_fmt, pCodecCtx-&width, pCodecCtx-&height);
// 15.h264 封装格式的文件头部,基本上每种编码都有着自己的格式的头部,想看具体实现的同学可以看看 h264 的具体实现
avformat_write_header(pFormatCtx, NULL);
// 16.创建编码后的数据 AVPacket 结构体来存储 AVFrame 编码后生成的数据
av_new_packet(&pkt, picture_size);
// 17.设置 yuv 数据中 y 图的宽高
y_size = pCodecCtx-&width * pCodecCtx-&
* 将CMSampleBufferRef格式的数据编码成h264并写入文件
- (void)encoderToH264:(CMSampleBufferRef)sampleBuffer
// 1.通过CMSampleBufferRef对象获取CVPixelBufferRef对象
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// 2.锁定imageBuffer内存地址开始进行编码
if (CVPixelBufferLockBaseAddress(imageBuffer, 0) == kCVReturnSuccess) {
// 3.从CVPixelBufferRef读取YUV的值
// NV12和NV21属于YUV格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane
// 3.1.获取Y分量的地址
UInt8 *bufferPtr = (UInt8 *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer,0);
// 3.2.获取UV分量的地址
UInt8 *bufferPtr1 = (UInt8 *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer,1);
// 3.3.根据像素获取图片的真实宽度&高度
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
// 获取Y分量长度
size_t bytesrow0 = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer,0);
size_t bytesrow1
= CVPixelBufferGetBytesPerRowOfPlane(imageBuffer,1);
UInt8 *yuv420_data = (UInt8 *)malloc(width * height *3/2);
// 3.4.将NV12数据转成YUV420P(I420)数据
UInt8 *pY = bufferP
UInt8 *pUV = bufferPtr1;
UInt8 *pU = yuv420_data + width*
UInt8 *pV = pU + width*height/4;
for(int i =0;i&i++)
memcpy(yuv420_data+i*width,pY+i*bytesrow0,width);
for(int j = 0;j&height/2;j++)
for(int i =0;i&width/2;i++)
*(pU++) = pUV[i&&1];
*(pV++) = pUV[(i&&1) + 1];
pUV+=bytesrow1;
// 3.5.分别读取YUV的数据
picture_buf = yuv420_
pFrame-&data[0] = picture_
pFrame-&data[1] = picture_buf+ y_
pFrame-&data[2] = picture_buf+ y_size*5/4;
// 4.设置当前帧
pFrame-&pts =
int got_picture = 0;
// 4.设置宽度高度以及YUV各式
pFrame-&width = encoder_h264_frame_
pFrame-&height = encoder_h264_frame_
pFrame-&format = PIX_FMT_YUV420P;
// 5.对编码前的原始数据(AVFormat)利用编码器进行编码,将 pFrame 编码后的数据传入pkt 中
int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
if(ret & 0) {
printf("Failed to encode! \n");
// 6.编码成功后写入 AVPacket 到 输入输出数据操作着 pFormatCtx 中,当然,记得释放内存
if (got_picture==1) {
framecnt++;
pkt.stream_index = video_st-&
ret = av_write_frame(pFormatCtx, &pkt);
av_free_packet(&pkt);
// 7.释放yuv数据
free(yuv420_data);
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
* 释放资源
- (void)freeX264Resource
// 1.释放AVFormatContext
int ret = flush_encoder(pFormatCtx,0);
if (ret & 0) {
printf("Flushing encoder failed\n");
// 2.将还未输出的AVPacket输出出来
av_write_trailer(pFormatCtx);
// 3.关闭资源
if (video_st){
avcodec_close(video_st-&codec);
av_free(pFrame);
avio_close(pFormatCtx-&pb);
avformat_free_context(pFormatCtx);
int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index)
AVPacket enc_
if (!(fmt_ctx-&streams[stream_index]-&codec-&codec-&capabilities &
CODEC_CAP_DELAY))
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)
if (!got_frame){
ret = av_write_frame(fmt_ctx, &enc_pkt);
if (ret & 0)
编码出来的视频是 x264 格式的,encoder.h264视频体积小方便传输,下面一章会介绍如何利用FFmpeg解码视频,并且播放。
FFmpeg X264编码参数 目录 码率控制
1 X264的preset和tune
2 编码延时建议
2 ffmpeg编码参数和x264参数对照 3 x264参数说明
5 2.码率控制X264提供三种码率控制的方式:bitrate, qp, crf。这...
为什么进行压缩编码? 视频是由一帧帧的图像组成(见实例)比如一张Gif图片其实就可以被分解成若干张单独的图片1.gif分别出的图片2.png 未经压缩的视频的数据量巨大比如:录音一分钟视频, 需要多大的空间来保存了?1& 为了不让用户感受到卡顿效果, 1秒钟之内至少需要16...
视频编码介绍 为什么进行压缩编码? 视频是由一帧帧的图像组成(见实例)比如一张Gif图片其实就可以被分解成若干张单独的图片分别出的图片 未经压缩的视频的数据量巨大比如:录音一分钟视频, 需要多大的空间来保存了?1& 为了不让用户感受到卡顿效果, 1秒钟之内至少需要16帧画面...
37 视频滤镜 在配置编译FFmpeg时可以通过--disable-filters来禁止所有滤镜的编译。也可以配置编译脚本来输出所有包含进编译的滤镜信息。 下面是当前可用的视频滤镜介绍。 alphaextract 把输入视频作为灰度视频来提取透明通道,它通常和alphame...
硬件编码相关知识(H264,H265) 阅读人群:研究硬件编码器应用于iOS开发中,从0研究关于硬件编解码,码流中解析数据结构 内容概述:关于H264,H265的背景,数据结构,在iOS开发中编解码的应用 博客地址:
硬件编码相关知识 简书地址:
硬件编码相关知识 一....
我有想过,我以后有多个孩子,如果第一胎是个女孩,那么第二胎最好是龙凤胎或双胞胎。那我就有三个孩子,估计我每天都会乐呵呵的,我喜欢孩子,而且我有三个。我一定是个幸福的爸爸。 当我知道她们要来到我身边的时候,我就会开始记录我要说的话。第二天读给她们听。 我要告诉她们,每天我的心...
http://dantezhao.com//concurrency-and-parallelism-future/ 0x00 前言其实Future模型离我们并不远,如果你接触过Spark、Hadoop这些优秀的开源项目,那么在运行程序的时候关注一下他们的...
今天来聊一聊写作吧。 一直有朋友问我,为什么要写文章?我从来都没有正面回答过。熟悉我的人都知道,这个人不关注时政,也没有热点,更不会取触目惊心的标题,只会写一些无聊的故事。 所以,一直读者寥寥,也没有什么人关注。 在知乎上有一个关于自媒体发展的提问,有人这么分析:前些年博客...
一开始临了张板绘的美女背影,画了几笔就画不下去了,只能擦掉;看来功力还不够,老老实实临简单的吧!
看你如此笨拙, 看你如此骄横, 看你如此狂躁, ………… 看你如此地无宁静平和的状态, 我没有恼怒, 没有不屑, 我满怀同情, 我在静心思索, 你如此这般是如何造就的? 我又能为你做点儿什么?}

我要回帖

更多关于 垃圾回收的算法与实现 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信