diff options
Diffstat (limited to 'examples/simple_encoder.c')
-rw-r--r-- | examples/simple_encoder.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/examples/simple_encoder.c b/examples/simple_encoder.c new file mode 100644 index 000000000..e64a962ae --- /dev/null +++ b/examples/simple_encoder.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Simple Encoder +// ============== +// +// This is an example of a simple encoder loop. It takes an input file in +// YV12 format, passes it through the encoder, and writes the compressed +// frames to disk in IVF format. Other decoder examples build upon this +// one. +// +// The details of the IVF format have been elided from this example for +// simplicity of presentation, as IVF files will not generally be used by +// your application. In general, an IVF file consists of a file header, +// followed by a variable number of frames. Each frame consists of a frame +// header followed by a variable length payload. The length of the payload +// is specified in the first four bytes of the frame header. The payload is +// the raw compressed data. +// +// Standard Includes +// ----------------- +// For encoders, you only have to include `vpx_encoder.h` and then any +// header files for the specific codecs you use. In this case, we're using +// vp8. The `VPX_CODEC_DISABLE_COMPAT` macro can be defined to ensure +// strict compliance with the latest SDK by disabling some backwards +// compatibility features. Defining this macro is encouraged. +// +// Getting The Default Configuration +// --------------------------------- +// Encoders have the notion of "usage profiles." For example, an encoder +// may want to publish default configurations for both a video +// conferencing appliction and a best quality offline encoder. These +// obviously have very different default settings. Consult the +// documentation for your codec to see if it provides any default +// configurations. All codecs provide a default configuration, number 0, +// which is valid for material in the vacinity of QCIF/QVGA. +// +// Updating The Configuration +// --------------------------------- +// Almost all applications will want to update the default configuration +// with settings specific to their usage. Here we set the width and height +// of the video file to that specified on the command line. We also scale +// the default bitrate based on the ratio between the default resolution +// and the resolution specified on the command line. +// +// Initializing The Codec +// ---------------------- +// The encoder is initialized by the following code. +// +// Encoding A Frame +// ---------------- +// The frame is read as a continuous block (size width * height * 3 / 2) +// from the input file. If a frame was read (the input file has not hit +// EOF) then the frame is passed to the encoder. Otherwise, a NULL +// is passed, indicating the End-Of-Stream condition to the encoder. The +// `frame_cnt` is reused as the presentation time stamp (PTS) and each +// frame is shown for one frame-time in duration. The flags parameter is +// unused in this example. The deadline is set to VPX_DL_REALTIME to +// make the example run as quickly as possible. +// +// Processing The Encoded Data +// --------------------------- +// Each packet of type `VPX_CODEC_CX_FRAME_PKT` contains the encoded data +// for this frame. We write a IVF frame header, followed by the raw data. +// +// Cleanup +// ------- +// The `vpx_codec_destroy` call frees any memory allocated by the codec. +// +// Error Handling +// -------------- +// This example does not special case any error return codes. If there was +// an error, a descriptive message is printed and the program exits. With +// few exeptions, vpx_codec functions return an enumerated error status, +// with the value `0` indicating success. + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#define VPX_CODEC_DISABLE_COMPAT 1 +#include "vpx/vpx_encoder.h" +#include "vpx/vp8cx.h" +#define interface (vpx_codec_vp8_cx()) +#define fourcc 0x30385056 + +#define IVF_FILE_HDR_SZ (32) +#define IVF_FRAME_HDR_SZ (12) + +static void mem_put_le16(char *mem, unsigned int val) { + mem[0] = val; + mem[1] = val>>8; +} + +static void mem_put_le32(char *mem, unsigned int val) { + mem[0] = val; + mem[1] = val>>8; + mem[2] = val>>16; + mem[3] = val>>24; +} + +static void die(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vprintf(fmt, ap); + if(fmt[strlen(fmt)-1] != '\n') + printf("\n"); + exit(EXIT_FAILURE); +} + +static void die_codec(vpx_codec_ctx_t *ctx, const char *s) { + const char *detail = vpx_codec_error_detail(ctx); + + printf("%s: %s\n", s, vpx_codec_error(ctx)); + if(detail) + printf(" %s\n",detail); + exit(EXIT_FAILURE); +} + +static int read_frame(FILE *f, vpx_image_t *img) { + size_t nbytes, to_read; + int res = 1; + + to_read = img->w*img->h*3/2; + nbytes = fread(img->planes[0], 1, to_read, f); + if(nbytes != to_read) { + res = 0; + if(nbytes > 0) + printf("Warning: Read partial frame. Check your width & height!\n"); + } + return res; +} + +static void write_ivf_file_header(FILE *outfile, + const vpx_codec_enc_cfg_t *cfg, + int frame_cnt) { + char header[32]; + + if(cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS) + return; + header[0] = 'D'; + header[1] = 'K'; + header[2] = 'I'; + header[3] = 'F'; + mem_put_le16(header+4, 0); /* version */ + mem_put_le16(header+6, 32); /* headersize */ + mem_put_le32(header+8, fourcc); /* headersize */ + mem_put_le16(header+12, cfg->g_w); /* width */ + mem_put_le16(header+14, cfg->g_h); /* height */ + mem_put_le32(header+16, cfg->g_timebase.den); /* rate */ + mem_put_le32(header+20, cfg->g_timebase.num); /* scale */ + mem_put_le32(header+24, frame_cnt); /* length */ + mem_put_le32(header+28, 0); /* unused */ + + (void) fwrite(header, 1, 32, outfile); +} + + +static void write_ivf_frame_header(FILE *outfile, + const vpx_codec_cx_pkt_t *pkt) +{ + char header[12]; + vpx_codec_pts_t pts; + + if(pkt->kind != VPX_CODEC_CX_FRAME_PKT) + return; + + pts = pkt->data.frame.pts; + mem_put_le32(header, pkt->data.frame.sz); + mem_put_le32(header+4, pts&0xFFFFFFFF); + mem_put_le32(header+8, pts >> 32); + + (void) fwrite(header, 1, 12, outfile); +} + +int main(int argc, char **argv) { + FILE *infile, *outfile; + vpx_codec_ctx_t codec; + vpx_codec_enc_cfg_t cfg; + int frame_cnt = 0; + vpx_image_t raw; + vpx_codec_err_t res; + long width; + long height; + int frame_avail; + int got_data; + int flags = 0; + + /* Open files */ + if(argc!=5) + die("Usage: %s <width> <height> <infile> <outfile>\n", argv[0]); + width = strtol(argv[1], NULL, 0); + height = strtol(argv[2], NULL, 0); + if(width < 16 || width%2 || height <16 || height%2) + die("Invalid resolution: %ldx%ld", width, height); + if(!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, width, height, 1)) + die("Faile to allocate image", width, height); + if(!(outfile = fopen(argv[4], "wb"))) + die("Failed to open %s for writing", argv[4]); + + printf("Using %s\n",vpx_codec_iface_name(interface)); + + /* Populate encoder configuration */ + res = vpx_codec_enc_config_default(interface, &cfg, 0); + if(res) { + printf("Failed to get config: %s\n", vpx_codec_err_to_string(res)); + return EXIT_FAILURE; + } + + /* Update the default configuration with our settings */ + cfg.rc_target_bitrate = width * height * cfg.rc_target_bitrate + / cfg.g_w / cfg.g_h; + cfg.g_w = width; + cfg.g_h = height; + + write_ivf_file_header(outfile, &cfg, 0); + + + /* Open input file for this encoding pass */ + if(!(infile = fopen(argv[3], "rb"))) + die("Failed to open %s for reading", argv[3]); + + /* Initialize codec */ + if(vpx_codec_enc_init(&codec, interface, &cfg, 0)) + die_codec(&codec, "Failed to initialize encoder"); + + frame_avail = 1; + got_data = 0; + while(frame_avail || got_data) { + vpx_codec_iter_t iter = NULL; + const vpx_codec_cx_pkt_t *pkt; + + frame_avail = read_frame(infile, &raw); + if(vpx_codec_encode(&codec, frame_avail? &raw : NULL, frame_cnt, + 1, flags, VPX_DL_REALTIME)) + die_codec(&codec, "Failed to encode frame"); + got_data = 0; + while( (pkt = vpx_codec_get_cx_data(&codec, &iter)) ) { + got_data = 1; + switch(pkt->kind) { + case VPX_CODEC_CX_FRAME_PKT: + write_ivf_frame_header(outfile, pkt); + (void) fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, + outfile); + break; + default: + break; + } + printf(pkt->kind == VPX_CODEC_CX_FRAME_PKT + && (pkt->data.frame.flags & VPX_FRAME_IS_KEY)? "K":"."); + fflush(stdout); + } + frame_cnt++; + } + printf("\n"); + fclose(infile); + + printf("Processed %d frames.\n",frame_cnt-1); + vpx_img_free(&raw); + if(vpx_codec_destroy(&codec)) + die_codec(&codec, "Failed to destroy codec"); + + /* Try to rewrite the file header with the actual frame count */ + if(!fseek(outfile, 0, SEEK_SET)) + write_ivf_file_header(outfile, &cfg, frame_cnt-1); + fclose(outfile); + return EXIT_SUCCESS; +} |