00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050 #include <stdio.h>
00051 #include <stdlib.h>
00052 #include <string.h>
00053
00054 #include "vpx/vpx_encoder.h"
00055
00056 #include "../tools_common.h"
00057 #include "../video_writer.h"
00058
00059 static const char *exec_name;
00060
00061 void usage_exit(void) {
00062 fprintf(stderr,
00063 "Usage: %s <codec> <width> <height> <infile> <outfile> "
00064 "<frame limit>\n",
00065 exec_name);
00066 exit(EXIT_FAILURE);
00067 }
00068
00069 static int get_frame_stats(vpx_codec_ctx_t *ctx, const vpx_image_t *img,
00070 vpx_codec_pts_t pts, unsigned int duration,
00071 vpx_enc_frame_flags_t flags, unsigned int deadline,
00072 vpx_fixed_buf_t *stats) {
00073 int got_pkts = 0;
00074 vpx_codec_iter_t iter = NULL;
00075 const vpx_codec_cx_pkt_t *pkt = NULL;
00076 const vpx_codec_err_t res =
00077 vpx_codec_encode(ctx, img, pts, duration, flags, deadline);
00078 if (res != VPX_CODEC_OK) die_codec(ctx, "Failed to get frame stats.");
00079
00080 while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
00081 got_pkts = 1;
00082
00083 if (pkt->kind == VPX_CODEC_STATS_PKT) {
00084 const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf;
00085 const size_t pkt_size = pkt->data.twopass_stats.sz;
00086 stats->buf = realloc(stats->buf, stats->sz + pkt_size);
00087 memcpy((uint8_t *)stats->buf + stats->sz, pkt_buf, pkt_size);
00088 stats->sz += pkt_size;
00089 }
00090 }
00091
00092 return got_pkts;
00093 }
00094
00095 static int encode_frame(vpx_codec_ctx_t *ctx, const vpx_image_t *img,
00096 vpx_codec_pts_t pts, unsigned int duration,
00097 vpx_enc_frame_flags_t flags, unsigned int deadline,
00098 VpxVideoWriter *writer) {
00099 int got_pkts = 0;
00100 vpx_codec_iter_t iter = NULL;
00101 const vpx_codec_cx_pkt_t *pkt = NULL;
00102 const vpx_codec_err_t res =
00103 vpx_codec_encode(ctx, img, pts, duration, flags, deadline);
00104 if (res != VPX_CODEC_OK) die_codec(ctx, "Failed to encode frame.");
00105
00106 while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
00107 got_pkts = 1;
00108 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
00109 const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
00110
00111 if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
00112 pkt->data.frame.sz,
00113 pkt->data.frame.pts))
00114 die_codec(ctx, "Failed to write compressed frame.");
00115 printf(keyframe ? "K" : ".");
00116 fflush(stdout);
00117 }
00118 }
00119
00120 return got_pkts;
00121 }
00122
00123 static vpx_fixed_buf_t pass0(vpx_image_t *raw, FILE *infile,
00124 const VpxInterface *encoder,
00125 const vpx_codec_enc_cfg_t *cfg, int max_frames) {
00126 vpx_codec_ctx_t codec;
00127 int frame_count = 0;
00128 vpx_fixed_buf_t stats = { NULL, 0 };
00129
00130 if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
00131 die_codec(&codec, "Failed to initialize encoder");
00132
00133
00134 while (vpx_img_read(raw, infile)) {
00135 ++frame_count;
00136 get_frame_stats(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY,
00137 &stats);
00138 if (max_frames > 0 && frame_count >= max_frames) break;
00139 }
00140
00141
00142 while (get_frame_stats(&codec, NULL, frame_count, 1, 0, VPX_DL_GOOD_QUALITY,
00143 &stats)) {
00144 }
00145
00146 printf("Pass 0 complete. Processed %d frames.\n", frame_count);
00147 if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
00148
00149 return stats;
00150 }
00151
00152 static void pass1(vpx_image_t *raw, FILE *infile, const char *outfile_name,
00153 const VpxInterface *encoder, const vpx_codec_enc_cfg_t *cfg,
00154 int max_frames) {
00155 VpxVideoInfo info = { encoder->fourcc,
00156 cfg->g_w,
00157 cfg->g_h,
00158 { cfg->g_timebase.num, cfg->g_timebase.den } };
00159 VpxVideoWriter *writer = NULL;
00160 vpx_codec_ctx_t codec;
00161 int frame_count = 0;
00162
00163 writer = vpx_video_writer_open(outfile_name, kContainerIVF, &info);
00164 if (!writer) die("Failed to open %s for writing", outfile_name);
00165
00166 if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
00167 die_codec(&codec, "Failed to initialize encoder");
00168
00169
00170 while (vpx_img_read(raw, infile)) {
00171 ++frame_count;
00172 encode_frame(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY, writer);
00173
00174 if (max_frames > 0 && frame_count >= max_frames) break;
00175 }
00176
00177
00178 while (encode_frame(&codec, NULL, -1, 1, 0, VPX_DL_GOOD_QUALITY, writer)) {
00179 }
00180
00181 printf("\n");
00182
00183 if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
00184
00185 vpx_video_writer_close(writer);
00186
00187 printf("Pass 1 complete. Processed %d frames.\n", frame_count);
00188 }
00189
00190 int main(int argc, char **argv) {
00191 FILE *infile = NULL;
00192 int w, h;
00193 vpx_codec_ctx_t codec;
00194 vpx_codec_enc_cfg_t cfg;
00195 vpx_image_t raw;
00196 vpx_codec_err_t res;
00197 vpx_fixed_buf_t stats;
00198
00199 const VpxInterface *encoder = NULL;
00200 const int fps = 30;
00201 const int bitrate = 200;
00202 const char *const codec_arg = argv[1];
00203 const char *const width_arg = argv[2];
00204 const char *const height_arg = argv[3];
00205 const char *const infile_arg = argv[4];
00206 const char *const outfile_arg = argv[5];
00207 int max_frames = 0;
00208 exec_name = argv[0];
00209
00210 if (argc != 7) die("Invalid number of arguments.");
00211
00212 max_frames = (int)strtol(argv[6], NULL, 0);
00213
00214 encoder = get_vpx_encoder_by_name(codec_arg);
00215 if (!encoder) die("Unsupported codec.");
00216
00217 w = (int)strtol(width_arg, NULL, 0);
00218 h = (int)strtol(height_arg, NULL, 0);
00219
00220 if (w <= 0 || h <= 0 || (w % 2) != 0 || (h % 2) != 0)
00221 die("Invalid frame size: %dx%d", w, h);
00222
00223 if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, w, h, 1))
00224 die("Failed to allocate image", w, h);
00225
00226 printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
00227
00228
00229 res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
00230 if (res) die_codec(&codec, "Failed to get default codec config.");
00231
00232 cfg.g_w = w;
00233 cfg.g_h = h;
00234 cfg.g_timebase.num = 1;
00235 cfg.g_timebase.den = fps;
00236 cfg.rc_target_bitrate = bitrate;
00237
00238 if (!(infile = fopen(infile_arg, "rb")))
00239 die("Failed to open %s for reading", infile_arg);
00240
00241
00242 cfg.g_pass = VPX_RC_FIRST_PASS;
00243 stats = pass0(&raw, infile, encoder, &cfg, max_frames);
00244
00245
00246 rewind(infile);
00247 cfg.g_pass = VPX_RC_LAST_PASS;
00248 cfg.rc_twopass_stats_in = stats;
00249 pass1(&raw, infile, outfile_arg, encoder, &cfg, max_frames);
00250 free(stats.buf);
00251
00252 vpx_img_free(&raw);
00253 fclose(infile);
00254
00255 return EXIT_SUCCESS;
00256 }