2
0

twopass_encoder.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. // Two Pass Encoder
  11. // ================
  12. //
  13. // This is an example of a two pass encoder loop. It takes an input file in
  14. // YV12 format, passes it through the encoder twice, and writes the compressed
  15. // frames to disk in IVF format. It builds upon the simple_encoder example.
  16. //
  17. // Twopass Variables
  18. // -----------------
  19. // Twopass mode needs to track the current pass number and the buffer of
  20. // statistics packets.
  21. //
  22. // Updating The Configuration
  23. // ---------------------------------
  24. // In two pass mode, the configuration has to be updated on each pass. The
  25. // statistics buffer is passed on the last pass.
  26. //
  27. // Encoding A Frame
  28. // ----------------
  29. // Encoding a frame in two pass mode is identical to the simple encoder
  30. // example. To increase the quality while sacrificing encoding speed,
  31. // VPX_DL_BEST_QUALITY can be used in place of VPX_DL_GOOD_QUALITY.
  32. //
  33. // Processing Statistics Packets
  34. // -----------------------------
  35. // Each packet of type `VPX_CODEC_CX_FRAME_PKT` contains the encoded data
  36. // for this frame. We write a IVF frame header, followed by the raw data.
  37. //
  38. //
  39. // Pass Progress Reporting
  40. // -----------------------------
  41. // It's sometimes helpful to see when each pass completes.
  42. //
  43. //
  44. // Clean-up
  45. // -----------------------------
  46. // Destruction of the encoder instance must be done on each pass. The
  47. // raw image should be destroyed at the end as usual.
  48. #include <stdio.h>
  49. #include <stdlib.h>
  50. #include <string.h>
  51. #include "vpx/vpx_encoder.h"
  52. #include "../tools_common.h"
  53. #include "../video_writer.h"
  54. static const char *exec_name;
  55. void usage_exit(void) {
  56. fprintf(stderr,
  57. "Usage: %s <codec> <width> <height> <infile> <outfile> "
  58. "<frame limit>\n",
  59. exec_name);
  60. exit(EXIT_FAILURE);
  61. }
  62. static int get_frame_stats(vpx_codec_ctx_t *ctx, const vpx_image_t *img,
  63. vpx_codec_pts_t pts, unsigned int duration,
  64. vpx_enc_frame_flags_t flags, unsigned int deadline,
  65. vpx_fixed_buf_t *stats) {
  66. int got_pkts = 0;
  67. vpx_codec_iter_t iter = NULL;
  68. const vpx_codec_cx_pkt_t *pkt = NULL;
  69. const vpx_codec_err_t res =
  70. vpx_codec_encode(ctx, img, pts, duration, flags, deadline);
  71. if (res != VPX_CODEC_OK) die_codec(ctx, "Failed to get frame stats.");
  72. while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
  73. got_pkts = 1;
  74. if (pkt->kind == VPX_CODEC_STATS_PKT) {
  75. const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf;
  76. const size_t pkt_size = pkt->data.twopass_stats.sz;
  77. stats->buf = realloc(stats->buf, stats->sz + pkt_size);
  78. memcpy((uint8_t *)stats->buf + stats->sz, pkt_buf, pkt_size);
  79. stats->sz += pkt_size;
  80. }
  81. }
  82. return got_pkts;
  83. }
  84. static int encode_frame(vpx_codec_ctx_t *ctx, const vpx_image_t *img,
  85. vpx_codec_pts_t pts, unsigned int duration,
  86. vpx_enc_frame_flags_t flags, unsigned int deadline,
  87. VpxVideoWriter *writer) {
  88. int got_pkts = 0;
  89. vpx_codec_iter_t iter = NULL;
  90. const vpx_codec_cx_pkt_t *pkt = NULL;
  91. const vpx_codec_err_t res =
  92. vpx_codec_encode(ctx, img, pts, duration, flags, deadline);
  93. if (res != VPX_CODEC_OK) die_codec(ctx, "Failed to encode frame.");
  94. while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
  95. got_pkts = 1;
  96. if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
  97. const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
  98. if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
  99. pkt->data.frame.sz,
  100. pkt->data.frame.pts))
  101. die_codec(ctx, "Failed to write compressed frame.");
  102. printf(keyframe ? "K" : ".");
  103. fflush(stdout);
  104. }
  105. }
  106. return got_pkts;
  107. }
  108. static vpx_fixed_buf_t pass0(vpx_image_t *raw, FILE *infile,
  109. const VpxInterface *encoder,
  110. const vpx_codec_enc_cfg_t *cfg, int max_frames) {
  111. vpx_codec_ctx_t codec;
  112. int frame_count = 0;
  113. vpx_fixed_buf_t stats = { NULL, 0 };
  114. if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
  115. die_codec(&codec, "Failed to initialize encoder");
  116. // Calculate frame statistics.
  117. while (vpx_img_read(raw, infile)) {
  118. ++frame_count;
  119. get_frame_stats(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY,
  120. &stats);
  121. if (max_frames > 0 && frame_count >= max_frames) break;
  122. }
  123. // Flush encoder.
  124. while (get_frame_stats(&codec, NULL, frame_count, 1, 0, VPX_DL_GOOD_QUALITY,
  125. &stats)) {
  126. }
  127. printf("Pass 0 complete. Processed %d frames.\n", frame_count);
  128. if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
  129. return stats;
  130. }
  131. static void pass1(vpx_image_t *raw, FILE *infile, const char *outfile_name,
  132. const VpxInterface *encoder, const vpx_codec_enc_cfg_t *cfg,
  133. int max_frames) {
  134. VpxVideoInfo info = { encoder->fourcc,
  135. cfg->g_w,
  136. cfg->g_h,
  137. { cfg->g_timebase.num, cfg->g_timebase.den } };
  138. VpxVideoWriter *writer = NULL;
  139. vpx_codec_ctx_t codec;
  140. int frame_count = 0;
  141. writer = vpx_video_writer_open(outfile_name, kContainerIVF, &info);
  142. if (!writer) die("Failed to open %s for writing", outfile_name);
  143. if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
  144. die_codec(&codec, "Failed to initialize encoder");
  145. // Encode frames.
  146. while (vpx_img_read(raw, infile)) {
  147. ++frame_count;
  148. encode_frame(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY, writer);
  149. if (max_frames > 0 && frame_count >= max_frames) break;
  150. }
  151. // Flush encoder.
  152. while (encode_frame(&codec, NULL, -1, 1, 0, VPX_DL_GOOD_QUALITY, writer)) {
  153. }
  154. printf("\n");
  155. if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
  156. vpx_video_writer_close(writer);
  157. printf("Pass 1 complete. Processed %d frames.\n", frame_count);
  158. }
  159. int main(int argc, char **argv) {
  160. FILE *infile = NULL;
  161. int w, h;
  162. vpx_codec_ctx_t codec;
  163. vpx_codec_enc_cfg_t cfg;
  164. vpx_image_t raw;
  165. vpx_codec_err_t res;
  166. vpx_fixed_buf_t stats;
  167. const VpxInterface *encoder = NULL;
  168. const int fps = 30; // TODO(dkovalev) add command line argument
  169. const int bitrate = 200; // kbit/s TODO(dkovalev) add command line argument
  170. const char *const codec_arg = argv[1];
  171. const char *const width_arg = argv[2];
  172. const char *const height_arg = argv[3];
  173. const char *const infile_arg = argv[4];
  174. const char *const outfile_arg = argv[5];
  175. int max_frames = 0;
  176. exec_name = argv[0];
  177. if (argc != 7) die("Invalid number of arguments.");
  178. max_frames = (int)strtol(argv[6], NULL, 0);
  179. encoder = get_vpx_encoder_by_name(codec_arg);
  180. if (!encoder) die("Unsupported codec.");
  181. w = (int)strtol(width_arg, NULL, 0);
  182. h = (int)strtol(height_arg, NULL, 0);
  183. if (w <= 0 || h <= 0 || (w % 2) != 0 || (h % 2) != 0)
  184. die("Invalid frame size: %dx%d", w, h);
  185. if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, w, h, 1))
  186. die("Failed to allocate image", w, h);
  187. printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
  188. // Configuration
  189. res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
  190. if (res) die_codec(&codec, "Failed to get default codec config.");
  191. cfg.g_w = w;
  192. cfg.g_h = h;
  193. cfg.g_timebase.num = 1;
  194. cfg.g_timebase.den = fps;
  195. cfg.rc_target_bitrate = bitrate;
  196. if (!(infile = fopen(infile_arg, "rb")))
  197. die("Failed to open %s for reading", infile_arg);
  198. // Pass 0
  199. cfg.g_pass = VPX_RC_FIRST_PASS;
  200. stats = pass0(&raw, infile, encoder, &cfg, max_frames);
  201. // Pass 1
  202. rewind(infile);
  203. cfg.g_pass = VPX_RC_LAST_PASS;
  204. cfg.rc_twopass_stats_in = stats;
  205. pass1(&raw, infile, outfile_arg, encoder, &cfg, max_frames);
  206. free(stats.buf);
  207. vpx_img_free(&raw);
  208. fclose(infile);
  209. return EXIT_SUCCESS;
  210. }