Skip to content

Instantly share code, notes, and snippets.

@racerxdl
Created December 28, 2013 21:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save racerxdl/8164330 to your computer and use it in GitHub Desktop.
Save racerxdl/8164330 to your computer and use it in GitHub Desktop.
A simple VPX Encoder Class based on VP8 SDK example
/* This is not a full code, just an example */
#include "video/VPXEncoder.h"
VPXEncoder encoder;
char *pixels;
int main() {
/* do blablabla */
encoder.in_width = 1280; // Input Image Width
encoder.in_height = 720; // Input Image Height
encoder.out_width = 1280; // Output Image Width
encoder.out_height = 720; // Output Image Height
encoder.open("teste.vpx"); // Open the output file for writting, in this example teste.vpx will be the file
/* do your stuff */
while(bla) {
// Do Stuff and put the pixels you want on char array pixels. The pixels array must be width * height * 3 (RGB Pixel data)
// If you need to Flip Vertically (for example with an OpenGL Pixel array) use this
encoder.encode(pixels, true);
// If not, use this
encoder.encode(pixels);
}
// Lets close the file and finish the vpx:
encoder.close()
return 0;
}
/*
* VPXEncoder.h
*
* Created on: 28/12/2013
* Author: lucas
* Based on: http://www.webmproject.org/docs/vp8-sdk/example__simple__encoder.html
* An part of StepFever project.
*/
#include "VPXEncoder.h"
VPXEncoder::VPXEncoder() :in_width(0)
,in_height(0)
,out_width(0)
,out_height(0)
,in_pixel_format(AV_PIX_FMT_RGB24)
,out_pixel_format(AV_PIX_FMT_YUV420P)
,sws(NULL)
{}
void VPXEncoder::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 */
(void) fwrite(header, 1, 32, outfile);
}
void VPXEncoder::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);
(void) fwrite(header, 1, 12, outfile);
}
void VPXEncoder::die_codec(vpx_codec_ctx_t *ctx, const char *s) {
const char *detail = vpx_codec_error_detail(ctx);
cout << s << ": " << vpx_codec_error(ctx) << endl;
if(detail)
cout << " " << detail << endl;
exit(EXIT_FAILURE);
}
VPXEncoder::~VPXEncoder() {
if(sws)
close();
}
bool VPXEncoder::open(std::string filename) {
if(!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, out_width, out_height, 1)) {
cout << "Failed to allocate image " << out_width << " " << out_height << endl;
return false;
}
if(out_pixel_format != AV_PIX_FMT_YUV420P) {
cout << ("At this moment the output format must be AV_PIX_FMT_YUV420P") << endl;
return false;
}
sws = sws_getContext(in_width, in_height, in_pixel_format,
out_width, out_height, out_pixel_format,
SWS_FAST_BILINEAR, NULL, NULL, NULL);
if(!sws) {
cout << "Cannot create SWS context" << endl;
::exit(EXIT_FAILURE);
}
outfile = fopen(filename.c_str(), "w+b");
if(!outfile) {
cout << "Cannot open the VP8 destination file" << endl;
return false;
}
res = vpx_codec_enc_config_default(interface, &cfg, 0);
if(res) {
cout << "Failed to get config: " << vpx_codec_err_to_string(res) << endl;
return false;
}
cfg.rc_target_bitrate = out_width * out_height * cfg.rc_target_bitrate;
cfg.g_w = out_width;
cfg.g_h = out_height;
write_ivf_file_header(outfile, &cfg, 0);
if(vpx_codec_enc_init(&codec, interface, &cfg, 0))
die_codec(&codec, "Failed to initialize encoder");
frame_avail = 1;
got_data = 0;
}
bool VPXEncoder::encode(char* pixels, bool ReflectY) {
vpx_codec_iter_t iter = NULL;
const vpx_codec_cx_pkt_t *pkt;
if(!sws) {
cout << ("Not initialized, so cannot encode") << endl;
return false;
}
// copy the pixels into our "raw input" container.
int bytes_filled = avpicture_fill(&pic_raw, (uint8_t*)pixels, in_pixel_format, in_width, in_height);
if(!bytes_filled) {
cout << ("Cannot fill the raw input buffer") << endl;
return false;
}
// Reflect for OpenGL
if(ReflectY) {
for (int i = 0; i < 4; i++) {
pic_raw.data[i] += pic_raw.linesize[i] * (in_height-1);
pic_raw.linesize[i] = -pic_raw.linesize[i];
}
}
// convert to I420 for vp8
int h = sws_scale(sws, pic_raw.data, pic_raw.linesize, 0, in_height, raw.planes, raw.stride);
if(h != out_height) {
cout << "scale failed: " << h << endl;
return false;
}
if(vpx_codec_encode(&codec, &raw, frame_cnt, 1, flags, VPX_DL_REALTIME))
die_codec(&codec, "Failed to encode frame");
while( (pkt = vpx_codec_get_cx_data(&codec, &iter)) ) {
switch(pkt->kind) {
case VPX_CODEC_CX_FRAME_PKT:
write_ivf_frame_header(outfile, pkt);
(void) fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile);
break;
default:break;
}
}
frame_cnt++;
return true;
}
bool VPXEncoder::close() {
if(outfile) {
if(!fseek(outfile, 0, SEEK_SET))
write_ivf_file_header(outfile, &cfg, frame_cnt-1);
fclose(outfile);
outfile = NULL;
}
return true;
}
/*
* VPXEncoder.h
*
* Created on: 28/12/2013
* Author: lucas
* Based on: http://www.webmproject.org/docs/vp8-sdk/example__simple__encoder.html
* An part of StepFever project.
*/
#include "../game.h"
#ifndef VPXENCODER_H_
#define VPXENCODER_H_
//#define VPX_CODEC_DISABLE_COMPAT 1
#include "vpx/vpx_encoder.h"
#include "vpx/vp8cx.h"
#define interface (vpx_codec_vp8_cx())
#define fourcc 0x30385056
#define IVF_FILE_HDR_SZ (32)
#define IVF_FRAME_HDR_SZ (12)
extern "C" {
#include <x264.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
}
class VPXEncoder {
private:
static inline void mem_put_le16(char *mem, unsigned int val) { mem[0] = val; mem[1] = val>>8; }
static inline 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);
static void write_ivf_frame_header(FILE *outfile, const vpx_codec_cx_pkt_t *pkt);
static void die_codec(vpx_codec_ctx_t *ctx, const char *s);
public:
VPXEncoder();
virtual ~VPXEncoder();
bool open(std::string filename); /* open for encoding */
bool encode(char* pixels, bool ReflectY); /* encode the given data */
bool encode(char* pixels) { return encode(pixels, false); }; /* encode the given data */
bool close(); /* close the encoder and file, frees all memory */
// Params
FILE *outfile;
vpx_codec_ctx_t codec;
vpx_codec_enc_cfg_t cfg;
int frame_cnt = 0;
vpx_image_t raw;
vpx_codec_err_t res;
int frame_avail;
int got_data;
int flags = 0;
int in_width;
int in_height;
int out_width;
int out_height;
AVPixelFormat in_pixel_format;
AVPixelFormat out_pixel_format;
AVPicture pic_raw;
struct SwsContext* sws;
};
#endif /* VPXENCODER_H_ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment