最後活躍 1 month ago

A simple VPX Encoder Class based on VP8 SDK example

修訂 947f6a3b63861b7dc09282e9dbd1206d05d0457b

VPXEncoder.cpp 原始檔案
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
13VPXEncoder::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
22void 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
44void 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}
59void 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
67VPXEncoder::~VPXEncoder() {
68 if(sws)
69 close();
70}
71
72bool 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
116bool 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
162bool 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 原始檔案
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
26extern "C" {
27 #include <x264.h>
28 #include <libswscale/swscale.h>
29 #include <libavcodec/avcodec.h>
30}
31
32
33class VPXEncoder {
34private:
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
41public:
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 原始檔案
1/* This is not a full code, just an example */
2
3#include "video/VPXEncoder.h"
4
5VPXEncoder encoder;
6char *pixels;
7
8int 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}