| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2010 The WebM project authors. All Rights Reserved. | 2 * Copyright (c) 2010 The WebM project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 // | 37 // |
| 38 // The active map is cleared on frame 44. | 38 // The active map is cleared on frame 44. |
| 39 // | 39 // |
| 40 // Observing The Effects | 40 // Observing The Effects |
| 41 // --------------------- | 41 // --------------------- |
| 42 // Use the `simple_decoder` example to decode this sample, and observe | 42 // Use the `simple_decoder` example to decode this sample, and observe |
| 43 // the change in the image at frames 22, 33, and 44. | 43 // the change in the image at frames 22, 33, and 44. |
| 44 | 44 |
| 45 #include <stdio.h> | 45 #include <stdio.h> |
| 46 #include <stdlib.h> | 46 #include <stdlib.h> |
| 47 #include <stdarg.h> | |
| 48 #include <string.h> | 47 #include <string.h> |
| 48 |
| 49 #define VPX_CODEC_DISABLE_COMPAT 1 | 49 #define VPX_CODEC_DISABLE_COMPAT 1 |
| 50 #include "vpx/vp8cx.h" |
| 50 #include "vpx/vpx_encoder.h" | 51 #include "vpx/vpx_encoder.h" |
| 51 #include "vpx/vp8cx.h" | |
| 52 #define interface (vpx_codec_vp8_cx()) | |
| 53 #define fourcc 0x30385056 | |
| 54 | 52 |
| 55 #define IVF_FILE_HDR_SZ (32) | 53 #include "./tools_common.h" |
| 56 #define IVF_FRAME_HDR_SZ (12) | 54 #include "./video_writer.h" |
| 57 | 55 |
| 58 static void mem_put_le16(char *mem, unsigned int val) { | 56 static const char *exec_name; |
| 59 mem[0] = val; | 57 |
| 60 mem[1] = val>>8; | 58 void usage_exit() { |
| 59 fprintf(stderr, "Usage: %s <width> <height> <infile> <outfile>\n", exec_name); |
| 60 exit(EXIT_FAILURE); |
| 61 } | 61 } |
| 62 | 62 |
| 63 static void mem_put_le32(char *mem, unsigned int val) { | 63 static void set_roi_map(const vpx_codec_enc_cfg_t *cfg, |
| 64 mem[0] = val; | 64 vpx_codec_ctx_t *codec) { |
| 65 mem[1] = val>>8; | 65 unsigned int i; |
| 66 mem[2] = val>>16; | 66 vpx_roi_map_t roi = {0}; |
| 67 mem[3] = val>>24; | 67 |
| 68 roi.rows = cfg->g_h / 16; |
| 69 roi.cols = cfg->g_w / 16; |
| 70 |
| 71 roi.delta_q[0] = 0; |
| 72 roi.delta_q[1] = -2; |
| 73 roi.delta_q[2] = -4; |
| 74 roi.delta_q[3] = -6; |
| 75 |
| 76 roi.delta_lf[0] = 0; |
| 77 roi.delta_lf[1] = 1; |
| 78 roi.delta_lf[2] = 2; |
| 79 roi.delta_lf[3] = 3; |
| 80 |
| 81 roi.static_threshold[0] = 1500; |
| 82 roi.static_threshold[1] = 1000; |
| 83 roi.static_threshold[2] = 500; |
| 84 roi.static_threshold[3] = 0; |
| 85 |
| 86 roi.roi_map = (uint8_t *)malloc(roi.rows * roi.cols); |
| 87 for (i = 0; i < roi.rows * roi.cols; ++i) |
| 88 roi.roi_map[i] = i % 4; |
| 89 |
| 90 if (vpx_codec_control(codec, VP8E_SET_ROI_MAP, &roi)) |
| 91 die_codec(codec, "Failed to set ROI map"); |
| 92 |
| 93 free(roi.roi_map); |
| 68 } | 94 } |
| 69 | 95 |
| 70 static void die(const char *fmt, ...) { | 96 static void set_active_map(const vpx_codec_enc_cfg_t *cfg, |
| 71 va_list ap; | 97 vpx_codec_ctx_t *codec) { |
| 98 unsigned int i; |
| 99 vpx_active_map_t map = {0}; |
| 72 | 100 |
| 73 va_start(ap, fmt); | 101 map.rows = cfg->g_h / 16; |
| 74 vprintf(fmt, ap); | 102 map.cols = cfg->g_w / 16; |
| 75 if(fmt[strlen(fmt)-1] != '\n') | 103 |
| 76 printf("\n"); | 104 map.active_map = (uint8_t *)malloc(map.rows * map.cols); |
| 77 exit(EXIT_FAILURE); | 105 for (i = 0; i < map.rows * map.cols; ++i) |
| 106 map.active_map[i] = i % 2; |
| 107 |
| 108 if (vpx_codec_control(codec, VP8E_SET_ACTIVEMAP, &map)) |
| 109 die_codec(codec, "Failed to set active map"); |
| 110 |
| 111 free(map.active_map); |
| 78 } | 112 } |
| 79 | 113 |
| 80 static void die_codec(vpx_codec_ctx_t *ctx, const char *s) { | 114 static void unset_active_map(const vpx_codec_enc_cfg_t *cfg, |
| 81 const char *detail = vpx_codec_error_detail(ctx); | 115 vpx_codec_ctx_t *codec) { |
| 116 vpx_active_map_t map = {0}; |
| 82 | 117 |
| 83 printf("%s: %s\n", s, vpx_codec_error(ctx)); | 118 map.rows = cfg->g_h / 16; |
| 84 if(detail) | 119 map.cols = cfg->g_w / 16; |
| 85 printf(" %s\n",detail); | 120 map.active_map = NULL; |
| 86 exit(EXIT_FAILURE); | 121 |
| 122 if (vpx_codec_control(codec, VP8E_SET_ACTIVEMAP, &map)) |
| 123 die_codec(codec, "Failed to set active map"); |
| 87 } | 124 } |
| 88 | 125 |
| 89 static int read_frame(FILE *f, vpx_image_t *img) { | 126 static void encode_frame(vpx_codec_ctx_t *codec, |
| 90 size_t nbytes, to_read; | 127 vpx_image_t *img, |
| 91 int res = 1; | 128 int frame_index, |
| 129 VpxVideoWriter *writer) { |
| 130 vpx_codec_iter_t iter = NULL; |
| 131 const vpx_codec_cx_pkt_t *pkt = NULL; |
| 132 const vpx_codec_err_t res = vpx_codec_encode(codec, img, frame_index, 1, 0, |
| 133 VPX_DL_GOOD_QUALITY); |
| 134 if (res != VPX_CODEC_OK) |
| 135 die_codec(codec, "Failed to encode frame"); |
| 92 | 136 |
| 93 to_read = img->w*img->h*3/2; | 137 while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != NULL) { |
| 94 nbytes = fread(img->planes[0], 1, to_read, f); | 138 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { |
| 95 if(nbytes != to_read) { | 139 const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0; |
| 96 res = 0; | 140 if (!vpx_video_writer_write_frame(writer, |
| 97 if(nbytes > 0) | 141 pkt->data.frame.buf, |
| 98 printf("Warning: Read partial frame. Check your width & height!\n"); | 142 pkt->data.frame.sz, |
| 143 pkt->data.frame.pts)) { |
| 144 die_codec(codec, "Failed to write compressed frame"); |
| 145 } |
| 146 |
| 147 printf(keyframe ? "K" : "."); |
| 148 fflush(stdout); |
| 99 } | 149 } |
| 100 return res; | 150 } |
| 101 } | |
| 102 | |
| 103 static void write_ivf_file_header(FILE *outfile, | |
| 104 const vpx_codec_enc_cfg_t *cfg, | |
| 105 int frame_cnt) { | |
| 106 char header[32]; | |
| 107 | |
| 108 if(cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS) | |
| 109 return; | |
| 110 header[0] = 'D'; | |
| 111 header[1] = 'K'; | |
| 112 header[2] = 'I'; | |
| 113 header[3] = 'F'; | |
| 114 mem_put_le16(header+4, 0); /* version */ | |
| 115 mem_put_le16(header+6, 32); /* headersize */ | |
| 116 mem_put_le32(header+8, fourcc); /* headersize */ | |
| 117 mem_put_le16(header+12, cfg->g_w); /* width */ | |
| 118 mem_put_le16(header+14, cfg->g_h); /* height */ | |
| 119 mem_put_le32(header+16, cfg->g_timebase.den); /* rate */ | |
| 120 mem_put_le32(header+20, cfg->g_timebase.num); /* scale */ | |
| 121 mem_put_le32(header+24, frame_cnt); /* length */ | |
| 122 mem_put_le32(header+28, 0); /* unused */ | |
| 123 | |
| 124 (void) fwrite(header, 1, 32, outfile); | |
| 125 } | |
| 126 | |
| 127 | |
| 128 static void write_ivf_frame_header(FILE *outfile, | |
| 129 const vpx_codec_cx_pkt_t *pkt) | |
| 130 { | |
| 131 char header[12]; | |
| 132 vpx_codec_pts_t pts; | |
| 133 | |
| 134 if(pkt->kind != VPX_CODEC_CX_FRAME_PKT) | |
| 135 return; | |
| 136 | |
| 137 pts = pkt->data.frame.pts; | |
| 138 mem_put_le32(header, pkt->data.frame.sz); | |
| 139 mem_put_le32(header+4, pts&0xFFFFFFFF); | |
| 140 mem_put_le32(header+8, pts >> 32); | |
| 141 | |
| 142 (void) fwrite(header, 1, 12, outfile); | |
| 143 } | 151 } |
| 144 | 152 |
| 145 int main(int argc, char **argv) { | 153 int main(int argc, char **argv) { |
| 146 FILE *infile, *outfile; | 154 FILE *infile = NULL; |
| 147 vpx_codec_ctx_t codec; | 155 vpx_codec_ctx_t codec = {0}; |
| 148 vpx_codec_enc_cfg_t cfg; | 156 vpx_codec_enc_cfg_t cfg = {0}; |
| 149 int frame_cnt = 0; | 157 int frame_count = 0; |
| 150 vpx_image_t raw; | 158 vpx_image_t raw = {0}; |
| 151 vpx_codec_err_t res; | 159 vpx_codec_err_t res; |
| 152 long width; | 160 VpxVideoInfo info = {0}; |
| 153 long height; | 161 VpxVideoWriter *writer = NULL; |
| 154 int frame_avail; | 162 const VpxInterface *encoder = NULL; |
| 155 int got_data; | 163 const int fps = 2; // TODO(dkovalev) add command line argument |
| 156 int flags = 0; | 164 const int bitrate = 200; // kbit/s TODO(dkovalev) add command line argument |
| 157 | 165 |
| 158 /* Open files */ | 166 exec_name = argv[0]; |
| 159 if(argc!=5) | |
| 160 die("Usage: %s <width> <height> <infile> <outfile>\n", argv[0]); | |
| 161 width = strtol(argv[1], NULL, 0); | |
| 162 height = strtol(argv[2], NULL, 0); | |
| 163 if(width < 16 || width%2 || height <16 || height%2) | |
| 164 die("Invalid resolution: %ldx%ld", width, height); | |
| 165 if(!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, width, height, 1)) | |
| 166 die("Faile to allocate image", width, height); | |
| 167 if(!(outfile = fopen(argv[4], "wb"))) | |
| 168 die("Failed to open %s for writing", argv[4]); | |
| 169 | 167 |
| 170 printf("Using %s\n",vpx_codec_iface_name(interface)); | 168 if (argc != 5) |
| 169 die("Invalid number of arguments"); |
| 171 | 170 |
| 172 /* Populate encoder configuration */ | 171 encoder = get_vpx_encoder_by_name("vp8"); // only vp8 for now |
| 173 res = vpx_codec_enc_config_default(interface, &cfg, 0); | 172 if (!encoder) |
| 174 if(res) { | 173 die("Unsupported codec."); |
| 175 printf("Failed to get config: %s\n", vpx_codec_err_to_string(res)); | 174 |
| 176 return EXIT_FAILURE; | 175 info.codec_fourcc = encoder->fourcc; |
| 176 info.frame_width = strtol(argv[1], NULL, 0); |
| 177 info.frame_height = strtol(argv[2], NULL, 0); |
| 178 info.time_base.numerator = 1; |
| 179 info.time_base.denominator = fps; |
| 180 |
| 181 if (info.frame_width <= 0 || |
| 182 info.frame_height <= 0 || |
| 183 (info.frame_width % 2) != 0 || |
| 184 (info.frame_height % 2) != 0) { |
| 185 die("Invalid frame size: %dx%d", info.frame_width, info.frame_height); |
| 186 } |
| 187 |
| 188 if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width, |
| 189 info.frame_height, 1)) { |
| 190 die("Failed to allocate image."); |
| 191 } |
| 192 |
| 193 printf("Using %s\n", vpx_codec_iface_name(encoder->interface())); |
| 194 |
| 195 res = vpx_codec_enc_config_default(encoder->interface(), &cfg, 0); |
| 196 if (res) |
| 197 die_codec(&codec, "Failed to get default codec config."); |
| 198 |
| 199 cfg.g_w = info.frame_width; |
| 200 cfg.g_h = info.frame_height; |
| 201 cfg.g_timebase.num = info.time_base.numerator; |
| 202 cfg.g_timebase.den = info.time_base.denominator; |
| 203 cfg.rc_target_bitrate = bitrate; |
| 204 |
| 205 writer = vpx_video_writer_open(argv[4], kContainerIVF, &info); |
| 206 if (!writer) |
| 207 die("Failed to open %s for writing.", argv[4]); |
| 208 |
| 209 if (!(infile = fopen(argv[3], "rb"))) |
| 210 die("Failed to open %s for reading.", argv[3]); |
| 211 |
| 212 if (vpx_codec_enc_init(&codec, encoder->interface(), &cfg, 0)) |
| 213 die_codec(&codec, "Failed to initialize encoder"); |
| 214 |
| 215 while (vpx_img_read(&raw, infile)) { |
| 216 ++frame_count; |
| 217 |
| 218 if (frame_count == 22) { |
| 219 set_roi_map(&cfg, &codec); |
| 220 } else if (frame_count == 33) { |
| 221 set_active_map(&cfg, &codec); |
| 222 } else if (frame_count == 44) { |
| 223 unset_active_map(&cfg, &codec); |
| 177 } | 224 } |
| 178 | 225 |
| 179 /* Update the default configuration with our settings */ | 226 encode_frame(&codec, &raw, frame_count, writer); |
| 180 cfg.rc_target_bitrate = width * height * cfg.rc_target_bitrate | 227 } |
| 181 / cfg.g_w / cfg.g_h; | 228 encode_frame(&codec, NULL, -1, writer); |
| 182 cfg.g_w = width; | 229 printf("\n"); |
| 183 cfg.g_h = height; | 230 fclose(infile); |
| 231 printf("Processed %d frames.\n", frame_count); |
| 184 | 232 |
| 185 write_ivf_file_header(outfile, &cfg, 0); | 233 vpx_img_free(&raw); |
| 234 if (vpx_codec_destroy(&codec)) |
| 235 die_codec(&codec, "Failed to destroy codec."); |
| 186 | 236 |
| 237 vpx_video_writer_close(writer); |
| 187 | 238 |
| 188 /* Open input file for this encoding pass */ | 239 return EXIT_SUCCESS; |
| 189 if(!(infile = fopen(argv[3], "rb"))) | |
| 190 die("Failed to open %s for reading", argv[3]); | |
| 191 | |
| 192 /* Initialize codec */ | |
| 193 if(vpx_codec_enc_init(&codec, interface, &cfg, 0)) | |
| 194 die_codec(&codec, "Failed to initialize encoder"); | |
| 195 | |
| 196 frame_avail = 1; | |
| 197 got_data = 0; | |
| 198 while(frame_avail || got_data) { | |
| 199 vpx_codec_iter_t iter = NULL; | |
| 200 const vpx_codec_cx_pkt_t *pkt; | |
| 201 | |
| 202 if(frame_cnt + 1 == 22) { | |
| 203 vpx_roi_map_t roi; | |
| 204 unsigned int i; | |
| 205 | |
| 206 roi.rows = cfg.g_h/16; | |
| 207 roi.cols = cfg.g_w/16; | |
| 208 | |
| 209 roi.delta_q[0] = 0; | |
| 210 roi.delta_q[1] = -2; | |
| 211 roi.delta_q[2] = -4; | |
| 212 roi.delta_q[3] = -6; | |
| 213 | |
| 214 roi.delta_lf[0] = 0; | |
| 215 roi.delta_lf[1] = 1; | |
| 216 roi.delta_lf[2] = 2; | |
| 217 roi.delta_lf[3] = 3; | |
| 218 | |
| 219 roi.static_threshold[0] = 1500; | |
| 220 roi.static_threshold[1] = 1000; | |
| 221 roi.static_threshold[2] = 500; | |
| 222 roi.static_threshold[3] = 0; | |
| 223 | |
| 224 /* generate an ROI map for example */ | |
| 225 roi.roi_map = malloc(roi.rows * roi.cols); | |
| 226 for(i=0;i<roi.rows*roi.cols;i++) | |
| 227 roi.roi_map[i] = i & 3; | |
| 228 | |
| 229 if(vpx_codec_control(&codec, VP8E_SET_ROI_MAP, &roi)) | |
| 230 die_codec(&codec, "Failed to set ROI map"); | |
| 231 | |
| 232 free(roi.roi_map); | |
| 233 } else if(frame_cnt + 1 == 33) { | |
| 234 vpx_active_map_t active; | |
| 235 unsigned int i; | |
| 236 | |
| 237 active.rows = cfg.g_h/16; | |
| 238 active.cols = cfg.g_w/16; | |
| 239 | |
| 240 /* generate active map for example */ | |
| 241 active.active_map = malloc(active.rows * active.cols); | |
| 242 for(i=0;i<active.rows*active.cols;i++) | |
| 243 active.active_map[i] = i & 1; | |
| 244 | |
| 245 if(vpx_codec_control(&codec, VP8E_SET_ACTIVEMAP, &active)) | |
| 246 die_codec(&codec, "Failed to set active map"); | |
| 247 | |
| 248 free(active.active_map); | |
| 249 } else if(frame_cnt + 1 == 44) { | |
| 250 vpx_active_map_t active; | |
| 251 | |
| 252 active.rows = cfg.g_h/16; | |
| 253 active.cols = cfg.g_w/16; | |
| 254 | |
| 255 /* pass in null map to disable active_map*/ | |
| 256 active.active_map = NULL; | |
| 257 | |
| 258 if(vpx_codec_control(&codec, VP8E_SET_ACTIVEMAP, &active)) | |
| 259 die_codec(&codec, "Failed to set active map"); | |
| 260 } | |
| 261 frame_avail = read_frame(infile, &raw); | |
| 262 if(vpx_codec_encode(&codec, frame_avail? &raw : NULL, frame_cnt, | |
| 263 1, flags, VPX_DL_REALTIME)) | |
| 264 die_codec(&codec, "Failed to encode frame"); | |
| 265 got_data = 0; | |
| 266 while( (pkt = vpx_codec_get_cx_data(&codec, &iter)) ) { | |
| 267 got_data = 1; | |
| 268 switch(pkt->kind) { | |
| 269 case VPX_CODEC_CX_FRAME_PKT: | |
| 270 write_ivf_frame_header(outfile, pkt); | |
| 271 (void) fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, | |
| 272 outfile); | |
| 273 break; | |
| 274 default: | |
| 275 break; | |
| 276 } | |
| 277 printf(pkt->kind == VPX_CODEC_CX_FRAME_PKT | |
| 278 && (pkt->data.frame.flags & VPX_FRAME_IS_KEY)? "K":"."); | |
| 279 fflush(stdout); | |
| 280 } | |
| 281 frame_cnt++; | |
| 282 } | |
| 283 printf("\n"); | |
| 284 fclose(infile); | |
| 285 | |
| 286 printf("Processed %d frames.\n",frame_cnt-1); | |
| 287 vpx_img_free(&raw); | |
| 288 if(vpx_codec_destroy(&codec)) | |
| 289 die_codec(&codec, "Failed to destroy codec"); | |
| 290 | |
| 291 /* Try to rewrite the file header with the actual frame count */ | |
| 292 if(!fseek(outfile, 0, SEEK_SET)) | |
| 293 write_ivf_file_header(outfile, &cfg, frame_cnt-1); | |
| 294 fclose(outfile); | |
| 295 return EXIT_SUCCESS; | |
| 296 } | 240 } |
| OLD | NEW |