| OLD | NEW |
| (Empty) | |
| 1 /* |
| 2 * Copyright (c) 2010 The WebM project authors. All Rights Reserved. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ |
| 10 |
| 11 // Two Pass Encoder |
| 12 // ================ |
| 13 // |
| 14 // This is an example of a two pass encoder loop. It takes an input file in |
| 15 // YV12 format, passes it through the encoder twice, and writes the compressed |
| 16 // frames to disk in IVF format. It builds upon the simple_encoder example. |
| 17 // |
| 18 // Twopass Variables |
| 19 // ----------------- |
| 20 // Twopass mode needs to track the current pass number and the buffer of |
| 21 // statistics packets. |
| 22 // |
| 23 // Updating The Configuration |
| 24 // --------------------------------- |
| 25 // In two pass mode, the configuration has to be updated on each pass. The |
| 26 // statistics buffer is passed on the last pass. |
| 27 // |
| 28 // Encoding A Frame |
| 29 // ---------------- |
| 30 // Encoding a frame in two pass mode is identical to the simple encoder |
| 31 // example, except the deadline is set to VPX_DL_BEST_QUALITY to get the |
| 32 // best quality possible. VPX_DL_GOOD_QUALITY could also be used. |
| 33 // |
| 34 // |
| 35 // Processing Statistics Packets |
| 36 // ----------------------------- |
| 37 // Each packet of type `VPX_CODEC_CX_FRAME_PKT` contains the encoded data |
| 38 // for this frame. We write a IVF frame header, followed by the raw data. |
| 39 // |
| 40 // |
| 41 // Pass Progress Reporting |
| 42 // ----------------------------- |
| 43 // It's sometimes helpful to see when each pass completes. |
| 44 // |
| 45 // |
| 46 // Clean-up |
| 47 // ----------------------------- |
| 48 // Destruction of the encoder instance must be done on each pass. The |
| 49 // raw image should be destroyed at the end as usual. |
| 50 |
| 51 #include <stdio.h> |
| 52 #include <stdlib.h> |
| 53 #include <stdarg.h> |
| 54 #include <string.h> |
| 55 #define VPX_CODEC_DISABLE_COMPAT 1 |
| 56 #include "vpx/vpx_encoder.h" |
| 57 #include "vpx/vp8cx.h" |
| 58 #define interface (vpx_codec_vp8_cx()) |
| 59 #define fourcc 0x30385056 |
| 60 |
| 61 #define IVF_FILE_HDR_SZ (32) |
| 62 #define IVF_FRAME_HDR_SZ (12) |
| 63 |
| 64 static void mem_put_le16(char *mem, unsigned int val) { |
| 65 mem[0] = val; |
| 66 mem[1] = val>>8; |
| 67 } |
| 68 |
| 69 static void mem_put_le32(char *mem, unsigned int val) { |
| 70 mem[0] = val; |
| 71 mem[1] = val>>8; |
| 72 mem[2] = val>>16; |
| 73 mem[3] = val>>24; |
| 74 } |
| 75 |
| 76 static void die(const char *fmt, ...) { |
| 77 va_list ap; |
| 78 |
| 79 va_start(ap, fmt); |
| 80 vprintf(fmt, ap); |
| 81 if(fmt[strlen(fmt)-1] != '\n') |
| 82 printf("\n"); |
| 83 exit(EXIT_FAILURE); |
| 84 } |
| 85 |
| 86 static void die_codec(vpx_codec_ctx_t *ctx, const char *s) { |
| 87 const char *detail = vpx_codec_error_detail(ctx); |
| 88 |
| 89 printf("%s: %s\n", s, vpx_codec_error(ctx)); |
| 90 if(detail) |
| 91 printf(" %s\n",detail); |
| 92 exit(EXIT_FAILURE); |
| 93 } |
| 94 |
| 95 static int read_frame(FILE *f, vpx_image_t *img) { |
| 96 size_t nbytes, to_read; |
| 97 int res = 1; |
| 98 |
| 99 to_read = img->w*img->h*3/2; |
| 100 nbytes = fread(img->planes[0], 1, to_read, f); |
| 101 if(nbytes != to_read) { |
| 102 res = 0; |
| 103 if(nbytes > 0) |
| 104 printf("Warning: Read partial frame. Check your width & height!\n"); |
| 105 } |
| 106 return res; |
| 107 } |
| 108 |
| 109 static void write_ivf_file_header(FILE *outfile, |
| 110 const vpx_codec_enc_cfg_t *cfg, |
| 111 int frame_cnt) { |
| 112 char header[32]; |
| 113 |
| 114 if(cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS) |
| 115 return; |
| 116 header[0] = 'D'; |
| 117 header[1] = 'K'; |
| 118 header[2] = 'I'; |
| 119 header[3] = 'F'; |
| 120 mem_put_le16(header+4, 0); /* version */ |
| 121 mem_put_le16(header+6, 32); /* headersize */ |
| 122 mem_put_le32(header+8, fourcc); /* headersize */ |
| 123 mem_put_le16(header+12, cfg->g_w); /* width */ |
| 124 mem_put_le16(header+14, cfg->g_h); /* height */ |
| 125 mem_put_le32(header+16, cfg->g_timebase.den); /* rate */ |
| 126 mem_put_le32(header+20, cfg->g_timebase.num); /* scale */ |
| 127 mem_put_le32(header+24, frame_cnt); /* length */ |
| 128 mem_put_le32(header+28, 0); /* unused */ |
| 129 |
| 130 (void) fwrite(header, 1, 32, outfile); |
| 131 } |
| 132 |
| 133 |
| 134 static void write_ivf_frame_header(FILE *outfile, |
| 135 const vpx_codec_cx_pkt_t *pkt) |
| 136 { |
| 137 char header[12]; |
| 138 vpx_codec_pts_t pts; |
| 139 |
| 140 if(pkt->kind != VPX_CODEC_CX_FRAME_PKT) |
| 141 return; |
| 142 |
| 143 pts = pkt->data.frame.pts; |
| 144 mem_put_le32(header, pkt->data.frame.sz); |
| 145 mem_put_le32(header+4, pts&0xFFFFFFFF); |
| 146 mem_put_le32(header+8, pts >> 32); |
| 147 |
| 148 (void) fwrite(header, 1, 12, outfile); |
| 149 } |
| 150 |
| 151 int main(int argc, char **argv) { |
| 152 FILE *infile, *outfile; |
| 153 vpx_codec_ctx_t codec; |
| 154 vpx_codec_enc_cfg_t cfg; |
| 155 int frame_cnt = 0; |
| 156 vpx_image_t raw; |
| 157 vpx_codec_err_t res; |
| 158 long width; |
| 159 long height; |
| 160 int frame_avail; |
| 161 int got_data; |
| 162 int flags = 0; |
| 163 int pass; |
| 164 vpx_fixed_buf_t stats = {0}; |
| 165 |
| 166 /* Open files */ |
| 167 if(argc!=5) |
| 168 die("Usage: %s <width> <height> <infile> <outfile>\n", argv[0]); |
| 169 width = strtol(argv[1], NULL, 0); |
| 170 height = strtol(argv[2], NULL, 0); |
| 171 if(width < 16 || width%2 || height <16 || height%2) |
| 172 die("Invalid resolution: %ldx%ld", width, height); |
| 173 if(!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, width, height, 1)) |
| 174 die("Faile to allocate image", width, height); |
| 175 if(!(outfile = fopen(argv[4], "wb"))) |
| 176 die("Failed to open %s for writing", argv[4]); |
| 177 |
| 178 printf("Using %s\n",vpx_codec_iface_name(interface)); |
| 179 |
| 180 /* Populate encoder configuration */ |
| 181 res = vpx_codec_enc_config_default(interface, &cfg, 0); |
| 182 if(res) { |
| 183 printf("Failed to get config: %s\n", vpx_codec_err_to_string(res)); |
| 184 return EXIT_FAILURE; |
| 185 } |
| 186 |
| 187 /* Update the default configuration with our settings */ |
| 188 cfg.rc_target_bitrate = width * height * cfg.rc_target_bitrate |
| 189 / cfg.g_w / cfg.g_h; |
| 190 cfg.g_w = width; |
| 191 cfg.g_h = height; |
| 192 |
| 193 write_ivf_file_header(outfile, &cfg, 0); |
| 194 |
| 195 for(pass=0; pass<2; pass++) { |
| 196 frame_cnt = 0; |
| 197 |
| 198 if(pass == 0) |
| 199 cfg.g_pass = VPX_RC_FIRST_PASS; |
| 200 else { |
| 201 cfg.g_pass = VPX_RC_LAST_PASS; |
| 202 cfg.rc_twopass_stats_in = stats; |
| 203 } |
| 204 |
| 205 /* Open input file for this encoding pass */ |
| 206 if(!(infile = fopen(argv[3], "rb"))) |
| 207 die("Failed to open %s for reading", argv[3]); |
| 208 |
| 209 /* Initialize codec */ |
| 210 if(vpx_codec_enc_init(&codec, interface, &cfg, 0)) |
| 211 die_codec(&codec, "Failed to initialize encoder"); |
| 212 |
| 213 frame_avail = 1; |
| 214 got_data = 0; |
| 215 while(frame_avail || got_data) { |
| 216 vpx_codec_iter_t iter = NULL; |
| 217 const vpx_codec_cx_pkt_t *pkt; |
| 218 |
| 219 frame_avail = read_frame(infile, &raw); |
| 220 if(vpx_codec_encode(&codec, frame_avail? &raw : NULL, frame_cnt, |
| 221 1, flags, VPX_DL_BEST_QUALITY)) |
| 222 die_codec(&codec, "Failed to encode frame"); |
| 223 got_data = 0; |
| 224 while( (pkt = vpx_codec_get_cx_data(&codec, &iter)) ) { |
| 225 got_data = 1; |
| 226 switch(pkt->kind) { |
| 227 case VPX_CODEC_CX_FRAME_PKT: |
| 228 write_ivf_frame_header(outfile, pkt); |
| 229 (void) fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, |
| 230 outfile); |
| 231 break; |
| 232 case VPX_CODEC_STATS_PKT: |
| 233 stats.buf = realloc(stats.buf, stats.sz |
| 234 + pkt->data.twopass_stats.sz); |
| 235 if(!stats.buf) |
| 236 die("Memory reallocation failed.\n"); |
| 237 memcpy((char*)stats.buf + stats.sz, |
| 238 pkt->data.twopass_stats.buf, |
| 239 pkt->data.twopass_stats.sz); |
| 240 stats.sz += pkt->data.twopass_stats.sz; |
| 241 break; |
| 242 default: |
| 243 break; |
| 244 } |
| 245 printf(pkt->kind == VPX_CODEC_CX_FRAME_PKT |
| 246 && (pkt->data.frame.flags & VPX_FRAME_IS_KEY)? "K":"."); |
| 247 fflush(stdout); |
| 248 } |
| 249 frame_cnt++; |
| 250 } |
| 251 printf("\n"); |
| 252 fclose(infile); |
| 253 printf("Pass %d complete.\n", pass+1); |
| 254 if(vpx_codec_destroy(&codec)) |
| 255 die_codec(&codec, "Failed to destroy codec"); |
| 256 } |
| 257 |
| 258 printf("Processed %d frames.\n",frame_cnt-1); |
| 259 vpx_img_free(&raw); |
| 260 free(stats.buf); |
| 261 |
| 262 /* Try to rewrite the file header with the actual frame count */ |
| 263 if(!fseek(outfile, 0, SEEK_SET)) |
| 264 write_ivf_file_header(outfile, &cfg, frame_cnt-1); |
| 265 fclose(outfile); |
| 266 return EXIT_SUCCESS; |
| 267 } |
| OLD | NEW |