Dernière activité 1 month ago

A simple VPX Encoder Class based on VP8 SDK example

Lucas Teske a révisé ce gist 12 years ago. Aller à la révision

3 files changed, 274 insertions

VPXEncoder.cpp(fichier créé)

@@ -0,0 +1,172 @@
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 + }

VPXEncoder.h(fichier créé)

@@ -0,0 +1,72 @@
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_ */

example.cpp(fichier créé)

@@ -0,0 +1,30 @@
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 + }
Plus récent Plus ancien