[xiph-commits] r16420 - trunk/theora/examples
gmaxwell at svn.xiph.org
gmaxwell at svn.xiph.org
Wed Aug 5 10:22:47 PDT 2009
Author: gmaxwell
Date: 2009-08-05 10:22:46 -0700 (Wed, 05 Aug 2009)
New Revision: 16420
Modified:
trunk/theora/examples/Makefile.am
trunk/theora/examples/encoder_example.c
trunk/theora/examples/png2theora.c
Log:
png2theora: New rate control options (two-pass)
Modified: trunk/theora/examples/Makefile.am
===================================================================
--- trunk/theora/examples/Makefile.am 2009-08-05 14:03:13 UTC (rev 16419)
+++ trunk/theora/examples/Makefile.am 2009-08-05 17:22:46 UTC (rev 16420)
@@ -31,7 +31,7 @@
png2theora_SOURCES = png2theora.c
png2theora_CFLAGS = $(OGG_CFLAGS) $(PNG_CFLAGS)
-png2theora_LDADD = $(GETOPT_OBJS) $(LDADD) $(PNG_LIBS)
+png2theora_LDADD = $(GETOPT_OBJS) $(LDADDENC) $(PNG_LIBS)
debug:
$(MAKE) all CFLAGS="@DEBUG@"
Modified: trunk/theora/examples/encoder_example.c
===================================================================
--- trunk/theora/examples/encoder_example.c 2009-08-05 14:03:13 UTC (rev 16419)
+++ trunk/theora/examples/encoder_example.c 2009-08-05 17:22:46 UTC (rev 16420)
@@ -1117,8 +1117,8 @@
actually allocate space for the padding.
This is okay, because the library will never read data from the padded
region.
- This is only currently true of the experimental encoder; do NOT do this
- with the reference encoder.*/
+ This is only currently true of the 1.1 encoder; do NOT do this
+ with the 1.0 encoder.*/
ycbcr[0].width=frame_w;
ycbcr[0].height=frame_h;
ycbcr[0].stride=pic_w;
Modified: trunk/theora/examples/png2theora.c
===================================================================
--- trunk/theora/examples/png2theora.c 2009-08-05 14:03:13 UTC (rev 16419)
+++ trunk/theora/examples/png2theora.c 2009-08-05 17:22:46 UTC (rev 16420)
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <time.h>
#include <math.h>
#include <libgen.h>
#include <sys/types.h>
@@ -36,9 +37,8 @@
#include <png.h>
#include <ogg/ogg.h>
-#include "theora/theora.h"
+#include "theora/theoraenc.h"
-
#define PROGRAM_NAME "png2theora"
#define PROGRAM_VERSION "1.1"
@@ -47,32 +47,46 @@
static int video_fps_denominator = 1;
static int video_aspect_numerator = 0;
static int video_aspect_denominator = 0;
-static int video_rate = 0;
-static int video_quality = 63;
-static int chroma_format = OC_PF_420;
+static int video_rate = -1;
+static int video_quality = -1;
+ogg_uint32_t keyframe_frequency=0;
+int buf_delay=-1;
+int vp3_compatible=0;
+static int chroma_format = TH_PF_420;
-static int theora_initialized = 0;
+static FILE *twopass_file = NULL;
+static int twopass=0;
+static int passno;
static FILE *ogg_fp = NULL;
static ogg_stream_state ogg_os;
+static ogg_packet op;
+static ogg_page og;
+
+static th_enc_ctx *td;
+static th_info ti;
-static theora_state theora_td;
-static theora_info theora_ti;
-
static char *input_filter;
-const char *optstring = "o:hv:\4:\2:V:s:S:f:F:";
+const char *optstring = "o:hv:\4:\2:V:s:S:f:F:ck:d:\1\2\3\4\5\6";
struct option options [] = {
{"output",required_argument,NULL,'o'},
{"help",no_argument,NULL,'h'},
- {"chroma-444",no_argument,NULL,'\4'},
- {"chroma-422",no_argument,NULL,'\2'},
+ {"chroma-444",no_argument,NULL,'\5'},
+ {"chroma-422",no_argument,NULL,'\6'},
{"video-rate-target",required_argument,NULL,'V'},
{"video-quality",required_argument,NULL,'v'},
{"aspect-numerator",required_argument,NULL,'s'},
{"aspect-denominator",required_argument,NULL,'S'},
{"framerate-numerator",required_argument,NULL,'f'},
{"framerate-denominator",required_argument,NULL,'F'},
+ {"vp3-compatible",no_argument,NULL,'c'},
+ {"soft-target",no_argument,NULL,'\1'},
+ {"keyframe-freq",required_argument,NULL,'k'},
+ {"buf-delay",required_argument,NULL,'d'},
+ {"two-pass",no_argument,NULL,'\2'},
+ {"first-pass",required_argument,NULL,'\3'},
+ {"second-pass",required_argument,NULL,'\4'},
{NULL,0,NULL,0}
};
@@ -84,18 +98,44 @@
" i.e. file-%%06d.png to look for files file000001.png to file9999999.png \n\n"
"Options: \n\n"
" -o --output <filename.ogv> file name for encoded output (required);\n"
- " -V --video-rate-target <n> bitrate target for Theora video\n\n"
" -v --video-quality <n> Theora quality selector fro 0 to 10\n"
" (0 yields smallest files but lowest\n"
" video quality. 10 yields highest\n"
" fidelity but large files)\n\n"
- " --chroma-444 Use 4:4:4 chroma subsampling\n"
- " --chroma-422 Use 4:2:2 chroma subsampling\n"
+ " -V --video-rate-target <n> bitrate target for Theora video\n\n"
+ " --soft-target Use a large reservoir and treat the rate\n"
+ " as a soft target; rate control is less\n"
+ " strict but resulting quality is usually\n"
+ " higher/smoother overall. Soft target also\n"
+ " allows an optional -v setting to specify\n"
+ " a minimum allowed quality.\n\n"
+ " --two-pass Compress input using two-pass rate control\n"
+ " This option performs both passes automatically.\n\n"
+ " --first-pass <filename> Perform first-pass of a two-pass rate\n"
+ " controlled encoding, saving pass data to\n"
+ " <filename> for a later second pass\n\n"
+ " --second-pass <filename> Perform second-pass of a two-pass rate\n"
+ " controlled encoding, reading first-pass\n"
+ " data from <filename>. The first pass\n"
+ " data must come from a first encoding pass\n"
+ " using identical input video to work\n"
+ " properly.\n\n"
+ " -k --keyframe-freq <n> Keyframe frequency\n"
+ " -d --buf-delay <n> Buffer delay (in frames). Longer delays\n"
+ " allow smoother rate adaptation and provide\n"
+ " better overall quality, but require more\n"
+ " client side buffering and add latency. The\n"
+ " default value is the keyframe interval for\n"
+ " one-pass encoding (or somewhat larger if\n"
+ " --soft-target is used) and infinite for\n"
+ " two-pass encoding.\n"
+ " --chroma-444 Use 4:4:4 chroma subsampling\n"
+ " --chroma-422 Use 4:2:2 chroma subsampling\n"
" (4:2:0 is default)\n\n"
- " -s --aspect-numerator <n> Aspect ratio numerator, default is 0\n"
- " -S --aspect-denominator <n> Aspect ratio denominator, default is 0\n"
- " -f --framerate-numerator <n> Frame rate numerator\n"
- " -F --framerate-denominator <n> Frame rate denominator\n"
+ " -s --aspect-numerator <n> Aspect ratio numerator, default is 0\n"
+ " -S --aspect-denominator <n> Aspect ratio denominator, default is 0\n"
+ " -f --framerate-numerator <n> Frame rate numerator\n"
+ " -F --framerate-denominator <n> Frame rate denominator\n"
" The frame rate nominator divided by this\n"
" determines the frame rate in units per tick\n"
,PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_NAME
@@ -103,69 +143,10 @@
exit(0);
}
-
static int
-theora_open(const char *pathname)
-{
- ogg_packet op;
- ogg_page og;
- theora_comment tc;
-
- ogg_fp = fopen(pathname, "wb");
- if(!ogg_fp) {
- fprintf(stderr, "%s: error: %s\n",
- pathname, "couldn't open output file");
- return 1;
- }
-
- if(ogg_stream_init(&ogg_os, rand())) {
- fprintf(stderr, "%s: error: %s\n",
- pathname, "couldn't create ogg stream state");
- return 1;
- }
-
- if(theora_encode_init(&theora_td, &theora_ti)) {
- fprintf(stderr, "%s: error: %s\n",
- pathname, "couldn't initialize theora encoding");
- return 1;
- }
-
- theora_encode_header(&theora_td, &op);
- ogg_stream_packetin(&ogg_os, &op);
- if(ogg_stream_pageout(&ogg_os, &og)) {
- fwrite(og.header, og.header_len, 1, ogg_fp);
- fwrite(og.body, og.body_len, 1, ogg_fp);
- }
- /* libogg flushes automatically after the first header */
-
- theora_comment_init(&tc);
- theora_encode_comment(&tc, &op);
- ogg_stream_packetin(&ogg_os, &op);
- while(ogg_stream_pageout(&ogg_os, &og)) {
- fwrite(og.header, og.header_len, 1, ogg_fp);
- fwrite(og.body, og.body_len, 1, ogg_fp);
- }
-
- theora_encode_tables(&theora_td, &op);
- ogg_stream_packetin(&ogg_os, &op);
- while(ogg_stream_pageout(&ogg_os, &og)) {
- fwrite(og.header, og.header_len, 1, ogg_fp);
- fwrite(og.body, og.body_len, 1, ogg_fp);
- }
-
- /* flush at the end of the headers */
- if(ogg_stream_flush(&ogg_os, &og)) {
- fwrite(og.header, og.header_len, 1, ogg_fp);
- fwrite(og.body, og.body_len, 1, ogg_fp);
- }
-
- return 0;
-}
-
-static int
theora_write_frame(unsigned long w, unsigned long h, unsigned char *yuv, int last)
{
- yuv_buffer yuv_buf;
+ th_ycbcr_buffer ycbcr;
ogg_packet op;
ogg_page og;
@@ -185,28 +166,27 @@
/* Must hold: yuv_h >= h */
yuv_h = (h + 15) & ~15;
- yuv_buf.uv_width = (chroma_format == OC_PF_444) ? yuv_w : (yuv_w >> 1);
- yuv_buf.uv_stride = yuv_buf.uv_width;
- yuv_buf.uv_height = (chroma_format == OC_PF_420) ? (yuv_h >> 1) : yuv_h;
+ ycbcr[0].width = yuv_w;
+ ycbcr[0].height = yuv_h;
+ ycbcr[0].stride = yuv_w;
+ ycbcr[1].width = (chroma_format == TH_PF_444) ? yuv_w : (yuv_w >> 1);
+ ycbcr[1].stride = ycbcr[1].width;
+ ycbcr[1].height = (chroma_format == TH_PF_420) ? (yuv_h >> 1) : yuv_h;
+ ycbcr[2].width = ycbcr[1].width;
+ ycbcr[2].stride = ycbcr[1].stride;
+ ycbcr[2].height = ycbcr[1].height;
- yuv_y = malloc(yuv_w * yuv_h);
- yuv_u = malloc(yuv_buf.uv_width * yuv_buf.uv_height);
- yuv_v = malloc(yuv_buf.uv_width * yuv_buf.uv_height);
+ ycbcr[0].data = yuv_y = malloc(ycbcr[0].stride * ycbcr[0].height);
+ ycbcr[1].data = yuv_u = malloc(ycbcr[1].stride * ycbcr[1].height);
+ ycbcr[2].data = yuv_v = malloc(ycbcr[2].stride * ycbcr[2].height);
- yuv_buf.y_width = yuv_w;
- yuv_buf.y_height = yuv_h;
- yuv_buf.y_stride = yuv_w;
- yuv_buf.y = yuv_y;
- yuv_buf.u = yuv_u;
- yuv_buf.v = yuv_v;
-
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
yuv_y[x + y * yuv_w] = yuv[3 * (x + y * w) + 0];
}
}
- if (chroma_format == OC_PF_420) {
+ if (chroma_format == TH_PF_420) {
for(y = 0; y < h; y += 2) {
for(x = 0; x < w; x += 2) {
yuv_u[(x >> 1) + (y >> 1) * (yuv_w >> 1)] =
@@ -215,41 +195,94 @@
yuv[3 * (x + y * w) + 2];
}
}
- } else if (chroma_format == OC_PF_444) {
+ } else if (chroma_format == TH_PF_444) {
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
- yuv_u[x + y * yuv_buf.uv_stride] = yuv[3 * (x + y * w) + 1];
- yuv_v[x + y * yuv_buf.uv_stride] = yuv[3 * (x + y * w) + 2];
+ yuv_u[x + y * ycbcr[1].stride] = yuv[3 * (x + y * w) + 1];
+ yuv_v[x + y * ycbcr[2].stride] = yuv[3 * (x + y * w) + 2];
}
}
- } else { /* OC_PF_422 */
+ } else { /* TH_PF_422 */
for(y = 0; y < h; y += 1) {
for(x = 0; x < w; x += 2) {
- yuv_u[(x >> 1) + y * yuv_buf.uv_stride] =
+ yuv_u[(x >> 1) + y * ycbcr[1].stride] =
yuv[3 * (x + y * w) + 1];
- yuv_v[(x >> 1) + y * yuv_buf.uv_stride] =
+ yuv_v[(x >> 1) + y * ycbcr[2].stride] =
yuv[3 * (x + y * w) + 2];
}
}
}
- if(theora_encode_YUVin(&theora_td, &yuv_buf)) {
+ /* Theora is a one-frame-in,one-frame-out system; submit a frame
+ for compression and pull out the packet */
+ /* in two-pass mode's second pass, we need to submit first-pass data */
+ if(passno==2){
+ int ret;
+ for(;;){
+ static unsigned char buffer[80];
+ static int buf_pos;
+ int bytes;
+ /*Ask the encoder how many bytes it would like.*/
+ bytes=th_encode_ctl(td,TH_ENCCTL_2PASS_IN,NULL,0);
+ if(bytes<0){
+ fprintf(stderr,"Error submitting pass data in second pass.\n");
+ exit(1);
+ }
+ /*If it's got enough, stop.*/
+ if(bytes==0)break;
+ /*Read in some more bytes, if necessary.*/
+ if(bytes>80-buf_pos)bytes=80-buf_pos;
+ if(bytes>0&&fread(buffer+buf_pos,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Could not read frame data from two-pass data file!\n");
+ exit(1);
+ }
+ /*And pass them off.*/
+ ret=th_encode_ctl(td,TH_ENCCTL_2PASS_IN,buffer,bytes);
+ if(ret<0){
+ fprintf(stderr,"Error submitting pass data in second pass.\n");
+ exit(1);
+ }
+ /*If the encoder consumed the whole buffer, reset it.*/
+ if(ret>=bytes)buf_pos=0;
+ /*Otherwise remember how much it used.*/
+ else buf_pos+=ret;
+ }
+ }
+
+ if(th_encode_ycbcr_in(td, ycbcr)) {
fprintf(stderr, "%s: error: could not encode frame\n",
option_output);
return 1;
}
- if(!theora_encode_packetout(&theora_td, last, &op)) {
+ /* in two-pass mode's first pass we need to extract and save the pass data */
+ if(passno==1){
+ unsigned char *buffer;
+ int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer));
+ if(bytes<0){
+ fprintf(stderr,"Could not read two-pass data from encoder.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(twopass_file);
+ }
+
+ if(!th_encode_packetout(td, last, &op)) {
fprintf(stderr, "%s: error: could not read packets\n",
option_output);
return 1;
}
- ogg_stream_packetin(&ogg_os, &op);
- while(ogg_stream_pageout(&ogg_os, &og)) {
- fwrite(og.header, og.header_len, 1, ogg_fp);
- fwrite(og.body, og.body_len, 1, ogg_fp);
- }
+ if (passno!=1) {
+ ogg_stream_packetin(&ogg_os, &op);
+ while(ogg_stream_pageout(&ogg_os, &og)) {
+ fwrite(og.header, og.header_len, 1, ogg_fp);
+ fwrite(og.body, og.body_len, 1, ogg_fp);
+ }
+ }
free(yuv_y);
free(yuv_u);
@@ -258,33 +291,6 @@
return 0;
}
-static void
-theora_close(void)
-{
- ogg_packet op;
- ogg_page og;
-
- if (theora_initialized) {
- theora_encode_packetout(&theora_td, 1, &op);
- while(ogg_stream_pageout(&ogg_os, &og)) {
- fwrite(og.header, og.header_len, 1, ogg_fp);
- fwrite(og.body, og.body_len, 1, ogg_fp);
- }
- if(ogg_stream_flush(&ogg_os, &og)) {
- fwrite(og.header, og.header_len, 1, ogg_fp);
- fwrite(og.body, og.body_len, 1, ogg_fp);
- }
-
- theora_info_clear(&theora_ti);
- theora_clear(&theora_td);
-
- fflush(ogg_fp);
- fclose(ogg_fp);
- }
-
- ogg_stream_clear(&ogg_os);
-}
-
static unsigned char
clamp(double d)
{
@@ -359,8 +365,6 @@
return 1;
}
- fprintf(stderr, "%s\n", pathname);
-
fread(header, 1, 8, fp);
if(png_sig_cmp(header, 0, 8)) {
fprintf(stderr, "%s: error: %s\n",
@@ -445,6 +449,12 @@
return !strcmp(name, de->d_name);
}
+static int ilog(unsigned _v){
+ int ret;
+ for(ret=0;_v;ret++)_v>>=1;
+ return ret;
+}
+
int
main(int argc, char *argv[])
{
@@ -453,8 +463,11 @@
char *input_mask;
char *input_directory;
char *scratch;
+ th_comment tc;
struct dirent **png_files;
-
+ int soft_target=0;
+ int ret;
+
while(1) {
c=getopt_long(argc,argv,optstring,options,&long_option_index);
@@ -484,6 +497,27 @@
}
video_quality=0;
break;
+ case '\1':
+ soft_target=1;
+ break;
+ case 'c':
+ vp3_compatible=1;
+ break;
+ case 'k':
+ keyframe_frequency=rint(atof(optarg));
+ if(keyframe_frequency<1 || keyframe_frequency>2147483647){
+ fprintf(stderr,"Illegal keyframe frequency\n");
+ exit(1);
+ }
+ break;
+
+ case 'd':
+ buf_delay=atoi(optarg);
+ if(buf_delay<=0){
+ fprintf(stderr,"Illegal buffer delay\n");
+ exit(1);
+ }
+ break;
case 's':
video_aspect_numerator=rint(atof(optarg));
break;
@@ -496,12 +530,36 @@
case 'F':
video_fps_denominator=rint(atof(optarg));
break;
- case '\2':
- chroma_format=OC_PF_422;
+ case '\5':
+ chroma_format=TH_PF_444;
break;
- case '\4':
- chroma_format=OC_PF_444;
+ case '\6':
+ chroma_format=TH_PF_422;
break;
+ case '\2':
+ twopass=3; /* perform both passes */
+ twopass_file=tmpfile();
+ if(!twopass_file){
+ fprintf(stderr,"Unable to open temporary file for twopass data\n");
+ exit(1);
+ }
+ break;
+ case '\3':
+ twopass=1; /* perform first pass */
+ twopass_file=fopen(optarg,"wb");
+ if(!twopass_file){
+ fprintf(stderr,"Unable to open \'%s\' for twopass data\n",optarg);
+ exit(1);
+ }
+ break;
+ case '\4':
+ twopass=2; /* perform second pass */
+ twopass_file=fopen(optarg,"rb");
+ if(!twopass_file){
+ fprintf(stderr,"Unable to open twopass data file \'%s\'",optarg);
+ exit(1);
+ }
+ break;
default:
usage();
break;
@@ -512,6 +570,26 @@
usage();
}
+ if(soft_target){
+ if(video_rate<=0){
+ fprintf(stderr,"Soft rate target (--soft-tagret) requested without a bitrate (-V).\n");
+ exit(1);
+ }
+ if(video_quality==-1)
+ video_quality=0;
+ }else{
+ if(video_rate>0)
+ video_quality=0;
+ if(video_quality==-1)
+ video_quality=48;
+ }
+
+ if(keyframe_frequency<=0){
+ /*Use a default keyframe frequency of 64 for 1-pass (streaming) mode, and
+ 256 for two-pass mode.*/
+ keyframe_frequency=twopass?256:64;
+ }
+
input_mask = argv[optind];
if (!input_mask) {
fprintf(stderr, "no input files specified; run with -h for help.\n");
@@ -530,67 +608,262 @@
input_directory, input_filter);
#endif
n = scandir (input_directory, &png_files, include_files, alphasort);
- for(i=0;i<n;i++) {
+
+ if (!n) {
+ fprintf(stderr, "no input files found; run with -h for help.\n");
+ exit(1);
+ }
+
+ ogg_fp = fopen(option_output, "wb");
+ if(!ogg_fp) {
+ fprintf(stderr, "%s: error: %s\n",
+ option_output, "couldn't open output file");
+ return 1;
+ }
+
+ srand(time(NULL));
+ if(ogg_stream_init(&ogg_os, rand())) {
+ fprintf(stderr, "%s: error: %s\n",
+ option_output, "couldn't create ogg stream state");
+ return 1;
+ }
+
+ for(passno=(twopass==3?1:twopass);passno<=(twopass==3?2:twopass);passno++){
unsigned int w;
unsigned int h;
unsigned char *yuv;
char input_png[1024];
int last = 0;
- sprintf(input_png, "%s/%s", input_directory, png_files[i]->d_name);
-
+ snprintf(input_png, 1023,"%s/%s", input_directory, png_files[0]->d_name);
if(png_read(input_png, &w, &h, &yuv)) {
fprintf(stderr, "could not read %s\n", input_png);
- theora_close();
exit(1);
}
- if(!theora_initialized) {
- theora_info_init(&theora_ti);
+ if (passno!=2) fprintf(stderr,"%d frames, %dx%d\n",n,w,h);
- theora_ti.width = ((w + 15) >>4)<<4;
- theora_ti.height = ((h + 15)>>4)<<4;
- theora_ti.frame_width = w;
- theora_ti.frame_height = h;
- theora_ti.offset_x = 0;
- theora_ti.offset_y = 0;
- theora_ti.fps_numerator = video_fps_numerator;
- theora_ti.fps_denominator = video_fps_denominator;
- theora_ti.aspect_numerator = video_aspect_numerator;
- theora_ti.aspect_denominator = video_aspect_denominator;
- theora_ti.colorspace = OC_CS_UNSPECIFIED;
- theora_ti.pixelformat = chroma_format;
- theora_ti.target_bitrate = video_rate;
- theora_ti.quality = video_quality;
+ /* setup complete. Raw processing loop */
+ switch(passno){
+ case 0: case 2:
+ fprintf(stderr,"\rCompressing.... \n");
+ break;
+ case 1:
+ fprintf(stderr,"\rScanning first pass.... \n");
+ break;
+ }
- theora_ti.dropframes_p = 0;
- theora_ti.quick_p = 1;
- theora_ti.keyframe_auto_p = 1;
- theora_ti.keyframe_frequency = 64;
- theora_ti.keyframe_frequency_force = 64;
- theora_ti.keyframe_data_target_bitrate = video_rate * 1.5;
- theora_ti.keyframe_mindistance = 8;
- theora_ti.noise_sensitivity = 1;
+ fprintf(stderr, "%s\n", input_png);
- if(theora_open(option_output)) {
- /* XXX: cleanup */
- return 1;
- }
+ th_info_init(&ti);
+ ti.frame_width = ((w + 15) >>4)<<4;
+ ti.frame_height = ((h + 15)>>4)<<4;
+ ti.pic_width = w;
+ ti.pic_height = h;
+ ti.pic_x = 0;
+ ti.pic_y = 0;
+ ti.fps_numerator = video_fps_numerator;
+ ti.fps_denominator = video_fps_denominator;
+ ti.aspect_numerator = video_aspect_numerator;
+ ti.aspect_denominator = video_aspect_denominator;
+ ti.colorspace = TH_CS_UNSPECIFIED;
+ ti.pixel_fmt = chroma_format;
+ ti.target_bitrate = video_rate;
+ ti.quality = video_quality;
+ ti.keyframe_granule_shift=ilog(keyframe_frequency-1);
- theora_initialized = 1;
+ td=th_encode_alloc(&ti);
+ th_info_clear(&ti);
+ /* setting just the granule shift only allows power-of-two keyframe
+ spacing. Set the actual requested spacing. */
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
+ &keyframe_frequency,sizeof(keyframe_frequency-1));
+ if(ret<0){
+ fprintf(stderr,"Could not set keyframe interval to %d.\n",(int)keyframe_frequency);
}
+ if(vp3_compatible){
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_VP3_COMPATIBLE,&vp3_compatible,
+ sizeof(vp3_compatible));
+ if(ret<0||!vp3_compatible){
+ fprintf(stderr,"Could not enable strict VP3 compatibility.\n");
+ if(ret>=0){
+ fprintf(stderr,"Ensure your source format is supported by VP3.\n");
+ fprintf(stderr,
+ "(4:2:0 pixel format, width and height multiples of 16).\n");
+ }
+ }
+ }
+ if(soft_target){
+ /* reverse the rate control flags to favor a 'long time' strategy */
+ int arg = TH_RATECTL_CAP_UNDERFLOW;
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_FLAGS,&arg,sizeof(arg));
+ if(ret<0)
+ fprintf(stderr,"Could not set encoder flags for --soft-target\n");
+ /* Default buffer control is overridden on two-pass */
+ if(!twopass&&buf_delay<0){
+ if((keyframe_frequency*7>>1) > 5*video_fps_numerator/video_fps_denominator)
+ arg=keyframe_frequency*7>>1;
+ else
+ arg=30*video_fps_numerator/video_fps_denominator;
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_BUFFER,&arg,sizeof(arg));
+ if(ret<0)
+ fprintf(stderr,"Could not set rate control buffer for --soft-target\n");
+ }
+ }
+ /* set up two-pass if needed */
+ if(passno==1){
+ unsigned char *buffer;
+ int bytes;
+ bytes=th_encode_ctl(td,TH_ENCCTL_2PASS_OUT,&buffer,sizeof(buffer));
+ if(bytes<0){
+ fprintf(stderr,"Could not set up the first pass of two-pass mode.\n");
+ fprintf(stderr,"Did you remember to specify an estimated bitrate?\n");
+ exit(1);
+ }
+ /*Perform a seek test to ensure we can overwrite this placeholder data at
+ the end; this is better than letting the user sit through a whole
+ encode only to find out their pass 1 file is useless at the end.*/
+ if(fseek(twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(twopass_file);
+ }
+ if(passno==2){
+ /*Enable the second pass here.
+ We make this call just to set the encoder into 2-pass mode, because
+ by default enabling two-pass sets the buffer delay to the whole file
+ (because there's no way to explicitly request that behavior).
+ If we waited until we were actually encoding, it would overwite our
+ settings.*/
+ if(th_encode_ctl(td,TH_ENCCTL_2PASS_IN,NULL,0)<0){
+ fprintf(stderr,"Could not set up the second pass of two-pass mode.\n");
+ exit(1);
+ }
+ if(twopass==3){
+ if(fseek(twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ }
+ }
+ /*Now we can set the buffer delay if the user requested a non-default one
+ (this has to be done after two-pass is enabled).*/
+ if(passno!=1&&buf_delay>=0){
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_BUFFER,
+ &buf_delay,sizeof(buf_delay));
+ if(ret<0){
+ fprintf(stderr,"Warning: could not set desired buffer delay.\n");
+ }
+ }
+ /* write the bitstream header packets with proper page interleave */
+ th_comment_init(&tc);
+ /* first packet will get its own page automatically */
+ if(th_encode_flushheader(td,&tc,&op)<=0){
+ fprintf(stderr,"Internal Theora library error.\n");
+ exit(1);
+ }
+ th_comment_clear(&tc);
+ if(passno!=1){
+ ogg_stream_packetin(&ogg_os,&op);
+ if(ogg_stream_pageout(&ogg_os,&og)!=1){
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ fwrite(og.header,1,og.header_len,ogg_fp);
+ fwrite(og.body,1,og.body_len,ogg_fp);
+ }
+ /* create the remaining theora headers */
+ for(;;){
+ ret=th_encode_flushheader(td,&tc,&op);
+ if(ret<0){
+ fprintf(stderr,"Internal Theora library error.\n");
+ exit(1);
+ }
+ else if(!ret)break;
+ if(passno!=1)ogg_stream_packetin(&ogg_os,&op);
+ }
+ /* Flush the rest of our headers. This ensures
+ the actual data in each stream will start
+ on a new page, as per spec. */
+ if(passno!=1){
+ for(;;){
+ int result = ogg_stream_flush(&ogg_os,&og);
+ if(result<0){
+ /* can't get here */
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ if(result==0)break;
+ fwrite(og.header,1,og.header_len,ogg_fp);
+ fwrite(og.body,1,og.body_len,ogg_fp);
+ }
+ }
- if(i >= n-1) last = 1;
- if(theora_write_frame(w, h, yuv, last)) {
- theora_close();
- free(input_directory);
- free(input_filter);
- exit(1);
+ i=0; last=0;
+ do {
+ if(i >= n-1) last = 1;
+ if(theora_write_frame(w, h, yuv, last)) {
+ goto close;
+ exit(1);
+ }
+ free(yuv);
+ i++;
+ if (!last) {
+ snprintf(input_png, 1023,"%s/%s", input_directory, png_files[i]->d_name);
+ if(png_read(input_png, &w, &h, &yuv)) {
+ fprintf(stderr, "could not read %s\n", input_png);
+ exit(1);
+ }
+ fprintf(stderr, "%s\n", input_png);
+ }
+ } while (!last);
+
+ if(passno==1){
+ /* need to read the final (summary) packet */
+ unsigned char *buffer;
+ int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer));
+ if(bytes<0){
+ fprintf(stderr,"Could not read two-pass summary data from encoder.\n");
+ exit(1);
+ }
+ if(fseek(twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(twopass_file);
}
+ th_encode_free(td);
+ }
- free(yuv);
+close:
+ if(ogg_stream_flush(&ogg_os, &og)) {
+ fwrite(og.header, og.header_len, 1, ogg_fp);
+ fwrite(og.body, og.body_len, 1, ogg_fp);
+ }
+
+ free(input_directory);
+ free(input_filter);
+
+ while (n--) free(png_files[n]);
+ free(png_files);
+
+ if(ogg_fp){
+ fflush(ogg_fp);
+ if(ogg_fp!=stdout)fclose(ogg_fp);
}
- theora_close();
+ ogg_stream_clear(&ogg_os);
+ if(twopass_file)fclose(twopass_file);
+ fprintf(stderr,"\r \ndone.\n\n");
+
return 0;
}
More information about the commits
mailing list