[xiph-commits] r16353 - in trunk/ffmpeg2theora: . src
j at svn.xiph.org
j at svn.xiph.org
Tue Jul 28 13:08:40 PDT 2009
Author: j
Date: 2009-07-28 13:08:40 -0700 (Tue, 28 Jul 2009)
New Revision: 16353
Modified:
trunk/ffmpeg2theora/ChangeLog
trunk/ffmpeg2theora/src/ffmpeg2theora.c
trunk/ffmpeg2theora/src/ffmpeg2theora.h
trunk/ffmpeg2theora/src/theorautils.c
trunk/ffmpeg2theora/src/theorautils.h
Log:
add 2 pass encoding
Modified: trunk/ffmpeg2theora/ChangeLog
===================================================================
--- trunk/ffmpeg2theora/ChangeLog 2009-07-28 14:44:15 UTC (rev 16352)
+++ trunk/ffmpeg2theora/ChangeLog 2009-07-28 20:08:40 UTC (rev 16353)
@@ -7,6 +7,8 @@
- update to ffmpeg trunk and new ffmepg api
- use new libtheora encoding api
add new encoding options --soft-target, --buf-delay
+ - two pass encoding, --two-pass
+ or in two calls with --first-pass and --second-pass
0.24 2009-03-12
- fix to make --max_size --no_upscaling work
Modified: trunk/ffmpeg2theora/src/ffmpeg2theora.c
===================================================================
--- trunk/ffmpeg2theora/src/ffmpeg2theora.c 2009-07-28 14:44:15 UTC (rev 16352)
+++ trunk/ffmpeg2theora/src/ffmpeg2theora.c 2009-07-28 20:08:40 UTC (rev 16353)
@@ -53,6 +53,9 @@
NULL_FLAG,
DEINTERLACE_FLAG,
SOFTTARGET_FLAG,
+ TWOPASS_FLAG,
+ FIRSTPASS_FLAG,
+ SECONDPASS_FLAG,
OPTIMIZE_FLAG,
SYNC_FLAG,
NOAUDIO_FLAG,
@@ -178,7 +181,7 @@
this->picture_height=0; // set to 0 to not resize the output
this->video_quality=-1; // defaults set later
this->video_bitrate=0;
- this->keyint=64;
+ this->keyint=0;
this->force_input_fps.num = -1;
this->force_input_fps.den = 1;
this->sync=0;
@@ -764,7 +767,7 @@
this->picture_width, this->picture_height, this->pix_fmt,
sws_flags, NULL, NULL, NULL
);
- if (!info.frontend) {
+ if (!info.frontend && !(info.twopass==3 && info.passno==2)) {
if (this->frame_topBand || this->frame_bottomBand ||
this->frame_leftBand || this->frame_rightBand ||
this->picture_width != (display_width-this->frame_leftBand - this->frame_rightBand) ||
@@ -1021,11 +1024,15 @@
info.speed_level = max_speed_level;
th_encode_ctl(info.td, TH_ENCCTL_SET_SPLEVEL, &info.speed_level, sizeof(int));
}
- if(this->buf_delay >= 0){
+ if(info.passno!=1 && this->buf_delay >= 0){
+ int arg = this->buf_delay;
ret = th_encode_ctl(info.td, TH_ENCCTL_SET_RATE_BUFFER,
&this->buf_delay, sizeof(this->buf_delay));
+ if (this->buf_delay != arg)
+ fprintf(stderr, "Warning: could not set desired buffer delay of %d, using %d instead.\n",
+ arg, this->buf_delay);
if(ret < 0){
- fprintf(stderr, "Warning: could not set desired buffer delay. %d\n", ret);
+ fprintf(stderr, "Warning: could not set desired buffer delay.\n");
}
}
/* setting just the granule shift only allows power-of-two keyframe
@@ -1042,16 +1049,58 @@
if(ret<0)
fprintf(stderr, "Could not set encoder flags for --soft-target\n");
/* Default buffer control is overridden on two-pass */
- if(this->buf_delay<0){
- if((this->keyint*7>>1)>5*this->framerate_new.num/this->framerate_new.den)
- arg = this->keyint*7>>1;
- else
- arg = 30*this->framerate_new.num/this->framerate_new.den;
- ret = th_encode_ctl(info.td, TH_ENCCTL_SET_RATE_BUFFER, &arg,sizeof(arg));
- if(ret<0)
- fprintf(stderr, "Could not set rate control buffer for --soft-target\n");
+ if(!info.twopass && this->buf_delay<0){
+ if((this->keyint*7>>1)>5*this->framerate_new.num/this->framerate_new.den)
+ arg = this->keyint*7>>1;
+ else
+ arg = 30*this->framerate_new.num/this->framerate_new.den;
+ ret = th_encode_ctl(info.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(info.passno==1){
+ unsigned char *buffer;
+ int bytes;
+ bytes=th_encode_ctl(info.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(info.twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,info.twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(info.twopass_file);
+ }
+ if(info.passno==2){
+ /* enable second pass here, actual data feeding comes later */
+ if(th_encode_ctl(info.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(info.twopass==3){
+ /* 'automatic' second pass */
+ if(av_seek_frame( this->context, -1, (int64_t)AV_TIME_BASE*this->start_time, 1)<0){
+ fprintf(stderr,"Could not rewind video input file for second pass!\n");
+ exit(1);
+ }
+ if(fseek(info.twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ }
+ }
+
}
/* audio settings here */
info.channels = this->channels;
@@ -1302,7 +1351,8 @@
}
}
}
- if ((audio_eos && !audio_done) || (ret >= 0 && pkt.stream_index == this->audio_index)) {
+ if (info.passno!=1)
+ if ((audio_eos && !audio_done) || (ret >= 0 && pkt.stream_index == this->audio_index)) {
this->pts_offset = (double) pkt.pts / AV_TIME_BASE -
(double) this->sample_count / this->sample_rate;
while((audio_eos && !audio_done) || avpkt.size > 0 ) {
@@ -1352,6 +1402,7 @@
}
}
+ if (info.passno!=1)
if (!this->disable_subtitles && subtitles_enabled[pkt.stream_index] && is_supported_subtitle_stream(this, pkt.stream_index)) {
AVStream *stream=this->context->streams[pkt.stream_index];
AVCodecContext *enc = stream->codec;
@@ -1425,7 +1476,7 @@
}
/* if we have subtitles starting before then, add it */
- if (info.with_kate) {
+ if (info.passno!=1 && info.with_kate) {
double avtime = info.audio_only ? info.audiotime :
info.video_only ? info.videotime :
info.audiotime < info.videotime ? info.audiotime : info.videotime;
@@ -1446,8 +1497,8 @@
}
/* flush out the file */
-
oggmux_flush (&info, video_eos + audio_eos);
+
av_free_packet (&pkt);
} while (ret >= 0 && !(audio_done && video_done));
@@ -1651,6 +1702,19 @@
" 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 requires that the input to the\n"
+ " to the encoder is seekable and performs\n"
+ " 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"
" --optimize optimize video output filesize (slower) (same as speedlevel 0)\n"
" --speedlevel [0 2] encoding is faster with higher values the cost is quality and bandwidth\n"
@@ -1800,7 +1864,9 @@
{"videobitrate",required_argument,NULL,'V'},
{"audioquality",required_argument,NULL,'a'},
{"audiobitrate",required_argument,NULL,'A'},
- {"soft-target",0,&flag,SOFTTARGET_FLAG},
+ {"two-pass",0,&flag,TWOPASS_FLAG},
+ {"first-pass",required_argument,&flag,FIRSTPASS_FLAG},
+ {"second-pass",required_argument,&flag,SECONDPASS_FLAG},
{"keyint",required_argument,NULL,'K'},
{"buf-delay",required_argument,NULL,'d'},
{"deinterlace",0,&flag,DEINTERLACE_FLAG},
@@ -1887,6 +1953,33 @@
convert->soft_target = 1;
flag = -1;
break;
+ case TWOPASS_FLAG:
+ info.twopass = 3;
+ info.twopass_file = tmpfile();
+ if(!info.twopass_file){
+ fprintf(stderr,"Unable to open temporary file for twopass data\n");
+ exit(1);
+ }
+ flag = -1;
+ break;
+ case FIRSTPASS_FLAG:
+ info.twopass = 1;
+ info.twopass_file = fopen(optarg,"wb");
+ if(!info.twopass_file){
+ fprintf(stderr,"Unable to open \'%s\' for twopass data\n", optarg);
+ exit(1);
+ }
+ flag = -1;
+ break;
+ case SECONDPASS_FLAG:
+ info.twopass = 2;
+ info.twopass_file = fopen(optarg,"rb");
+ if(!info.twopass_file){
+ fprintf(stderr,"Unable to open \'%s\' for twopass data\n", optarg);
+ exit(1);
+ }
+ flag = -1;
+ break;
case PP_FLAG:
if (!strcmp(optarg, "help")) {
fprintf(stdout, "%s", pp_help);
@@ -2256,6 +2349,12 @@
exit(1);
}
+ if(convert->keyint <= 0) {
+ /*Use a default keyframe frequency of 64 for 1-pass (streaming) mode, and
+ 256 for two-pass mode.*/
+ convert->keyint = info.twopass?256:64;
+ }
+
if (convert->soft_target) {
if (convert->video_bitrate <= 0) {
fprintf(stderr,"Soft rate target (--soft-tagret) requested without a bitrate (-V).\n");
@@ -2320,13 +2419,15 @@
info.outfile = stdout;
}
else {
- info.outfile = fopen(outputfile_name,"wb");
+ if(info.twopass!=1)
+ info.outfile = fopen(outputfile_name,"wb");
}
#else
if (!strcmp(outputfile_name,"-")) {
snprintf(outputfile_name,sizeof(outputfile_name),"/dev/stdout");
}
- info.outfile = fopen(outputfile_name,"wb");
+ if(info.twopass!=1)
+ info.outfile = fopen(outputfile_name,"wb");
#endif
if (output_json) {
if (using_stdin) {
@@ -2358,7 +2459,7 @@
convert->pts_offset =
(double) convert->context->start_time / AV_TIME_BASE;
- if (!info.outfile) {
+ if (info.twopass!=1 && !info.outfile) {
if (info.frontend)
fprintf(info.frontend, "\"{result\": \"Unable to open output file.\"}\n");
else
@@ -2368,7 +2469,9 @@
if (convert->context->duration != AV_NOPTS_VALUE) {
info.duration = (double)convert->context->duration / AV_TIME_BASE;
}
- ff2theora_output(convert);
+ for(info.passno=(info.twopass==3?1:info.twopass);info.passno<=(info.twopass==3?2:info.twopass);info.passno++){
+ ff2theora_output(convert);
+ }
convert->audio_index = convert->video_index = -1;
}
else{
Modified: trunk/ffmpeg2theora/src/ffmpeg2theora.h
===================================================================
--- trunk/ffmpeg2theora/src/ffmpeg2theora.h 2009-07-28 14:44:15 UTC (rev 16352)
+++ trunk/ffmpeg2theora/src/ffmpeg2theora.h 2009-07-28 20:08:40 UTC (rev 16353)
@@ -103,6 +103,7 @@
int uv_lut_used;
unsigned char y_lut[256];
unsigned char uv_lut[256];
+
}
*ff2theora;
Modified: trunk/ffmpeg2theora/src/theorautils.c
===================================================================
--- trunk/ffmpeg2theora/src/theorautils.c 2009-07-28 14:44:15 UTC (rev 16352)
+++ trunk/ffmpeg2theora/src/theorautils.c 2009-07-28 20:08:40 UTC (rev 16353)
@@ -71,6 +71,10 @@
info->k_page=0;
#endif
+ info->twopass_file = NULL;
+ info->twopass = 0;
+ info->passno = 0;
+
info->with_kate = 0;
info->n_kate_streams = 0;
info->kate_streams = NULL;
@@ -254,7 +258,6 @@
ogg_stream_init (&info->to, rand ()); /* oops, add one ot the above */
}
/* init theora done */
-
/* initialize Vorbis too, if we have audio. */
if (!info->video_only) {
int ret;
@@ -285,7 +288,7 @@
/* audio init done */
/* initialize kate if we have subtitles */
- if (info->with_kate) {
+ if (info->with_kate && info->passno!=1) {
#ifdef HAVE_KATE
int ret, n;
for (n=0; n<info->n_kate_streams; ++n) {
@@ -309,7 +312,7 @@
/* first packet should be skeleton fishead packet, if skeleton is used */
- if (info->with_skeleton) {
+ if (info->with_skeleton && info->passno!=1) {
ogg_stream_init (&info->so, rand());
add_fishead_packet (info);
if (ogg_stream_pageout (&info->so, &og) != 1) {
@@ -337,13 +340,15 @@
fprintf(stderr, "Internal Theora library error.\n");
exit(1);
}
- ogg_stream_packetin(&info->to, &op);
- if(ogg_stream_pageout(&info->to, &og) != 1) {
- fprintf(stderr, "Internal Ogg library error.\n");
- exit(1);
+ if(info->passno!=1){
+ ogg_stream_packetin(&info->to, &op);
+ if(ogg_stream_pageout(&info->to, &og) != 1) {
+ fprintf(stderr, "Internal Ogg library error.\n");
+ exit(1);
+ }
+ fwrite(og.header, 1, og.header_len, info->outfile);
+ fwrite(og.body, 1, og.body_len, info->outfile);
}
- fwrite(og.header, 1, og.header_len, info->outfile);
- fwrite(og.body, 1, og.body_len, info->outfile);
/* create the remaining theora headers */
for(;;){
@@ -353,10 +358,11 @@
exit(1);
}
else if(!ret) break;
- ogg_stream_packetin(&info->to, &op);
+ if(info->passno!=1)
+ ogg_stream_packetin(&info->to, &op);
}
}
- if (!info->video_only) {
+ if (!info->video_only && info->passno!=1) {
ogg_packet header;
ogg_packet header_comm;
ogg_packet header_code;
@@ -378,7 +384,7 @@
}
#ifdef HAVE_KATE
- if (info->with_kate) {
+ if (info->with_kate && info->passno!=1) {
int n;
for (n=0; n<info->n_kate_streams; ++n) {
oggmux_kate_stream *ks=info->kate_streams+n;
@@ -406,7 +412,7 @@
#endif
/* output the appropriate fisbone packets */
- if (info->with_skeleton) {
+ if (info->with_skeleton && info->passno!=1) {
add_fisbone_packet (info);
while (1) {
int result = ogg_stream_flush (&info->so, &og);
@@ -425,7 +431,7 @@
/* Flush the rest of our headers. This ensures
* the actual data in each stream will start
* on a new page, as per spec. */
- while (1 && !info->audio_only) {
+ while (1 && !info->audio_only && info->passno!=1) {
int result = ogg_stream_flush (&info->to, &og);
if (result < 0) {
/* can't get here */
@@ -437,7 +443,7 @@
fwrite (og.header, 1, og.header_len, info->outfile);
fwrite (og.body, 1, og.body_len, info->outfile);
}
- while (1 && !info->video_only) {
+ while (1 && !info->video_only && info->passno!=1) {
int result = ogg_stream_flush (&info->vo, &og);
if (result < 0) {
/* can't get here */
@@ -450,7 +456,7 @@
fwrite (og.body, 1, og.body_len, info->outfile);
}
#ifdef HAVE_KATE
- if (info->with_kate) {
+ if (info->with_kate && info->passno!=1) {
int n;
for (n=0; n<info->n_kate_streams; ++n) {
oggmux_kate_stream *ks=info->kate_streams+n;
@@ -470,7 +476,7 @@
}
#endif
- if (info->with_skeleton) {
+ if (info->with_skeleton && info->passno!=1) {
int result;
/* build and add the e_o_s packet */
@@ -505,12 +511,79 @@
*/
void oggmux_add_video (oggmux_info *info, th_ycbcr_buffer ycbcr, int e_o_s) {
ogg_packet op;
- int r;
+ int r, ret;
+
+ if(info->passno==2){
+ 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(info->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,info->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(info->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;
+ }
+ }
+
th_encode_ycbcr_in(info->td, ycbcr);
+ /* in two-pass mode's first pass we need to extract and save the pass data */
+ if(info->passno==1){
+
+ unsigned char *buffer;
+ int bytes = th_encode_ctl(info->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,info->twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(info->twopass_file);
+ }
+
while (th_encode_packetout (info->td, e_o_s, &op) > 0) {
ogg_stream_packetin (&info->to, &op);
info->v_pkg++;
}
+ if(info->passno==1 && e_o_s){
+ /* need to read the final (summary) packet */
+ unsigned char *buffer;
+ int bytes = th_encode_ctl(info->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(info->twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,info->twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(info->twopass_file);
+ }
}
/**
@@ -641,7 +714,17 @@
int remaining_seconds = (long) remaining % 60;
int remaining_minutes = ((long) remaining / 60) % 60;
int remaining_hours = (long) remaining / 3600;
- if (timebase - last > 0.5) {
+
+ if (info->passno==1) {
+ remaining = time(NULL) - info->start_time;
+ remaining_seconds = (long) remaining % 60;
+ remaining_minutes = ((long) remaining / 60) % 60;
+ remaining_hours = (long) remaining / 3600;
+ fprintf (stderr,"\r Scanning video first pass, time elapsed: %02d:%02d:%02d ",
+ remaining_hours, remaining_minutes, remaining_seconds
+ );
+ }
+ else if (timebase - last > 0.5) {
last = timebase;
if (info->frontend) {
fprintf(info->frontend, "{\"duration\": %lf, \"position\": %.02lf, \"audio_kbps\": %d, \"video_kbps\": %d, \"remaining\": %.02lf}\n",
@@ -652,7 +735,7 @@
);
fflush (info->frontend);
}
- else if (timebase > 0 ) {
+ else if (timebase > 0) {
if (!remaining) {
remaining = time(NULL) - info->start_time;
remaining_seconds = (long) remaining % 60;
@@ -665,11 +748,12 @@
);
}
else {
- fprintf (stderr,"\r %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, ET: %02d:%02d:%02d, est. size: %.01lf MB ",
+ fprintf (stderr,"\r %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, ET: %02d:%02d:%02d, est. size: %.01lf MB ",
hours, minutes, seconds, hundredths,
info->akbps, info->vkbps,
remaining_hours, remaining_minutes, remaining_seconds,
- estimated_size(info, timebase)
+ estimated_size(info, timebase),
+ info->passno
);
}
}
@@ -777,6 +861,10 @@
ogg_page og;
int best;
+ if (info->passno==1) {
+ print_stats(info, info->videotime);
+ return;
+ }
/* flush out the ogg pages to info->outfile */
while (1) {
/* Get pages for both streams, if not already present, and if available.*/
@@ -936,8 +1024,10 @@
if (info->with_skeleton)
ogg_stream_clear (&info->so);
- if (info->outfile && info->outfile != stdout)
+ if (info->passno!=1 && info->outfile && info->outfile != stdout)
fclose (info->outfile);
+ if(info->twopass_file)
+ fclose(info->twopass_file);
if (info->videopage)
free(info->videopage);
Modified: trunk/ffmpeg2theora/src/theorautils.h
===================================================================
--- trunk/ffmpeg2theora/src/theorautils.h 2009-07-28 14:44:15 UTC (rev 16352)
+++ trunk/ffmpeg2theora/src/theorautils.h 2009-07-28 20:08:40 UTC (rev 16353)
@@ -125,6 +125,10 @@
int k_page;
#endif
+ FILE *twopass_file;
+ int twopass;
+ int passno;
+
int n_kate_streams;
oggmux_kate_stream *kate_streams;
}
@@ -140,4 +144,5 @@
extern void oggmux_flush (oggmux_info *info, int e_o_s);
extern void oggmux_close (oggmux_info *info);
+
#endif
More information about the commits
mailing list