最简单的视频编码器:基于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格 ...
随机推荐
- [BZOJ]1031 字符加密Cipher(JSOI2007)
持续划水中…… 感觉BZOJ上AC人数多的基本都是一些模板题,也就是某些算法的裸题.这些题目mark一下到时候回来复习也是不错的选择. Description 喜欢钻研问题的JS同学,最近又迷上了对加 ...
- MFC程序设计小结
由于毕业设计要用到MFC,因此本人这段时间开始学习MFC编程,边学边做,现将一些重要的知识点总结如下: 创建一个MFC程序,操作步骤很简单,要点就是选择MFC AppWizard(exe).单文档或者 ...
- JVM回收方法区内存
很多人认为方法区(或者HotSpot虚拟机中的永久代)是没有垃圾收集的,Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区进行垃圾收集的“性价比”一般比较低:在堆中,尤其是 ...
- PLSQL(2)
游标 [1] 不带参数的游标 -- 取出EMP表中的所有人名字 DECLARE CURSOR C IS SELECT * FROM EMP; V_EMP ...
- SSM框架原理,作用及使用方法(非原创)
原帖:地址https://blog.csdn.net/bieleyang/article/details/77862042 如有侵权请联系删除 作用: SSM框架是spring MVC ,spring ...
- session.save()返回值问题
正常都应该返回插入的主键 但是 如果你用sessionFactory来写就一定返回0 先科普下持久化数据库的三个状态方便下面理解 一次会话状态中,持久化对象经历以下三种状态:1 transient:对 ...
- ES6(es2015)新增实用方法汇总
Array 1.map() [1,2,3,4].map(function(item, index, array){ return item * 2; }) 对数组中的每一项执行一次回调函数,三个参数 ...
- 【阿里聚安全·安全周刊】Google“手枪”替换 | 伊朗中央银行禁止加密货币
本周七个关键词:Google"手枪"替换丨IOS 漏洞影响工业交换机丨伊朗中央银行禁止加密货币丨黑客针对医疗保健丨付费DDoS攻击丨数据获利的8种方式丨MySQL 8.0 正式版 ...
- easyui datagrid属性和方法
本文可以当做api来使用 使用方法(Usage Example) 从现有的表单元素创建数据表格,定义在html中的行,列和数据. <table class="easyui-datagr ...
- 通过AIDL在两个APP之间Service通信
一.项目介绍 [知识准备] ①Android Interface definition language(aidl,android接口定义语言),其目的实现跨进程的调用.进程是程序在os中执行的载体, ...