[xiph-commits] r14612 - trunk/ffmpeg2theora
j at svn.xiph.org
j at svn.xiph.org
Thu Mar 20 03:46:54 PDT 2008
Author: j
Date: 2008-03-20 03:46:54 -0700 (Thu, 20 Mar 2008)
New Revision: 14612
Added:
trunk/ffmpeg2theora/get_libkate.sh
trunk/ffmpeg2theora/subtitles.txt
Modified:
trunk/ffmpeg2theora/ChangeLog
trunk/ffmpeg2theora/INSTALL
trunk/ffmpeg2theora/Makefile.am
trunk/ffmpeg2theora/README
trunk/ffmpeg2theora/configure.ac
trunk/ffmpeg2theora/ffmpeg2theora.1
trunk/ffmpeg2theora/ffmpeg2theora.c
trunk/ffmpeg2theora/theorautils.c
trunk/ffmpeg2theora/theorautils.h
Log:
add Kate support, subtitles can be added by providing a .srt subtitle.
See http://wiki.xiph.org/index.php/OggKate. Patch from ogg.k.ogg.k
Modified: trunk/ffmpeg2theora/ChangeLog
===================================================================
--- trunk/ffmpeg2theora/ChangeLog 2008-03-20 09:00:03 UTC (rev 14611)
+++ trunk/ffmpeg2theora/ChangeLog 2008-03-20 10:46:54 UTC (rev 14612)
@@ -1,3 +1,10 @@
+0.21 ??
+ - switch default extension to .ogv
+ - enable Ogg Skeleton by default
+ - add kate streams for subtitles (--subtitles, --subtitles-language,
+ subtitles-category, and --subtitles-encoding options)
+ - new presets
+
0.20 2007-11-31
- add postprocessing filters, denoise, deblock, dering
- new preset
Modified: trunk/ffmpeg2theora/INSTALL
===================================================================
--- trunk/ffmpeg2theora/INSTALL 2008-03-20 09:00:03 UTC (rev 14611)
+++ trunk/ffmpeg2theora/INSTALL 2008-03-20 10:46:54 UTC (rev 14612)
@@ -12,6 +12,9 @@
- ffmpeg-trunk <http://ffmpeg.sf.net> by running ./get_ffmpeg_svn.sh
(this script downloads current ffmpeg trunk, runs an appropriate
configure and builds ffmpeg)
+- for subtitles support, libkate <http://code.google.com/p/libkate/>,
+ or by running ./get_libkate.sh (this script downloads libkate, and
+ builds libkate)
if you did not install ffmpeg but want to staticly link it
(recomended by ffmpeg developers) update PKG_CONFIG_PATH to
@@ -49,3 +52,19 @@
undefined symbol: av_read_frame
this is a known limitation of vhooks in ffmpeg.
+
+subtitles suppport
+------------------
+
+Subtitles for multiple languages can be encoded from one or more utf-8
+SubRip format files (.srt) if you have libkate installed. Subtitles will
+be merged into the output Ogg stream as multiplexed Kate streams.
+
+For example, to encode a video with both German and Italian subtitles:
+
+./ffmpeg2theora -o ouput.ogv --subtitles german.srt --subtitles-language de_DE \
+ --subtitles italian.srt --subtitles-language it \
+ input.avi
+
+See subtitles.txt for more information on how to add subtitles to your
+videos.
Modified: trunk/ffmpeg2theora/Makefile.am
===================================================================
--- trunk/ffmpeg2theora/Makefile.am 2008-03-20 09:00:03 UTC (rev 14611)
+++ trunk/ffmpeg2theora/Makefile.am 2008-03-20 10:46:54 UTC (rev 14612)
@@ -7,7 +7,7 @@
bin_PROGRAMS = ffmpeg2theora
ffmpeg2theora_SOURCES = ffmpeg2theora.c theorautils.c
-ffmpeg2theora_LDFLAGS = -L$(prefix)/lib @XIPH_LIBS@ @FFMPEG_LIBS@
-ffmpeg2theora_CFLAGS = @XIPH_CFLAGS@ @FFMPEG_CFLAGS@
+ffmpeg2theora_LDFLAGS = -L$(prefix)/lib @XIPH_LIBS@ @KATE_LIBS@ @FFMPEG_LIBS@
+ffmpeg2theora_CFLAGS = @XIPH_CFLAGS@ @KATE_CFLAGS@ @FFMPEG_CFLAGS@
man_MANS = ffmpeg2theora.1
Modified: trunk/ffmpeg2theora/README
===================================================================
--- trunk/ffmpeg2theora/README 2008-03-20 09:00:03 UTC (rev 14611)
+++ trunk/ffmpeg2theora/README 2008-03-20 10:46:54 UTC (rev 14612)
@@ -14,3 +14,5 @@
there is a export plugin for kino (http://kino.schirmacher.de/)
more info in kino_export/README
+
+ subtitles can be embedded in an ogg stream, see subtitles.txt for more info.
Modified: trunk/ffmpeg2theora/configure.ac
===================================================================
--- trunk/ffmpeg2theora/configure.ac 2008-03-20 09:00:03 UTC (rev 14611)
+++ trunk/ffmpeg2theora/configure.ac 2008-03-20 10:46:54 UTC (rev 14612)
@@ -26,19 +26,31 @@
)
PKG_CHECK_MODULES(XIPH,ogg >= 1.1 vorbis vorbisenc theora >= 1.0beta1)
-
AC_SUBST(XIPH_CFLAGS)
AC_SUBST(XIPH_LIBS)
-PKG_CONFIG_PATH=./ffmpeg:$PKG_CONFIG_PATH
+export PKG_CONFIG_PATH=./libkate/pkg/pkgconfig:$PKG_CONFIG_PATH
+PKG_CHECK_MODULES(KATE,oggkate,
+ KATE_CFLAGS="-DHAVE_KATE -DHAVE_OGGKATE $KATE_CFLAGS",
+ [AC_MSG_RESULT(
+
+. Could not find libkate. Subtitles support will be disabled.
+ You can also run ./get_libkate.sh (for more information see INSTALL)
+ or update PKG_CONFIG_PATH to point to libkate's source folder
+)]
+)
+AC_SUBST(KATE_LIBS)
+AC_SUBST(KATE_CFLAGS)
+
+export PKG_CONFIG_PATH=./ffmpeg:$PKG_CONFIG_PATH
PKG_CHECK_MODULES(FFMPEG, libavformat libavcodec libavdevice libswscale libpostproc, HAVE_FFMPEG=yes,
AC_MSG_ERROR([
-could not find libavformat libavcodec libswscale libpostproc.
-install it
- sudo apt-get install libavformat-dev libavcodec-dev libavdevice-dev libswscale-dev libpostproc-dev
-or update PKG_CONFIG_PATH to point to ffmpegs source folder
-or run ./get_ffmpeg_svn.sh (for more information see INSTALL)
+. Could not find libavformat libavcodec libavdevice libswscale libpostproc.
+ You can install it via
+ sudo apt-get install libavformat-dev libavcodec-dev libavdevice-dev libswscale-dev libpostproc-dev
+ or update PKG_CONFIG_PATH to point to ffmpeg's source folder
+ or run ./get_ffmpeg_svn.sh (for more information see INSTALL)
])
)
AC_SUBST(FFMPEG_CFLAGS)
Modified: trunk/ffmpeg2theora/ffmpeg2theora.1
===================================================================
--- trunk/ffmpeg2theora/ffmpeg2theora.1 2008-03-20 09:00:03 UTC (rev 14611)
+++ trunk/ffmpeg2theora/ffmpeg2theora.1 2008-03-20 10:46:54 UTC (rev 14612)
@@ -148,6 +148,28 @@
Use A/V sync from input container. Since this does not work with
all input format you have to manualy enable it if you have
issues with A/V sync.
+.SS Subtitles options:
+.TP
+.B \-\-subtitles
+Encode subtitles from the given file to a multiplexed Kate stream.
+The input file should be in SubRip (.srt) format, encoded in utf-8,
+unless the --subtitles-encoding option is also given.
+.TP
+.B \-\-subtitles-encoding encoding
+Assumes the corresponding subtitles file is encoded in the given
+encoding (utf-8 and iso-8859-1 (aka latin1) are supported).
+.TP
+.B \-\-subtitles-language language
+Sets the language of the corresponding subtitles stream. This will
+be set in the corresponding Kate stream so a video player may make
+this available to the user for language selection.
+.TP
+.B \-\-subtitles-category category
+Sets the category of the corresponding subtitles stream. This will
+be set in the corresponding Kate stream so a video player may make
+this available to the user for selection. The default category is
+"subtitles". Suggested other categories may include "transcript",
+"commentary", "lyrics", etc.
.SS Metadata options:
.TP
.B \-\-artist
Modified: trunk/ffmpeg2theora/ffmpeg2theora.c
===================================================================
--- trunk/ffmpeg2theora/ffmpeg2theora.c 2008-03-20 09:00:03 UTC (rev 14611)
+++ trunk/ffmpeg2theora/ffmpeg2theora.c 2008-03-20 10:46:54 UTC (rev 14612)
@@ -25,6 +25,7 @@
#include <string.h>
#include <getopt.h>
#include <math.h>
+#include <errno.h>
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
@@ -35,6 +36,10 @@
#include "vorbis/codec.h"
#include "vorbis/vorbisenc.h"
+#ifdef HAVE_KATE
+#include "kate/kate.h"
+#endif
+
#ifdef WIN32
#include "fcntl.h"
#endif
@@ -58,6 +63,10 @@
ASPECT_FLAG,
INPUTFPS_FLAG,
AUDIOSTREAM_FLAG,
+ SUBTITLES_FLAG,
+ SUBTITLES_ENCODING_FLAG,
+ SUBTITLES_LANGUAGE_FLAG,
+ SUBTITLES_CATEGORY_FLAG,
VHOOK_FLAG,
FRONTEND_FLAG,
SPEEDLEVEL_FLAG,
@@ -74,6 +83,12 @@
V2V_PRESET_PADMASTREAM,
} F2T_PRESETS;
+typedef enum {
+ ENC_UNSET,
+ ENC_UTF8,
+ ENC_ISO_8859_1,
+} F2T_ENCODING;
+
#define PAL_HALF_WIDTH 384
#define PAL_HALF_HEIGHT 288
#define NTSC_HALF_WIDTH 320
@@ -87,6 +102,23 @@
static int sws_flags = SWS_BICUBIC;
+typedef struct ff2theora_subtitle{
+ char *text;
+ size_t len;
+ double t0;
+ double t1;
+} ff2theora_subtitle;
+
+typedef struct ff2theora_kate_stream{
+ const char *filename;
+ size_t num_subtitles;
+ ff2theora_subtitle *subtitles;
+ size_t subtitles_count; /* total subtitles output so far */
+ F2T_ENCODING subtitles_encoding;
+ char subtitles_language[16];
+ char subtitles_category[16];
+} ff2theora_kate_stream;
+
typedef struct ff2theora{
AVFormatContext *context;
int video_index;
@@ -142,9 +174,13 @@
double pts_offset; /* between given input pts and calculated output pts */
int64_t frame_count; /* total video frames output so far */
int64_t sample_count; /* total audio samples output so far */
+
+ size_t n_kate_streams;
+ ff2theora_kate_stream *kate_streams;
}
*ff2theora;
+
// gamma lookup table code
// ffmpeg2theora --nosound -f dv -H 32000 -S 0 -v 8 -x 384 -y 288 -G 1.5 input.dv
@@ -157,6 +193,203 @@
static unsigned char y_lut[256];
static unsigned char uv_lut[256];
+#define SUPPORTED_ENCODINGS "utf-8, utf8, iso-8859-1, latin1"
+
+static void report_unknown_subtitle_encoding(const char *name)
+{
+ fprintf(stderr, "Unknown character encoding: %s\n",name);
+ fprintf(stderr, "Valid character encodings are:\n");
+ fprintf(stderr, " " SUPPORTED_ENCODINGS "\n");
+}
+
+static char *fgets2(char *s,size_t sz,FILE *f)
+{
+ char *ret = fgets(s, sz, f);
+ /* fixup DOS newline character */
+ char *ptr=strchr(s, '\r');
+ if (ptr) *ptr='\n';
+ return ret;
+}
+
+#ifndef __GNUC__
+/* Windows doesn't have strcasecmp but stricmp (at least, DOS had)
+ (or was that strcmpi ? Might have been Borland C) */
+#define strcasecmp(s1, s2) stricmp(s1, s2)
+#endif
+
+static double hmsms2s(int h,int m,int s,int ms)
+{
+ return h*3600+m*60+s+ms/1000.0;
+}
+
+/* very simple implementation when no iconv */
+static void convert_subtitle_to_utf8(F2T_ENCODING encoding,unsigned char *text)
+{
+ size_t nbytes;
+ unsigned char *ptr,*newtext;
+
+ if (!text || !*text) return;
+
+ switch (encoding) {
+ case ENC_UNSET:
+ /* we don't know what encoding this is, assume utf-8 and we'll yell if it ain't */
+ break;
+ case ENC_UTF8:
+ /* nothing to do, already in utf-8 */
+ break;
+ case ENC_ISO_8859_1:
+ /* simple, characters above 0x7f are broken in two,
+ and code points map to the iso-8859-1 8 bit codes */
+ nbytes=0;
+ for (ptr=text;*ptr;++ptr) {
+ nbytes++;
+ if (0x80&*ptr) nbytes++;
+ }
+ newtext=(unsigned char*)malloc(1+nbytes);
+ if (!newtext) {
+ fprintf(stderr, "Memory allocation failed - cannot convert text\n");
+ return;
+ }
+ nbytes=0;
+ for (ptr=text;*ptr;++ptr) {
+ if (0x80&*ptr) {
+ newtext[nbytes++]=0xc0|((*ptr)>>6);
+ newtext[nbytes++]=0x80|((*ptr)&0x3f);
+ }
+ else {
+ newtext[nbytes++]=*ptr;
+ }
+ }
+ newtext[nbytes++]=0;
+ memcpy(text,newtext,nbytes);
+ free(newtext);
+ break;
+ default:
+ fprintf(stderr, "ERROR: encoding %d not handled in conversion!\n", encoding);
+ break;
+ }
+}
+
+static int load_subtitles(ff2theora_kate_stream *this)
+{
+#ifdef HAVE_KATE
+ enum { need_id, need_timing, need_text };
+ int need = need_id;
+ int last_seen_id=0;
+ int ret;
+ int id;
+ static char text[4096];
+ int h0,m0,s0,ms0,h1,m1,s1,ms1;
+ double t0,t1;
+ static char str[4096];
+ int warned=0;
+
+ FILE *f = fopen(this->filename, "r");
+ if (!f) {
+ fprintf(stderr,"WARNING - Failed to open subtitles file %s (%s)\n", this->filename, strerror(errno));
+ return -1;
+ }
+
+ /* first, check for a BOM */
+ ret=fread(str,1,3,f);
+ if (ret<3 || memcmp(str,"\xef\xbb\xbf",3)) {
+ /* No BOM, rewind */
+ fseek(f,0,SEEK_SET);
+ }
+
+ fgets2(str,sizeof(str),f);
+ while (!feof(f)) {
+ switch (need) {
+ case need_id:
+ ret=sscanf(str,"%d\n",&id);
+ if (ret!=1) {
+ fprintf(stderr,"WARNING - Syntax error: %s\n",str);
+ fclose(f);
+ return -1;
+ }
+ if (id!=last_seen_id+1) {
+ fprintf(stderr,"WARNING - Error: non consecutive ids: %s\n",str);
+ fclose(f);
+ return -1;
+ }
+ last_seen_id=id;
+ need=need_timing;
+ strcpy(text,"");
+ break;
+ case need_timing:
+ ret=sscanf(str,"%d:%d:%d%*[.,]%d --> %d:%d:%d%*[.,]%d\n",&h0,&m0,&s0,&ms0,&h1,&m1,&s1,&ms1);
+ if (ret!=8) {
+ fprintf(stderr,"WARNING - Syntax error: %s\n",str);
+ fclose(f);
+ return -1;
+ }
+ else {
+ t0=hmsms2s(h0,m0,s0,ms0);
+ t1=hmsms2s(h1,m1,s1,ms1);
+ }
+ need=need_text;
+ break;
+ case need_text:
+ if (*str=='\n') {
+ convert_subtitle_to_utf8(this->subtitles_encoding,(unsigned char*)text);
+ size_t len = strlen(text);
+ this->subtitles = (ff2theora_subtitle*)realloc(this->subtitles, (this->num_subtitles+1)*sizeof(ff2theora_subtitle));
+ if (!this->subtitles) {
+ fprintf(stderr, "Out of memory\n");
+ fclose(f);
+ return -1;
+ }
+ ret=kate_text_validate(kate_utf8,text,len+1);
+ if (ret<0) {
+ if (!warned) {
+ fprintf(stderr,"WARNING: subtitle %s is not valid utf-8\n",text);
+ fprintf(stderr," further invalid subtitles will NOT be flagged\n");
+ warned=1;
+ }
+ }
+ else {
+ /* kill off trailing \n characters */
+ while (len>0) {
+ if (text[len-1]=='\n') text[--len]=0; else break;
+ }
+ this->subtitles[this->num_subtitles].text = (char*)malloc(len+1);
+ memcpy(this->subtitles[this->num_subtitles].text, text, len+1);
+ this->subtitles[this->num_subtitles].len = len;
+ this->subtitles[this->num_subtitles].t0 = t0;
+ this->subtitles[this->num_subtitles].t1 = t1;
+ this->num_subtitles++;
+ }
+ need=need_id;
+ }
+ else {
+ strcat(text,str);
+ }
+ break;
+ }
+ fgets2(str,sizeof(str),f);
+ }
+
+ fclose(f);
+
+ /* fprintf(stderr," %u subtitles loaded.\n", this->num_subtitles); */
+
+ return this->num_subtitles;
+#else
+ return 0;
+#endif
+}
+
+static void free_subtitles(ff2theora this)
+{
+ size_t i,n;
+ for (i=0; i<this->n_kate_streams; ++i) {
+ ff2theora_kate_stream *ks=this->kate_streams+i;
+ for (n=0; n<ks->num_subtitles; ++n) free(ks->subtitles[n].text);
+ free(ks->subtitles);
+ }
+ free(this->kate_streams);
+}
+
static void y_lut_init(unsigned char *lut, double c, double b, double g) {
int i;
double v;
@@ -254,6 +487,72 @@
}
/**
+ * adds a new kate stream structure
+ */
+static void add_kate_stream(ff2theora this){
+ ff2theora_kate_stream *ks;
+ this->kate_streams=(ff2theora_kate_stream*)realloc(this->kate_streams,(this->n_kate_streams+1)*sizeof(ff2theora_kate_stream));
+ ks=&this->kate_streams[this->n_kate_streams++];
+ ks->filename = NULL;
+ ks->num_subtitles = 0;
+ ks->subtitles = 0;
+ ks->subtitles_count = 0; /* denotes not set yet */
+ ks->subtitles_encoding = ENC_UNSET;
+ strcpy(ks->subtitles_language, "");
+ strcpy(ks->subtitles_category, "");
+}
+
+/*
+ * sets the filename of the next subtitles file
+ */
+static void set_subtitles_file(ff2theora this,const char *filename){
+ size_t n;
+ for (n=0; n<this->n_kate_streams;++n) {
+ if (!this->kate_streams[n].filename) break;
+ }
+ if (n==this->n_kate_streams) add_kate_stream(this);
+ this->kate_streams[n].filename = filename;
+}
+
+/*
+ * sets the language of the next subtitles file
+ */
+static void set_subtitles_language(ff2theora this,const char *language){
+ size_t n;
+ for (n=0; n<this->n_kate_streams;++n) {
+ if (!this->kate_streams[n].subtitles_language[0]) break;
+ }
+ if (n==this->n_kate_streams) add_kate_stream(this);
+ strncpy(this->kate_streams[n].subtitles_language, language, 16);
+ this->kate_streams[n].subtitles_language[15] = 0;
+}
+
+/*
+ * sets the category of the next subtitles file
+ */
+static void set_subtitles_category(ff2theora this,const char *category){
+ size_t n;
+ for (n=0; n<this->n_kate_streams;++n) {
+ if (!this->kate_streams[n].subtitles_category[0]) break;
+ }
+ if (n==this->n_kate_streams) add_kate_stream(this);
+ strncpy(this->kate_streams[n].subtitles_category, category, 16);
+ this->kate_streams[n].subtitles_category[15] = 0;
+}
+
+/**
+ * sets the encoding of the next subtitles file
+ */
+static void set_subtitles_encoding(ff2theora this,F2T_ENCODING encoding){
+ size_t n;
+ for (n=0; n<this->n_kate_streams;++n) {
+ if (this->kate_streams[n].subtitles_encoding==ENC_UNSET) break;
+ }
+ if (n==this->n_kate_streams) add_kate_stream(this);
+ this->kate_streams[n].subtitles_encoding = encoding;
+}
+
+/**
* initialize ff2theora with default values
* @return ff2theora struct
*/
@@ -295,6 +594,9 @@
this->frame_leftBand=0;
this->frame_rightBand=0;
+ this->n_kate_streams=0;
+ this->kate_streams=NULL;
+
this->pix_fmt = PIX_FMT_YUV420P;
}
return this;
@@ -703,10 +1005,48 @@
info.sample_rate = this->sample_rate;
info.vorbis_quality = this->audio_quality * 0.1;
info.vorbis_bitrate = this->audio_bitrate;
+ /* subtitles */
+#ifdef HAVE_KATE
+ for (i=0; i<this->n_kate_streams; ++i) {
+ ff2theora_kate_stream *ks = this->kate_streams+i;
+ kate_info *ki = &info.kate_streams[i].ki;
+ if (ks->num_subtitles > 0) {
+ kate_info_init(ki);
+ kate_info_set_language(ki, ks->subtitles_language);
+ kate_info_set_category(ki, ks->subtitles_category[0]?ks->subtitles_category:"subtitles");
+ if(this->force_input_fps) {
+ ki->gps_numerator = 1000000 * (this->fps); /* fps= numerator/denominator */
+ ki->gps_denominator = 1000000;
+ }
+ else {
+ if (this->framerate_new.num > 0) {
+ // new framerate is interger only right now,
+ // so denominator is always 1
+ ki->gps_numerator = this->framerate_new.num;
+ ki->gps_denominator = this->framerate_new.den;
+ }
+ else {
+ ki->gps_numerator=vstream->r_frame_rate.num;
+ ki->gps_denominator = vstream->r_frame_rate.den;
+ }
+ }
+ ki->granule_shift = 32;
+ }
+ }
+#endif
oggmux_init (&info);
/*seek to start time*/
if(this->start_time) {
av_seek_frame( this->context, -1, (int64_t)AV_TIME_BASE*this->start_time, 1);
+ /* discard subtitles by their end time, so we still have those that start before the start time,
+ but end after it */
+ for (i=0; i<this->n_kate_streams; ++i) {
+ ff2theora_kate_stream *ks=this->kate_streams+i;
+ while (ks->subtitles_count < ks->num_subtitles && ks->subtitles[ks->subtitles_count].t1 <= this->start_time) {
+ /* printf("skipping subtitle %u\n", ks->subtitles_count); */
+ ks->subtitles_count++;
+ }
+ }
}
/*check for end time and calculate number of frames to encode*/
no_frames = fps*(this->end_time - this->start_time);
@@ -915,12 +1255,44 @@
}
}
+
+ /* if we have subtitles starting before then, add it */
+ if (info.with_kate) {
+ double avtime = info.audio_only ? info.audiotime :
+ info.video_only ? info.videotime :
+ info.audiotime < info.videotime ? info.audiotime : info.videotime;
+ for (i=0; i<this->n_kate_streams; ++i) {
+ ff2theora_kate_stream *ks = this->kate_streams+i;
+ if (ks->num_subtitles > 0) {
+ ff2theora_subtitle *sub = ks->subtitles+ks->subtitles_count;
+ /* we encode a bit in advance so we're sure to hit the time, the packet will
+ be held till the right time. If we don't do that, we can insert late and
+ oggz-validate moans */
+ while (ks->subtitles_count < ks->num_subtitles && sub->t0-1.0 <= avtime+this->start_time) {
+ int eos = (ks->subtitles_count == ks->num_subtitles-1);
+ oggmux_add_kate_text(&info, i, sub->t0, sub->t1, sub->text, sub->len, eos);
+ ks->subtitles_count++;
+ ++sub;
+ }
+ }
+ }
+ }
+
/* flush out the file */
oggmux_flush (&info, e_o_s);
av_free_packet (&pkt);
}
while (ret >= 0);
+ for (i=0; i<this->n_kate_streams; ++i) {
+ ff2theora_kate_stream *ks = this->kate_streams+i;
+ if (ks->num_subtitles > 0 && ks->subtitles_count<ks->num_subtitles) {
+ double t = (info.videotime<info.audiotime?info.audiotime:info.videotime)+this->start_time;
+ oggmux_add_kate_end_packet(&info, i, t);
+ oggmux_flush (&info, e_o_s);
+ }
+ }
+
oggmux_close (&info);
if(ppContext)
pp_free_context(ppContext);
@@ -932,6 +1304,7 @@
void ff2theora_close (ff2theora this){
/* clear out state */
+ free_subtitles(this);
av_free (this);
}
@@ -1115,6 +1488,13 @@
" not work with all input format you have to manually\n"
" enable it if you have issues with A/V sync\n"
"\n"
+ "Subtitles options:\n"
+ " --subtitles file use subtitles from the given file (SubRip (.srt) format)\n"
+ " --subtitles-encoding encoding set encoding of the subtitles file\n"
+ " supported are " SUPPORTED_ENCODINGS "\n"
+ " --subtitles-language language set subtitles language (de, en_GB, etc)\n"
+ " --subtitles-category category set subtitles category (default \"subtitles\")\n"
+ "\n"
"Metadata options:\n"
" --artist Name of artist (director)\n"
" --title Title\n"
@@ -1135,6 +1515,8 @@
"Examples:\n"
" ffmpeg2theora videoclip.avi (will write output to videoclip.ogv)\n"
"\n"
+ " ffmpeg2theora videoclip.avi subtitles.srt (same, with subtitles)\n"
+ "\n"
" cat something.dv | ffmpeg2theora -f dv -o output.ogv -\n"
"\n"
" Encode a series of images:\n"
@@ -1207,6 +1589,10 @@
{"cropleft",required_argument,&flag,CROPLEFT_FLAG},
{"inputfps",required_argument,&flag,INPUTFPS_FLAG},
{"audiostream",required_argument,&flag,AUDIOSTREAM_FLAG},
+ {"subtitles",required_argument,&flag,SUBTITLES_FLAG},
+ {"subtitles-encoding",required_argument,&flag,SUBTITLES_ENCODING_FLAG},
+ {"subtitles-language",required_argument,&flag,SUBTITLES_LANGUAGE_FLAG},
+ {"subtitles-category",required_argument,&flag,SUBTITLES_CATEGORY_FLAG},
{"starttime",required_argument,NULL,'s'},
{"endtime",required_argument,NULL,'e'},
{"sync",0,&flag,SYNC_FLAG},
@@ -1318,6 +1704,43 @@
case NOSKELETON:
info.with_skeleton=0;
break;
+#ifdef HAVE_KATE
+ case SUBTITLES_FLAG:
+ set_subtitles_file(convert,optarg);
+ flag = -1;
+ info.with_kate=1;
+ break;
+ case SUBTITLES_ENCODING_FLAG:
+ if (!strcmp(optarg,"utf-8")) set_subtitles_encoding(convert,ENC_UTF8);
+ if (!strcmp(optarg,"utf8")) set_subtitles_encoding(convert,ENC_UTF8);
+ else if (!strcmp(optarg,"iso-8859-1")) set_subtitles_encoding(convert,ENC_ISO_8859_1);
+ else if (!strcmp(optarg,"latin1")) set_subtitles_encoding(convert,ENC_ISO_8859_1);
+ else report_unknown_subtitle_encoding(optarg);
+ flag = -1;
+ break;
+ case SUBTITLES_LANGUAGE_FLAG:
+ if (strlen(optarg)>15) {
+ fprintf(stderr, "WARNING - language is limited to 15 characters, and will be truncated\n");
+ }
+ set_subtitles_language(convert,optarg);
+ flag = -1;
+ break;
+ case SUBTITLES_CATEGORY_FLAG:
+ if (strlen(optarg)>15) {
+ fprintf(stderr, "WARNING - category is limited to 15 characters, and will be truncated\n");
+ }
+ set_subtitles_category(convert,optarg);
+ flag = -1;
+ break;
+#else
+ case SUBTITLES_FLAG:
+ case SUBTITLES_ENCODING_FLAG:
+ case SUBTITLES_LANGUAGE_FLAG:
+ case SUBTITLES_CATEGORY_FLAG:
+ fprintf(stderr, "WARNING - Kate support not compiled in, subtitles will not be output\n"
+ " - install libkate and rebuild ffmpeg2theora for subtitle support\n");
+ break;
+#endif
}
}
@@ -1602,6 +2025,18 @@
}
}
+ oggmux_setup_kate_streams(&info, convert->n_kate_streams);
+
+ for (n=0; n<convert->n_kate_streams; ++n) {
+ ff2theora_kate_stream *ks=convert->kate_streams+n;
+ if (load_subtitles(ks)>=0) {
+ printf("Muxing Kate stream %d from %s as %s %s\n",
+ n,ks->filename,
+ ks->subtitles_language[0]?ks->subtitles_language:"<unknown language>",
+ ks->subtitles_category[0]?ks->subtitles_category:"subtitles");
+ }
+ }
+
if (av_open_input_file(&convert->context, inputfile_name, input_fmt, 0, formatParams) >= 0){
if (av_find_stream_info (convert->context) >= 0){
#ifdef WIN32
Added: trunk/ffmpeg2theora/get_libkate.sh
===================================================================
--- trunk/ffmpeg2theora/get_libkate.sh (rev 0)
+++ trunk/ffmpeg2theora/get_libkate.sh 2008-03-20 10:46:54 UTC (rev 14612)
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+version=0.1.0
+baseurl="http://libkate.googlecode.com/files/libkate-$version.tar.gz"
+
+which wget >& /dev/null
+if [ $? -eq 0 ]
+then
+ wget "$baseurl"
+else
+ which curl >& /dev/null
+ if [ $? -eq 0 ]
+ then
+ curl "$baseurl"
+ else
+ echo "Neither wget nor curl were found, cannot download libkate"
+ exit 1
+ fi
+fi
+
+if [ $? -ne 0 ]
+then
+ echo "Failed to download libkate"
+ exit 1
+fi
+
+tar xfz "libkate-$version.tar.gz"
+ln -fs "libkate-$version" libkate
+cd libkate && make
+
Property changes on: trunk/ffmpeg2theora/get_libkate.sh
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/ffmpeg2theora/subtitles.txt
===================================================================
--- trunk/ffmpeg2theora/subtitles.txt (rev 0)
+++ trunk/ffmpeg2theora/subtitles.txt 2008-03-20 10:46:54 UTC (rev 14612)
@@ -0,0 +1,131 @@
+Subtitles can be embedded in an Ogg stream alongside a Theora video.
+
+ * Overview
+ * Subtitles related options
+ * Converting non-utf-8 files to utf-8
+ * Examples
+
+
+
+ * Overview
+
+Subtitles are read from SubRip (.srt) format files and converted to
+Kate streams. Those SubRip files must be encoded in utf-8 (7 bit ASCII
+is a subset of utf-8 so are valid input as well). See below for more
+information on converting SubRip files with other encodings to utf-8.
+
+Subtitles support requires libkate, available from:
+http://code.google.com/p/libkate
+
+A subtitles input file is given with the --subtitles option.
+The language of subtitles in a file is given by the --subtitles-language
+option. See below for a list of subtitles related options. At the most
+simple, supplying a single input file:
+
+ ./ffmpeg2theora -o output.ogv --subtitles input.srt input.avi
+
+Any number of subtitles streams can be multiplexed, presumably with
+different languages and/or categories. See below for examples of this.
+
+
+
+ * Subtitles related options
+
+--subtitles <file>
+ Loads subtitles from a file, which must be an utf-8 encoded SubRip
+ (.srt) file
+
+--subtitles-language <language>
+ Sets the language of the relevant subtitles stream. Language must be
+ a language tag according to RFC 3066 (usually a two letter language
+ code, optionally followed by a dash (or underscore) and a region code.
+ Examples include en, it, ja, en_GB, de_DE, etc.
+ If unspecified, the default is to not set a language.
+
+--subtitles-category <category>
+ Sets the category of the relevant subtitles stream. Category must be
+ a free text string describing the type of the subtitles streams. This
+ is meant to be parsable automatically, so should be ASCII only, and
+ preferably among a list of predefined well known categories, such as
+ subtitles, commentary, transcript, lyrics.
+ If unspecified, the default is subtitles.
+
+--subtitles-encoding <encoding>
+ Sets the encoding of the relevant input file. Allowed encodings are
+ utf-8, utf8, iso-8859-1, and latin1. The first two are synonymous and
+ yield no conversion. The latter two are synonymous and convert from
+ iso-8859-1 to utf-8.
+ If the input file is in another encoding, a separate step is needed
+ to convert the input file to utf-8. See below for more information on
+ converting other encoding to utf-8.
+ If unspecified, the default is utf-8.
+
+
+
+ * Converting non-utf-8 files to utf-8
+
+If you have SubRip files in another format than utf-8, you can use the
+iconv or recode programs to convert them to utf-8 so ffmpeg2theora can
+read them.
+
+ * iconv
+ If you have a file called subtitles.srt which is not in utf-8,
+ you can convert it to utf-8 with the command:
+
+ iconv -t utf-8 -f ENCODING subtitles.srt > subtitles.utf8.srt
+
+ Substitute ENCODING with the actual encoding of the input file.
+ For instance, if your input file is in Shift-JIS encoding, replace
+ ENCODING with SHIFT-JIS. If your input file is in big endian UCS2
+ encoding, replace ENCODING with UCS-2BE.
+
+ This will create a new file called subtitles.utf8.srt, which will
+ be the equivalent of the input file, but in utf-8 format, so it
+ can be used as input to ffmpeg2theora.
+
+ To view a list of all the encodings iconv can convert to utf-8,
+ see the output of `iconv -l'.
+
+ * recode
+
+ If you have a file called subtitles.srt which is not in utf-8,
+ you can convert it to utf-8 with the command:
+
+ recode ENCODING..utf-8 < subtitles.srt > subtitles.utf8.srt
+
+ Substitute ENCODING with the actual encoding of the input file.
+ For instance, if your input file is in Shift-JIS encoding, replace
+ ENCODING with SHIFT-JIS. If your input file is in BIG5 encoding,
+ replace ENCODING with BIG5.
+
+ This will create a new file called subtitles.utf8.srt, which will
+ be the equivalent of the input file, but in utf-8 format, so it
+ can be used as input to ffmpeg2theora.
+
+ To view a list of all the encodings recode can convert to utf-8,
+ see the output of `recode -l'.
+
+
+ * Examples
+
+ Add a single English subtitles stream:
+
+ ./ffmpeg2theora --subtitles-language en --subtitles input.srt input.avi
+
+ Add German and Italian commentary:
+
+ ./ffmpeg2theora --subtitles comm.german.srt --subtitles-language de \
+ --subtitles-category commentary \
+ --subtitles comm.italian.srt --subtitles-language it \
+ --subtitles-category commentary \
+ input.avi
+
+ Add English subtitles and commentary:
+
+ ./ffmpeg2theora --subtitles subs.srt --subtitles-language en \
+ --subtitles-category subtitles \
+ --subtitles commentary.srt --subtitles-language en \
+ --subtitles-category commentary \
+ input.avi
+
+
Modified: trunk/ffmpeg2theora/theorautils.c
===================================================================
--- trunk/ffmpeg2theora/theorautils.c 2008-03-20 09:00:03 UTC (rev 14611)
+++ trunk/ffmpeg2theora/theorautils.c 2008-03-20 10:46:54 UTC (rev 14612)
@@ -28,6 +28,9 @@
#include "theora/theora.h"
#include "vorbis/codec.h"
#include "vorbis/vorbisenc.h"
+#ifdef HAVE_OGGKATE
+#include "kate/oggkate.h"
+#endif
#include "theorautils.h"
@@ -48,6 +51,7 @@
info->audiotime = 0;
info->audio_bytesout = 0;
info->video_bytesout = 0;
+ info->kate_bytesout = 0;
info->videopage_valid = 0;
info->audiopage_valid = 0;
@@ -61,12 +65,60 @@
info->v_pkg=0;
info->a_pkg=0;
+ info->k_pkg=0;
#ifdef OGGMUX_DEBUG
info->a_page=0;
info->v_page=0;
+ info->k_page=0;
#endif
+
+ info->with_kate = 0;
+ info->n_kate_streams = 0;
}
+void oggmux_setup_kate_streams(oggmux_info *info, int n_kate_streams)
+{
+ int n;
+
+ info->n_kate_streams = n_kate_streams;
+ if (n_kate_streams == 0) return;
+ info->kate_streams = (oggmux_kate_stream*)malloc(n_kate_streams*sizeof(oggmux_kate_stream));
+ for (n=0; n<n_kate_streams; ++n) {
+ oggmux_kate_stream *ks=info->kate_streams+n;
+ ks->katepage_valid = 0;
+ ks->katepage_buffer_length = 0;
+ ks->katepage = NULL;
+ ks->katetime = 0;
+ }
+}
+
+static void write16le(unsigned char *ptr,ogg_uint16_t v)
+{
+ ptr[0]=v&0xff;
+ ptr[1]=(v>>8)&0xff;
+}
+
+static void write32le(unsigned char *ptr,ogg_uint32_t v)
+{
+ ptr[0]=v&0xff;
+ ptr[1]=(v>>8)&0xff;
+ ptr[2]=(v>>16)&0xff;
+ ptr[3]=(v>>24)&0xff;
+}
+
+static void write64le(unsigned char *ptr,ogg_int64_t v)
+{
+ ogg_uint32_t hi=v>>32;
+ ptr[0]=v&0xff;
+ ptr[1]=(v>>8)&0xff;
+ ptr[2]=(v>>16)&0xff;
+ ptr[3]=(v>>24)&0xff;
+ ptr[4]=hi&0xff;
+ ptr[5]=(hi>>8)&0xff;
+ ptr[6]=(hi>>16)&0xff;
+ ptr[7]=(hi>>24)&0xff;
+}
+
void add_fishead_packet (oggmux_info *info) {
ogg_packet op;
@@ -77,14 +129,14 @@
memset (op.packet, 0, 64);
memcpy (op.packet, FISHEAD_IDENTIFIER, 8); /* identifier */
- *((ogg_uint16_t*)(op.packet+8)) = SKELETON_VERSION_MAJOR; /* version major */
- *((ogg_uint16_t*)(op.packet+10)) = SKELETON_VERSION_MINOR; /* version minor */
- *((ogg_int64_t*)(op.packet+12)) = (ogg_int64_t)0; /* presentationtime numerator */
- *((ogg_int64_t*)(op.packet+20)) = (ogg_int64_t)1000; /* presentationtime denominator */
- *((ogg_int64_t*)(op.packet+28)) = (ogg_int64_t)0; /* basetime numerator */
- *((ogg_int64_t*)(op.packet+36)) = (ogg_int64_t)1000; /* basetime denominator */
+ write16le(op.packet+8, SKELETON_VERSION_MAJOR); /* version major */
+ write16le(op.packet+10, SKELETON_VERSION_MINOR); /* version minor */
+ write64le(op.packet+12, (ogg_int64_t)0); /* presentationtime numerator */
+ write64le(op.packet+20, (ogg_int64_t)1000); /* presentationtime denominator */
+ write64le(op.packet+28, (ogg_int64_t)0); /* basetime numerator */
+ write64le(op.packet+36, (ogg_int64_t)1000); /* basetime denominator */
/* both the numerator are zero hence handled by the memset */
- *((ogg_uint32_t*)(op.packet+44)) = 0; /* UTC time, set to zero for now */
+ write32le(op.packet+44, 0); /* UTC time, set to zero for now */
op.b_o_s = 1; /* its the first packet of the stream */
op.e_o_s = 0; /* its not the last packet of the stream */
@@ -99,6 +151,7 @@
*/
void add_fisbone_packet (oggmux_info *info) {
ogg_packet op;
+ int n;
if (!info->audio_only) {
memset (&op, 0, sizeof (op));
@@ -108,14 +161,14 @@
memset (op.packet, 0, 82);
/* it will be the fisbone packet for the theora video */
memcpy (op.packet, FISBONE_IDENTIFIER, 8); /* identifier */
- *((ogg_uint32_t*)(op.packet+8)) = FISBONE_MESSAGE_HEADER_OFFSET; /* offset of the message header fields */
- *((ogg_uint32_t*)(op.packet+12)) = info->to.serialno; /* serialno of the theora stream */
- *((ogg_uint32_t*)(op.packet+16)) = 3; /* number of header packets */
+ write32le(op.packet+8, FISBONE_MESSAGE_HEADER_OFFSET); /* offset of the message header fields */
+ write32le(op.packet+12, info->to.serialno); /* serialno of the theora stream */
+ write32le(op.packet+16, 3); /* number of header packets */
/* granulerate, temporal resolution of the bitstream in samples/microsecond */
- *((ogg_int64_t*)(op.packet+20)) = info->ti.fps_numerator; /* granulrate numerator */
- *((ogg_int64_t*)(op.packet+28)) = info->ti.fps_denominator; /* granulrate denominator */
- *((ogg_int64_t*)(op.packet+36)) = 0; /* start granule */
- *((ogg_uint32_t*)(op.packet+44)) = 0; /* preroll, for theora its 0 */
+ write64le(op.packet+20, info->ti.fps_numerator); /* granulrate numerator */
+ write64le(op.packet+28, info->ti.fps_denominator); /* granulrate denominator */
+ write64le(op.packet+36, 0); /* start granule */
+ write32le(op.packet+44, 0); /* preroll, for theora its 0 */
*(op.packet+48) = theora_granule_shift (&info->ti); /* granule shift */
memcpy(op.packet+FISBONE_SIZE, "Content-Type: video/x-theora\r\n", 30); /* message header field, Content-Type */
@@ -135,14 +188,14 @@
memset (op.packet, 0, 82);
/* it will be the fisbone packet for the vorbis audio */
memcpy (op.packet, FISBONE_IDENTIFIER, 8); /* identifier */
- *((ogg_uint32_t*)(op.packet+8)) = FISBONE_MESSAGE_HEADER_OFFSET; /* offset of the message header fields */
- *((ogg_uint32_t*)(op.packet+12)) = info->vo.serialno; /* serialno of the vorbis stream */
- *((ogg_uint32_t*)(op.packet+16)) = 3; /* number of header packet */
+ write32le(op.packet+8, FISBONE_MESSAGE_HEADER_OFFSET); /* offset of the message header fields */
+ write32le(op.packet+12, info->vo.serialno); /* serialno of the vorbis stream */
+ write32le(op.packet+16, 3); /* number of header packet */
/* granulerate, temporal resolution of the bitstream in Hz */
- *((ogg_int64_t*)(op.packet+20)) = info->sample_rate; /* granulerate numerator */
- *((ogg_int64_t*)(op.packet+28)) = (ogg_int64_t)1; /* granulerate denominator */
- *((ogg_int64_t*)(op.packet+36)) = 0; /* start granule */
- *((ogg_uint32_t*)(op.packet+44)) = 2; /* preroll, for vorbis its 2 */
+ write64le(op.packet+20, info->sample_rate); /* granulerate numerator */
+ write64le(op.packet+28, (ogg_int64_t)1); /* granulerate denominator */
+ write64le(op.packet+36, 0); /* start granule */
+ write32le(op.packet+44, 2); /* preroll, for vorbis its 2 */
*(op.packet+48) = 0; /* granule shift, always 0 for vorbis */
memcpy (op.packet+FISBONE_SIZE, "Content-Type: audio/x-vorbis\r\n", 30);
/* Important: Check the case of Content-Type for correctness */
@@ -154,6 +207,37 @@
ogg_stream_packetin (&info->so, &op);
_ogg_free (op.packet);
}
+
+#ifdef HAVE_KATE
+ if (info->with_kate) {
+ for (n=0; n<info->n_kate_streams; ++n) {
+ oggmux_kate_stream *ks=info->kate_streams+n;
+ memset (&op, 0, sizeof (op));
+ op.packet = _ogg_calloc (86, sizeof(unsigned char));
+ memset (op.packet, 0, 86);
+ /* it will be the fisbone packet for the kate stream */
+ memcpy (op.packet, FISBONE_IDENTIFIER, 8); /* identifier */
+ write32le(op.packet+8, FISBONE_MESSAGE_HEADER_OFFSET); /* offset of the message header fields */
+ write32le(op.packet+12, ks->ko.serialno); /* serialno of the vorbis stream */
+ write32le(op.packet+16, ks->ki.num_headers); /* number of header packet */
+ /* granulerate, temporal resolution of the bitstream in Hz */
+ write64le(op.packet+20, ks->ki.gps_numerator); /* granulerate numerator */
+ write64le(op.packet+28, ks->ki.gps_denominator); /* granulerate denominator */
+ write64le(op.packet+36, 0); /* start granule */
+ write32le(op.packet+44, 0); /* preroll, for kate it's 0 */
+ *(op.packet+48) = ks->ki.granule_shift; /* granule shift */
+ memcpy (op.packet+FISBONE_SIZE, "Content-Type: application/x-kate\r\n", 34);
+ /* Important: Check the case of Content-Type for correctness */
+
+ op.b_o_s = 0;
+ op.e_o_s = 0;
+ op.bytes = 86;
+
+ ogg_stream_packetin (&info->so, &op);
+ _ogg_free (op.packet);
+ }
+ }
+#endif
}
void oggmux_init (oggmux_info *info){
@@ -204,6 +288,23 @@
}
/* audio init done */
+ /* initialize kate if we have subtitles */
+ if (info->with_kate) {
+ int ret, n;
+#ifdef HAVE_KATE
+ for (n=0; n<info->n_kate_streams; ++n) {
+ oggmux_kate_stream *ks=info->kate_streams+n;
+ ogg_stream_init (&ks->ko, rand ()); /* oops, add one ot the above */
+ ret = kate_encode_init (&ks->k, &ks->ki);
+ if (ret<0) fprintf(stderr, "kate_encode_init: %d\n",ret);
+ ret = kate_comment_init(&ks->kc);
+ if (ret<0) fprintf(stderr, "kate_comment_init: %d\n",ret);
+ kate_comment_add_tag (&ks->kc, "ENCODER",PACKAGE_STRING);
+ }
+#endif
+ }
+ /* kate init done */
+
/* first packet should be skeleton fishead packet, if skeleton is used */
if (info->with_skeleton) {
@@ -259,6 +360,34 @@
ogg_stream_packetin (&info->vo, &header_code);
}
+#ifdef HAVE_KATE
+ if (info->with_kate) {
+ int n;
+ for (n=0; n<info->n_kate_streams; ++n) {
+ oggmux_kate_stream *ks=info->kate_streams+n;
+ int ret;
+ while (1) {
+ ret=kate_ogg_encode_headers(&ks->k,&ks->kc,&op);
+ if (ret==0) {
+ ogg_stream_packetin(&ks->ko,&op);
+ ogg_packet_clear(&op);
+ }
+ if (ret<0) fprintf(stderr, "kate_encode_headers: %d\n",ret);
+ if (ret>0) break;
+ }
+
+ /* first header is on a separate page - libogg will do it automatically */
+ ret=ogg_stream_pageout (&ks->ko, &og);
+ if (ret!=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);
+ }
+ }
+#endif
+
/* output the appropriate fisbone packets */
if (info->with_skeleton) {
add_fisbone_packet (info);
@@ -307,6 +436,24 @@
fwrite (og.header, 1, og.header_len,info->outfile);
fwrite (og.body, 1, og.body_len, info->outfile);
}
+ if (info->with_kate) {
+ int n;
+ for (n=0; n<info->n_kate_streams; ++n) {
+ oggmux_kate_stream *ks=info->kate_streams+n;
+ while (1) {
+ int result = ogg_stream_flush (&ks->ko, &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,info->outfile);
+ fwrite (og.body, 1, og.body_len, info->outfile);
+ }
+ }
+ }
if (info->with_skeleton) {
int result;
@@ -387,6 +534,66 @@
}
}
+/**
+ * adds a subtitles text to the encoding sink
+ * if e_o_s is 1 the end of the logical bitstream will be marked.
+ * @param info oggmux_info
+ * @param idx which kate stream to output to
+ * @param t0 the show time of the text
+ * @param t1 the hide time of the text
+ * @param text the utf-8 text
+ * @param len the number of bytes in the text
+ * @param e_o_s 1 indicates end of stream
+ */
+void oggmux_add_kate_text (oggmux_info *info, int idx, double t0, double t1, const char *text, size_t len, int e_o_s){
+#ifdef HAVE_KATE
+ ogg_packet op;
+ oggmux_kate_stream *ks=info->kate_streams+idx;
+ int ret;
+ ret = kate_ogg_encode_text(&ks->k, t0, t1, text, len, &op);
+ if (ret>=0) {
+ ogg_stream_packetin (&ks->ko, &op);
+ info->k_pkg++;
+ }
+ else {
+ fprintf(stderr, "Failed to encode kate data packet (%f --> %f, [%s]): %d",
+ t0, t1, text, ret);
+ }
+ if(e_o_s) {
+ ret = kate_ogg_encode_finish(&ks->k, -1, &op);
+ if (ret>=0) {
+ ogg_stream_packetin (&ks->ko, &op);
+ info->k_pkg++;
+ }
+ else {
+ fprintf(stderr, "Failed to encode kate end packet: %d", ret);
+ }
+ }
+#endif
+}
+
+/**
+ * adds a kate end packet to the encoding sink
+ * @param info oggmux_info
+ * @param idx which kate stream to output to
+ * @param t the time of the end packet
+ */
+void oggmux_add_kate_end_packet (oggmux_info *info, int idx, double t){
+#ifdef HAVE_KATE
+ ogg_packet op;
+ oggmux_kate_stream *ks=info->kate_streams+idx;
+ int ret;
+ ret = kate_ogg_encode_finish(&ks->k, t, &op);
+ if (ret>=0) {
+ ogg_stream_packetin (&ks->ko, &op);
+ info->k_pkg++;
+ }
+ else {
+ fprintf(stderr, "Failed to encode kate end packet: %d", ret);
+ }
+#endif
+}
+
static double get_remaining(oggmux_info *info, double timebase) {
double remaining = 0;
double to_encode, time_so_far;
@@ -440,7 +647,7 @@
}
}
-static int write_audio_page(oggmux_info *info)
+static void write_audio_page(oggmux_info *info)
{
int ret;
@@ -465,7 +672,7 @@
print_stats(info, info->audiotime);
}
-static int write_video_page(oggmux_info *info)
+static void write_video_page(oggmux_info *info)
{
int ret;
@@ -491,10 +698,56 @@
print_stats(info, info->videotime);
}
+static void write_kate_page(oggmux_info *info, int idx)
+{
+ int ret;
+ oggmux_kate_stream *ks=info->kate_streams+idx;
+
+ ret = fwrite(ks->katepage, 1, ks->katepage_len, info->outfile);
+ if(ret < ks->katepage_len) {
+ fprintf(stderr,"error writing kate page\n");
+ }
+ else {
+ info->kate_bytesout += ret;
+ }
+ ks->katepage_valid = 0;
+ info->k_pkg -= ogg_page_packets((ogg_page *)&ks->katepage);
+#ifdef OGGMUX_DEBUG
+ ks->k_page++;
+ fprintf(stderr,"\nkate page %d (%d pkgs) | pkg remaining %d\n",ks->k_page,ogg_page_packets((ogg_page *)&info->katepage),info->k_pkg);
+#endif
+
+
+ /*
+ info->kkbps = rint (info->kate_bytesout * 8. / info->katetime * .001);
+ if(info->kkbps<0)
+ info->kkbps=0;
+ print_stats(info, info->katetime);
+ */
+}
+
+static int find_best_valid_kate_page(oggmux_info *info)
+{
+ int n;
+ double t=0.0;
+ int best=-1;
+ if (info->with_kate) for (n=0; n<info->n_kate_streams;++n) {
+ oggmux_kate_stream *ks=info->kate_streams+n;
+ if (ks->katepage_valid) {
+ if (best==-1 || ks->katetime<t) {
+ t=ks->katetime;
+ best=n;
+ }
+ }
+ }
+ return best;
+}
+
void oggmux_flush (oggmux_info *info, int e_o_s)
{
- int len;
+ int n,len;
ogg_page og;
+ int best;
/* flush out the ogg pages to info->outfile */
while(1) {
@@ -554,10 +807,53 @@
}
}
+#ifdef HAVE_KATE
+ if (info->with_kate) for (n=0; n<info->n_kate_streams; ++n) {
+ oggmux_kate_stream *ks=info->kate_streams+n;
+ if (!ks->katepage_valid) {
+ int k_next=0;
+ /* always flush kate stream */
+ if (ogg_stream_flush(&ks->ko, &og) > 0) {
+ k_next = 1;
+ }
+ if (k_next) {
+ len = og.header_len + og.body_len;
+ if(ks->katepage_buffer_length < len) {
+ ks->katepage = realloc(ks->katepage, len);
+ ks->katepage_buffer_length = len;
+ }
+ ks->katepage_len = len;
+ memcpy(ks->katepage, og.header, og.header_len);
+ memcpy(ks->katepage+og.header_len , og.body, og.body_len);
+
+ ks->katepage_valid = 1;
+ if(ogg_page_granulepos(&og)>0) {
+ ks->katetime= kate_granule_time (&ks->ki,
+ ogg_page_granulepos(&og));
+ }
+ }
+ }
+ }
+#endif
+
+#ifdef HAVE_KATE
+#define CHECK_KATE_OUTPUT(which) \
+ if (best>=0 && info->kate_streams[best].katetime/*-1.0*/<=info->which##time) { \
+ write_kate_page(info, best); \
+ continue; \
+ }
+#else
+#define CHECK_KATE_OUTPUT(which) ((void)0)
+#endif
+
+ best=find_best_valid_kate_page(info);
+
if(info->video_only && info->videopage_valid) {
+ CHECK_KATE_OUTPUT(video);
write_video_page(info);
}
else if(info->audio_only && info->audiopage_valid) {
+ CHECK_KATE_OUTPUT(audio);
write_audio_page(info);
}
/* We're using both. We can output only:
@@ -566,11 +862,18 @@
*/
else if(info->videopage_valid && info->audiopage_valid) {
/* Make sure they're in the right order. */
- if(info->videotime <= info->audiotime)
+ if(info->videotime <= info->audiotime) {
+ CHECK_KATE_OUTPUT(video);
write_video_page(info);
- else
+ }
+ else {
+ CHECK_KATE_OUTPUT(audio);
write_audio_page(info);
+ }
}
+ else if(e_o_s && best>=0) {
+ write_kate_page(info, best);
+ }
else if(e_o_s && info->videopage_valid) {
write_video_page(info);
}
@@ -584,6 +887,8 @@
}
void oggmux_close (oggmux_info *info){
+ int n;
+
ogg_stream_clear (&info->vo);
vorbis_block_clear (&info->vb);
vorbis_dsp_clear (&info->vd);
@@ -593,6 +898,15 @@
ogg_stream_clear (&info->to);
theora_clear (&info->td);
+#ifdef HAVE_KATE
+ for (n=0; n<info->n_kate_streams; ++n) {
+ ogg_stream_clear (&info->kate_streams[n].ko);
+ kate_comment_clear (&info->kate_streams[n].kc);
+ kate_info_clear (&info->kate_streams[n].ki);
+ kate_clear (&info->kate_streams[n].k);
+ }
+#endif
+
if (info->outfile && info->outfile != stdout)
fclose (info->outfile);
@@ -600,4 +914,9 @@
free(info->videopage);
if(info->audiopage)
free(info->audiopage);
+
+ for (n=0; n<info->n_kate_streams; ++n) {
+ if(info->kate_streams[n].katepage)
+ free(info->kate_streams[n].katepage);
+ }
}
Modified: trunk/ffmpeg2theora/theorautils.h
===================================================================
--- trunk/ffmpeg2theora/theorautils.h 2008-03-20 09:00:03 UTC (rev 14611)
+++ trunk/ffmpeg2theora/theorautils.h 2008-03-20 10:46:54 UTC (rev 14612)
@@ -22,6 +22,9 @@
#include "theora/theora.h"
#include "vorbis/codec.h"
#include "vorbis/vorbisenc.h"
+#ifdef HAVE_KATE
+#include "kate/kate.h"
+#endif
#include "ogg/ogg.h"
// #define OGGMUX_DEBUG
@@ -35,6 +38,23 @@
typedef struct
{
+#ifdef HAVE_KATE
+ kate_state k;
+ kate_info ki;
+ kate_comment kc;
+#endif
+ ogg_stream_state ko; /* take physical pages, weld into a logical
+ * stream of packets */
+ int katepage_valid;
+ unsigned char *katepage;
+ int katepage_len;
+ int katepage_buffer_length;
+ double katetime;
+}
+oggmux_kate_stream;
+
+typedef struct
+{
/* the file the mixed ogg stream is written to */
FILE *outfile;
@@ -61,6 +81,8 @@
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
vorbis_block vb; /* local working space for packet->PCM decode */
+ int with_kate;
+
/* used for muxing */
ogg_stream_state to; /* take physical pages, weld into a logical
* stream of packets */
@@ -87,21 +109,30 @@
int akbps;
ogg_int64_t audio_bytesout;
ogg_int64_t video_bytesout;
+ ogg_int64_t kate_bytesout;
time_t start_time;
//to do some manual page flusing
int v_pkg;
int a_pkg;
+ int k_pkg;
#ifdef OGGMUX_DEBUG
int a_page;
int v_page;
+ int k_page;
#endif
+
+ int n_kate_streams;
+ oggmux_kate_stream *kate_streams;
}
oggmux_info;
extern void init_info(oggmux_info *info);
+extern void oggmux_setup_kate_streams(oggmux_info *info, int n_kate_streams);
extern void oggmux_init (oggmux_info *info);
extern void oggmux_add_video (oggmux_info *info, yuv_buffer *yuv, int e_o_s);
extern void oggmux_add_audio (oggmux_info *info, int16_t * readbuffer, int bytesread, int samplesread,int e_o_s);
+extern void oggmux_add_kate_text (oggmux_info *info, int idx, double t0, double t1, const char *text, size_t len,int e_o_s);
+extern void oggmux_add_kate_end_packet (oggmux_info *info, int idx, double t);
extern void oggmux_flush (oggmux_info *info, int e_o_s);
extern void oggmux_close (oggmux_info *info);
More information about the commits
mailing list