最简单的视频编码器:基于libvpx(编码YUV为VP8)
=====================================================
最简单的视频编码器系列文章列表:
最简单的视频编码器:基于libx264(编码YUV为H.264)
最简单的视频编码器:基于libx265(编码YUV为H.265)
=====================================================
本文记录一个最简单的基于libvpx的VP8视频编码器。这个例子是从官方的示例代码中精简出来的例子。我发现与H.264不同,VP8的裸流(即不包含封装格式的纯视频数据流)是不能播放的。换言之,VP8的裸流必须存放在容器中才可以播放。官方示例代码中存储VP8视频流的封装格式是IVF。IVF这种封装格式不是很常见,相关的资料可以查询有关的文档。
此外,这个工程中的libvpx也可以编码VP9格式的视频。但是封装格式那里有点问题目前还没有解决,所以暂时没有包含编码VP9的代码。编码VP9和编码VP8的函数调用是一模一样的。
流程图
调用libvpx进行视频编码的流程图如下所示。
流程图中主要的函数如下所示。
vpx_img_alloc():为图像结构体vpx_image_t分配内存。
vpx_codec_enc_config_default():设置参数集结构体vpx_codec_enc_cfg_t的缺省值。
vpx_codec_enc_init():打开编码器。
vpx_codec_encode():编码一帧图像。
vpx_codec_get_cx_data():获取一帧压缩编码数据。
vpx_codec_destroy():关闭编码器。
存储数据的结构体如下所示。
vpx_image_t:存储压缩编码前的像素数据。
vpx_codec_cx_pkt_t:存储压缩编码后的码流数据。
IVF封装格式处理的函数如下所示。
write_ivf_file_header():写IVF封装格式的文件头。
write_ivf_frame_header():写IVF封装格式中每帧数据的帧头。
此外流程图中还包括一个“flush_encoder”模块,该模块使用的函数和编码模块是一样的。唯一的不同在于不再输入视频像素数据。它的作用是输出编码器中剩余的码流数据。
源代码
/** * 最简单的基于VPX的视频编码器 * Simplest VPX Encoder * * 雷霄骅 Lei Xiaohua * leixiaohua1020@126.com * 中国传媒大学/数字电视技术 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本程序精简了libvpx中的一个示例代码。 * 可以YUV格式的像素数据编码为VPx(VP8/VP9)码流,是最简单的 * 基于libvpx的视频编码器 * 需要注意的是,编码输出的封装格式是IVF * * This example modified from an example from vpx project. * It encode YUV data to VPX(VP8/VP9) bitstream. * It's the simplest encoder example based on libvpx. */ #include <stdio.h> #include <stdlib.h> #define VPX_CODEC_DISABLE_COMPAT 1 #include "vpx/vpx_encoder.h" #include "vpx/vp8cx.h" #define interface (&vpx_codec_vp8_cx_algo) #define fourcc 0x30385056 #define IVF_FILE_HDR_SZ (32) #define IVF_FRAME_HDR_SZ (12) static void mem_put_le16(char *mem, unsigned int val) { mem[0] = val; mem[1] = val>>8; } static void mem_put_le32(char *mem, unsigned int val) { mem[0] = val; mem[1] = val>>8; mem[2] = val>>16; mem[3] = val>>24; } static void write_ivf_file_header(FILE *outfile, const vpx_codec_enc_cfg_t *cfg, int frame_cnt) { char header[32]; if(cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS) return; header[0] = 'D'; header[1] = 'K'; header[2] = 'I'; header[3] = 'F'; mem_put_le16(header+4, 0); /* version */ mem_put_le16(header+6, 32); /* headersize */ mem_put_le32(header+8, fourcc); /* headersize */ mem_put_le16(header+12, cfg->g_w); /* width */ mem_put_le16(header+14, cfg->g_h); /* height */ mem_put_le32(header+16, cfg->g_timebase.den); /* rate */ mem_put_le32(header+20, cfg->g_timebase.num); /* scale */ mem_put_le32(header+24, frame_cnt); /* length */ mem_put_le32(header+28, 0); /* unused */ fwrite(header, 1, 32, outfile); } static void write_ivf_frame_header(FILE *outfile, const vpx_codec_cx_pkt_t *pkt) { char header[12]; vpx_codec_pts_t pts; if(pkt->kind != VPX_CODEC_CX_FRAME_PKT) return; pts = pkt->data.frame.pts; mem_put_le32(header, pkt->data.frame.sz); mem_put_le32(header+4, pts&0xFFFFFFFF); mem_put_le32(header+8, pts >> 32); fwrite(header, 1, 12, outfile); } int main(int argc, char **argv) { FILE *infile, *outfile; vpx_codec_ctx_t codec; vpx_codec_enc_cfg_t cfg; int frame_cnt = 0; unsigned char file_hdr[IVF_FILE_HDR_SZ]; unsigned char frame_hdr[IVF_FRAME_HDR_SZ]; vpx_image_t raw; vpx_codec_err_t ret; int width,height; int y_size; int frame_avail; int got_data; int flags = 0; width = 640; height = 360; /* Open input file for this encoding pass */ infile = fopen("../cuc_ieschool_640x360_yuv420p.yuv", "rb"); outfile = fopen("cuc_ieschool.ivf", "wb"); if(infile==NULL||outfile==NULL){ printf("Error open files.\n"); return -1; } if(!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, width, height, 1)){ printf("Fail to allocate image\n"); return -1; } printf("Using %s\n",vpx_codec_iface_name(interface)); /* Populate encoder configuration */ ret = vpx_codec_enc_config_default(interface, &cfg, 0); if(ret) { printf("Failed to get config: %s\n", vpx_codec_err_to_string(ret)); return -1; } /* Update the default configuration with our settings */ cfg.rc_target_bitrate =800; cfg.g_w = width; cfg.g_h = height; write_ivf_file_header(outfile, &cfg, 0); /* Initialize codec */ if(vpx_codec_enc_init(&codec, interface, &cfg, 0)){ printf("Failed to initialize encoder\n"); return -1; } frame_avail = 1; got_data = 0; y_size=cfg.g_w*cfg.g_h; while(frame_avail || got_data) { vpx_codec_iter_t iter = NULL; const vpx_codec_cx_pkt_t *pkt; if(fread(raw.planes[0], 1, y_size*3/2, infile)!=y_size*3/2){ frame_avail=0; } if(frame_avail){ ret=vpx_codec_encode(&codec,&raw,frame_cnt,1,flags,VPX_DL_REALTIME); }else{ ret=vpx_codec_encode(&codec,NULL,frame_cnt,1,flags,VPX_DL_REALTIME); } if(ret){ printf("Failed to encode frame\n"); return -1; } got_data = 0; while( (pkt = vpx_codec_get_cx_data(&codec, &iter)) ) { got_data = 1; switch(pkt->kind) { case VPX_CODEC_CX_FRAME_PKT: write_ivf_frame_header(outfile, pkt); fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz,outfile); break; default: break; } } printf("Succeed encode frame: %5d\n",frame_cnt); frame_cnt++; } fclose(infile); vpx_codec_destroy(&codec); /* Try to rewrite the file header with the actual frame count */ if(!fseek(outfile, 0, SEEK_SET)) write_ivf_file_header(outfile, &cfg, frame_cnt-1); fclose(outfile); return 0; }
运行结果
程序的输入为一个YUV文件(已经测试过YUV420P格式)。
输出为IVF封装格式的VP8码流文件。
VP8码流文件的信息如下所示。
下载
Simplest Encoder
项目主页
SourceForge:https://sourceforge.net/projects/simplestencoder/
Github:https://github.com/leixiaohua1020/simplest_encoder
开源中国:http://git.oschina.net/leixiaohua1020/simplest_encoder
CDSN下载地址:http://download.csdn.net/detail/leixiaohua1020/8284105
该解决方案包含了几个常见的编码器的使用示例:
simplest_vpx_encoder:最简单的基于libvpx的视频编码器
simplest_x264_encoder:最简单的基于libx264的视频编码器
simplest_x265_encoder:最简单的基于libx265的视频编码器
最简单的视频编码器:基于libvpx(编码YUV为VP8)的更多相关文章
- 最简单的视频编码器:基于libx265(编码YUV为H.265)
===================================================== 最简单的视频编码器系列文章列表: 最简单的视频编码器:编译 最简单的视频编码器:基于libx ...
- 最简单的视频编码器:基于libx264(编码YUV为H.264)
===================================================== 最简单的视频编码器系列文章列表: 最简单的视频编码器:编译 最简单的视频编码器:基于libx ...
- 最简单的视频编码器:编译(libx264,libx265,libvpx)
===================================================== 最简单的视频编码器系列文章列表: 最简单的视频编码器:编译 最简单的视频编码器:基于libx ...
- 最简单的基于FFmpeg的视频编码器-更新版(YUV编码为HEVC(H.265))
===================================================== 最简单的基于FFmpeg的视频编码器文章列表: 最简单的基于FFMPEG的视频编码器(YUV ...
- Jcompress: 一款基于huffman编码和最小堆的压缩、解压缩小程序
前言 最近基于huffman编码和最小堆排序算法实现了一个压缩.解压缩的小程序.其源代码已经上传到github上面: Jcompress下载地址 .在本人的github上面有一个叫Utility的re ...
- TensorFlow上实践基于自编码的One Class Learning
“我不知道什么是爱,但我知道什么是不爱” --One Class Learning的自白 一.单分类简介 如果将分类算法进行划分,根据类别个数的不同可以分为单分类.二分类.多分类,常见的分类算法主要解 ...
- WebGIS中兴趣点简单查询、基于Lucene分词查询的设计和实现
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.前言 兴趣点查询是指:输入框中输入地名.人名等查询信息后,地图上可 ...
- 极其简单的使用基于gulp和sass前端工作流
简单的记录自己如何在实际工作中使用gulp和sass的.我的原则是,小而美! gulp与sass介绍 gulp 什么是gulp?和Grunt一样,是一种任务管理工具:和Grunt又不一样,gulp是一 ...
- 使用X264编码yuv格式的视频帧使用ffmpeg解码h264视频帧
前面一篇博客介绍在centos上搭建点击打开链接ffmpeg及x264开发环境.以下就来问个样例: 1.利用x264库将YUV格式视频文件编码为h264格式视频文件 2.利用ffmpeh库将h264格 ...
随机推荐
- 简介JSP与FreeMarker及Volicity区别
FreeMarker FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页.电子邮件.配置文件.源代码等)的通用工具. 它不是面向最终用户的,而是一个 ...
- 去掉textarea和input在ios下默认出现的圆角
-webkit-appearance:none;/*清除ios默认圆角*/ border-radius:0;
- tensorflow共享变量 the difference between tf.Variable() and get_variable()
一般这样用tf.get_variable(): v = tf.get_variable(name, shape, dtype, initializer) 下面内容来源于 http://blog.csd ...
- Python盗号原理-代码实现截屏键盘记录远程发送(不要做坏事)
这年头盗号的从出不穷,不是脱裤就是社工钓鱼.今天呢我们就以前面的几篇技术文章做铺垫,来讲一下python盗号的原理(学习用途,请勿用于违法犯罪),知己知彼方能百战不殆嘛. 脱裤我们就不多提了,无非就是 ...
- DEV中svg图标的使用
0.开始之前 先看看使用效果 在操作栏的使用: 在菜单中的使用 1.简述SVG图标 中文名:可缩放矢量图形 外文名:Scalable Vector Graphics 外语缩写:SVG 开发商:万维网联 ...
- Centos 6.5 安装 rar
wget http://www.rarsoft.com/rar/rarlinux-x64-5.4.0.tar.gz tar -zxvf rarlinux-x64-5.4.0.tar.gz cd rar ...
- ubuntu查看IO
在命令行直接 cp 一个比较大的文件时,由于没有提示信息,总感觉很不放心,可以通过查看IO的方式确认cp操作的进展程度. 查看IO可以使用iostat命令,但是前提是要安装sysstat sudo a ...
- acm几何
fzu 2231,N个点求构成的平行四边行个数. 题意简重点在优化上 #include <cstdio> #include <iostream> #include <cs ...
- Java为什么要配置环境变量及如何配置环境变量
在没有配置环境变量之前,用cmd执行Java文件,需要指明Java的可执行文件,否则无法运行. 配置环境是为了在不用切换可执行文件目录下,方便Java程序的执行和控制. 那么环境变量就是让系统根据环境 ...
- 拾遗与填坑《深度探索C++对象模型》3.2节
<深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...