VPXEncoder.cpp
· 4.6 KiB · C++
Sin formato
/*
* 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;
}
| 1 | /* |
| 2 | * VPXEncoder.h |
| 3 | * |
| 4 | * Created on: 28/12/2013 |
| 5 | * Author: lucas |
| 6 | * Based on: http://www.webmproject.org/docs/vp8-sdk/example__simple__encoder.html |
| 7 | * An part of StepFever project. |
| 8 | */ |
| 9 | |
| 10 | |
| 11 | #include "VPXEncoder.h" |
| 12 | |
| 13 | VPXEncoder::VPXEncoder() :in_width(0) |
| 14 | ,in_height(0) |
| 15 | ,out_width(0) |
| 16 | ,out_height(0) |
| 17 | ,in_pixel_format(AV_PIX_FMT_RGB24) |
| 18 | ,out_pixel_format(AV_PIX_FMT_YUV420P) |
| 19 | ,sws(NULL) |
| 20 | {} |
| 21 | |
| 22 | void VPXEncoder::write_ivf_file_header(FILE *outfile, const vpx_codec_enc_cfg_t *cfg, int frame_cnt) { |
| 23 | char header[32]; |
| 24 | |
| 25 | if(cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS) |
| 26 | return; |
| 27 | header[0] = 'D'; |
| 28 | header[1] = 'K'; |
| 29 | header[2] = 'I'; |
| 30 | header[3] = 'F'; |
| 31 | mem_put_le16(header+4, 0); /* version */ |
| 32 | mem_put_le16(header+6, 32); /* headersize */ |
| 33 | mem_put_le32(header+8, fourcc); /* headersize */ |
| 34 | mem_put_le16(header+12, cfg->g_w); /* width */ |
| 35 | mem_put_le16(header+14, cfg->g_h); /* height */ |
| 36 | mem_put_le32(header+16, cfg->g_timebase.den); /* rate */ |
| 37 | mem_put_le32(header+20, cfg->g_timebase.num); /* scale */ |
| 38 | mem_put_le32(header+24, frame_cnt); /* length */ |
| 39 | mem_put_le32(header+28, 0); /* unused */ |
| 40 | |
| 41 | (void) fwrite(header, 1, 32, outfile); |
| 42 | } |
| 43 | |
| 44 | void VPXEncoder::write_ivf_frame_header(FILE *outfile, const vpx_codec_cx_pkt_t *pkt) |
| 45 | { |
| 46 | char header[12]; |
| 47 | vpx_codec_pts_t pts; |
| 48 | |
| 49 | if(pkt->kind != VPX_CODEC_CX_FRAME_PKT) |
| 50 | return; |
| 51 | |
| 52 | pts = pkt->data.frame.pts; |
| 53 | mem_put_le32(header, pkt->data.frame.sz); |
| 54 | mem_put_le32(header+4, pts&0xFFFFFFFF); |
| 55 | mem_put_le32(header+8, pts >> 32); |
| 56 | |
| 57 | (void) fwrite(header, 1, 12, outfile); |
| 58 | } |
| 59 | void VPXEncoder::die_codec(vpx_codec_ctx_t *ctx, const char *s) { |
| 60 | const char *detail = vpx_codec_error_detail(ctx); |
| 61 | cout << s << ": " << vpx_codec_error(ctx) << endl; |
| 62 | if(detail) |
| 63 | cout << " " << detail << endl; |
| 64 | exit(EXIT_FAILURE); |
| 65 | } |
| 66 | |
| 67 | VPXEncoder::~VPXEncoder() { |
| 68 | if(sws) |
| 69 | close(); |
| 70 | } |
| 71 | |
| 72 | bool VPXEncoder::open(std::string filename) { |
| 73 | |
| 74 | if(!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, out_width, out_height, 1)) { |
| 75 | cout << "Failed to allocate image " << out_width << " " << out_height << endl; |
| 76 | return false; |
| 77 | } |
| 78 | |
| 79 | if(out_pixel_format != AV_PIX_FMT_YUV420P) { |
| 80 | cout << ("At this moment the output format must be AV_PIX_FMT_YUV420P") << endl; |
| 81 | return false; |
| 82 | } |
| 83 | sws = sws_getContext(in_width, in_height, in_pixel_format, |
| 84 | out_width, out_height, out_pixel_format, |
| 85 | SWS_FAST_BILINEAR, NULL, NULL, NULL); |
| 86 | |
| 87 | if(!sws) { |
| 88 | cout << "Cannot create SWS context" << endl; |
| 89 | ::exit(EXIT_FAILURE); |
| 90 | } |
| 91 | |
| 92 | outfile = fopen(filename.c_str(), "w+b"); |
| 93 | if(!outfile) { |
| 94 | cout << "Cannot open the VP8 destination file" << endl; |
| 95 | return false; |
| 96 | } |
| 97 | |
| 98 | res = vpx_codec_enc_config_default(interface, &cfg, 0); |
| 99 | if(res) { |
| 100 | cout << "Failed to get config: " << vpx_codec_err_to_string(res) << endl; |
| 101 | return false; |
| 102 | } |
| 103 | |
| 104 | cfg.rc_target_bitrate = out_width * out_height * cfg.rc_target_bitrate; |
| 105 | cfg.g_w = out_width; |
| 106 | cfg.g_h = out_height; |
| 107 | |
| 108 | write_ivf_file_header(outfile, &cfg, 0); |
| 109 | if(vpx_codec_enc_init(&codec, interface, &cfg, 0)) |
| 110 | die_codec(&codec, "Failed to initialize encoder"); |
| 111 | |
| 112 | frame_avail = 1; |
| 113 | got_data = 0; |
| 114 | } |
| 115 | |
| 116 | bool VPXEncoder::encode(char* pixels, bool ReflectY) { |
| 117 | vpx_codec_iter_t iter = NULL; |
| 118 | const vpx_codec_cx_pkt_t *pkt; |
| 119 | |
| 120 | if(!sws) { |
| 121 | cout << ("Not initialized, so cannot encode") << endl; |
| 122 | return false; |
| 123 | } |
| 124 | |
| 125 | // copy the pixels into our "raw input" container. |
| 126 | int bytes_filled = avpicture_fill(&pic_raw, (uint8_t*)pixels, in_pixel_format, in_width, in_height); |
| 127 | if(!bytes_filled) { |
| 128 | cout << ("Cannot fill the raw input buffer") << endl; |
| 129 | return false; |
| 130 | } |
| 131 | |
| 132 | // Reflect for OpenGL |
| 133 | if(ReflectY) { |
| 134 | for (int i = 0; i < 4; i++) { |
| 135 | pic_raw.data[i] += pic_raw.linesize[i] * (in_height-1); |
| 136 | pic_raw.linesize[i] = -pic_raw.linesize[i]; |
| 137 | } |
| 138 | } |
| 139 | // convert to I420 for vp8 |
| 140 | int h = sws_scale(sws, pic_raw.data, pic_raw.linesize, 0, in_height, raw.planes, raw.stride); |
| 141 | |
| 142 | if(h != out_height) { |
| 143 | cout << "scale failed: " << h << endl; |
| 144 | return false; |
| 145 | } |
| 146 | if(vpx_codec_encode(&codec, &raw, frame_cnt, 1, flags, VPX_DL_REALTIME)) |
| 147 | die_codec(&codec, "Failed to encode frame"); |
| 148 | |
| 149 | while( (pkt = vpx_codec_get_cx_data(&codec, &iter)) ) { |
| 150 | switch(pkt->kind) { |
| 151 | case VPX_CODEC_CX_FRAME_PKT: |
| 152 | write_ivf_frame_header(outfile, pkt); |
| 153 | (void) fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile); |
| 154 | break; |
| 155 | default:break; |
| 156 | } |
| 157 | } |
| 158 | frame_cnt++; |
| 159 | return true; |
| 160 | } |
| 161 | |
| 162 | bool VPXEncoder::close() { |
| 163 | |
| 164 | if(outfile) { |
| 165 | if(!fseek(outfile, 0, SEEK_SET)) |
| 166 | write_ivf_file_header(outfile, &cfg, frame_cnt-1); |
| 167 | |
| 168 | fclose(outfile); |
| 169 | outfile = NULL; |
| 170 | } |
| 171 | return true; |
| 172 | } |
| 173 |
VPXEncoder.h
· 2.0 KiB · C
Sin formato
/*
* 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_ */
| 1 | /* |
| 2 | * VPXEncoder.h |
| 3 | * |
| 4 | * Created on: 28/12/2013 |
| 5 | * Author: lucas |
| 6 | * Based on: http://www.webmproject.org/docs/vp8-sdk/example__simple__encoder.html |
| 7 | * An part of StepFever project. |
| 8 | */ |
| 9 | |
| 10 | #include "../game.h" |
| 11 | |
| 12 | #ifndef VPXENCODER_H_ |
| 13 | #define VPXENCODER_H_ |
| 14 | |
| 15 | //#define VPX_CODEC_DISABLE_COMPAT 1 |
| 16 | |
| 17 | #include "vpx/vpx_encoder.h" |
| 18 | #include "vpx/vp8cx.h" |
| 19 | |
| 20 | #define interface (vpx_codec_vp8_cx()) |
| 21 | #define fourcc 0x30385056 |
| 22 | |
| 23 | #define IVF_FILE_HDR_SZ (32) |
| 24 | #define IVF_FRAME_HDR_SZ (12) |
| 25 | |
| 26 | extern "C" { |
| 27 | #include <x264.h> |
| 28 | #include <libswscale/swscale.h> |
| 29 | #include <libavcodec/avcodec.h> |
| 30 | } |
| 31 | |
| 32 | |
| 33 | class VPXEncoder { |
| 34 | private: |
| 35 | static inline void mem_put_le16(char *mem, unsigned int val) { mem[0] = val; mem[1] = val>>8; } |
| 36 | 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; } |
| 37 | static void write_ivf_file_header(FILE *outfile, const vpx_codec_enc_cfg_t *cfg, int frame_cnt); |
| 38 | static void write_ivf_frame_header(FILE *outfile, const vpx_codec_cx_pkt_t *pkt); |
| 39 | static void die_codec(vpx_codec_ctx_t *ctx, const char *s); |
| 40 | |
| 41 | public: |
| 42 | VPXEncoder(); |
| 43 | virtual ~VPXEncoder(); |
| 44 | bool open(std::string filename); /* open for encoding */ |
| 45 | bool encode(char* pixels, bool ReflectY); /* encode the given data */ |
| 46 | bool encode(char* pixels) { return encode(pixels, false); }; /* encode the given data */ |
| 47 | bool close(); /* close the encoder and file, frees all memory */ |
| 48 | |
| 49 | // Params |
| 50 | |
| 51 | FILE *outfile; |
| 52 | vpx_codec_ctx_t codec; |
| 53 | vpx_codec_enc_cfg_t cfg; |
| 54 | int frame_cnt = 0; |
| 55 | vpx_image_t raw; |
| 56 | vpx_codec_err_t res; |
| 57 | int frame_avail; |
| 58 | int got_data; |
| 59 | int flags = 0; |
| 60 | |
| 61 | int in_width; |
| 62 | int in_height; |
| 63 | int out_width; |
| 64 | int out_height; |
| 65 | AVPixelFormat in_pixel_format; |
| 66 | AVPixelFormat out_pixel_format; |
| 67 | AVPicture pic_raw; |
| 68 | |
| 69 | struct SwsContext* sws; |
| 70 | }; |
| 71 | |
| 72 | #endif /* VPXENCODER_H_ */ |
| 73 |
example.cpp
· 929 B · C++
Sin formato
/* 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;
}
| 1 | /* This is not a full code, just an example */ |
| 2 | |
| 3 | #include "video/VPXEncoder.h" |
| 4 | |
| 5 | VPXEncoder encoder; |
| 6 | char *pixels; |
| 7 | |
| 8 | int main() { |
| 9 | /* do blablabla */ |
| 10 | encoder.in_width = 1280; // Input Image Width |
| 11 | encoder.in_height = 720; // Input Image Height |
| 12 | encoder.out_width = 1280; // Output Image Width |
| 13 | encoder.out_height = 720; // Output Image Height |
| 14 | encoder.open("teste.vpx"); // Open the output file for writting, in this example teste.vpx will be the file |
| 15 | |
| 16 | /* do your stuff */ |
| 17 | |
| 18 | while(bla) { |
| 19 | // Do Stuff and put the pixels you want on char array pixels. The pixels array must be width * height * 3 (RGB Pixel data) |
| 20 | // If you need to Flip Vertically (for example with an OpenGL Pixel array) use this |
| 21 | encoder.encode(pixels, true); |
| 22 | // If not, use this |
| 23 | encoder.encode(pixels); |
| 24 | } |
| 25 | |
| 26 | // Lets close the file and finish the vpx: |
| 27 | encoder.close() |
| 28 | |
| 29 | return 0; |
| 30 | } |