Skip to content

Commit

Permalink
Theora encoding via libtheora.
Browse files Browse the repository at this point in the history
courtesy of Paul Richards, paul.richards gmail com

Originally committed as revision 7698 to svn://svn.ffmpeg.org/ffmpeg/trunk
  • Loading branch information
DonDiego committed Jan 25, 2007
1 parent bf50703 commit 150d277
Show file tree
Hide file tree
Showing 7 changed files with 296 additions and 1 deletion.
1 change: 1 addition & 0 deletions Changelog
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ version <next>
- Intel Music decoder
- Musepack decoder
- Flash Screen Video encoder
- Theora encoding via libtheora

version 0.4.9-pre1:

Expand Down
13 changes: 13 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ show_help(){
echo " --enable-mp3lame enable MP3 encoding via libmp3lame [default=no]"
echo " --enable-libnut enable NUT support via libnut [default=no]"
echo " --enable-libogg enable Ogg support via libogg [default=no]"
echo " --enable-libtheora enable Theora support via libtheora [default=no]"
echo " --enable-vorbis enable Vorbis support via libvorbis [default=no]"
echo " --enable-x264 enable H.264 encoding via x264 [default=no]"
echo " --enable-xvid enable Xvid encoding via xvidcore [default=no]"
Expand Down Expand Up @@ -444,6 +445,7 @@ CONFIG_LIST='
libmp3lame
libnut
libogg
libtheora
libvorbis
memalign_hack
mpegaudio_hp
Expand Down Expand Up @@ -514,6 +516,7 @@ dts_decoder_deps="libdts"
faac_encoder_deps="libfaac"
libgsm_decoder_deps="libgsm"
libgsm_encoder_deps="libgsm"
libtheora_encoder_deps="libtheora"
mp3lame_encoder_deps="libmp3lame"
oggvorbis_decoder_deps="libvorbis"
oggvorbis_encoder_deps="libvorbis"
Expand Down Expand Up @@ -651,6 +654,7 @@ libgsm="no"
libmp3lame="no"
libnut="no"
libogg="no"
libtheora="no"
libvorbis="no"
xvid="no"
x264="no"
Expand Down Expand Up @@ -1003,6 +1007,9 @@ for opt do
--enable-libogg) libogg="yes"
pkg_requires="$pkg_requires ogg >= 1.1"
;;
--enable-libtheora) libtheora="yes"
pkg_requires="$pkg_requires theora"
;;
--enable-vorbis) libvorbis="yes"
pkg_requires="$pkg_requires vorbis vorbisenc"
;;
Expand Down Expand Up @@ -1179,6 +1186,10 @@ EOF
exit 1;
fi

if test "$libtheora" = "yes" && test "$libogg" = "no"; then
die "libogg must be enabled to enable libtheora."
fi

if test "$libvorbis" = "yes" && test "$libogg" = "no"; then
die "libogg must be enabled to enable libvorbis."
fi
Expand Down Expand Up @@ -1532,6 +1543,7 @@ done
enabled libdts && require libdts dts.h dts_init -ldts -lm
enabled libgsm && require libgsm gsm.h gsm_create -lgsm
enabled libmp3lame && require LAME lame/lame.h lame_init -lmp3lame -lm
enabled libtheora && require libtheora theora/theora.h theora_info_init -ltheora -logg
enabled libvorbis && require libvorbis vorbis/vorbisenc.h vorbis_info_init -lvorbis -lvorbisenc -logg
enabled libogg && require libogg ogg/ogg.h ogg_sync_init -logg
enabled libnut && require libnut libnut.h nut_demuxer_init -lnut
Expand Down Expand Up @@ -1868,6 +1880,7 @@ echo "libgsm enabled $libgsm"
echo "libmp3lame enabled $libmp3lame"
echo "libnut enabled $libnut"
echo "libogg enabled $libogg"
echo "libtheora enabled $libtheora"
echo "libvorbis enabled $libvorbis"
echo "x264 enabled $x264"
echo "XviD enabled $xvid"
Expand Down
2 changes: 1 addition & 1 deletion doc/ffmpeg-doc.texi
Original file line number Diff line number Diff line change
Expand Up @@ -969,7 +969,7 @@ following image formats are supported:
@item On2 VP3 @tab @tab X @tab still experimental
@item On2 VP5 @tab @tab X @tab fourcc: VP50
@item On2 VP6 @tab @tab X @tab fourcc: VP60,VP61,VP62
@item Theora @tab @tab X @tab still experimental
@item Theora @tab X @tab X @tab still experimental
@item Intel Indeo 3 @tab @tab X
@item FLV @tab X @tab X @tab Sorenson H.263 used in Flash
@item Flash Screen Video @tab @tab X @tab fourcc: FSV1
Expand Down
1 change: 1 addition & 0 deletions libavcodec/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ OBJS-$(CONFIG_LIBFAAC) += faac.o
OBJS-$(CONFIG_LIBFAAD) += faad.o
OBJS-$(CONFIG_LIBGSM) += libgsm.o
OBJS-$(CONFIG_LIBMP3LAME) += mp3lameaudio.o
OBJS-$(CONFIG_LIBTHEORA) += libtheoraenc.o
OBJS-$(CONFIG_LIBVORBIS) += oggvorbis.o
OBJS-$(CONFIG_X264) += x264.o
OBJS-$(CONFIG_XVID) += xvidff.o xvid_rc.o
Expand Down
1 change: 1 addition & 0 deletions libavcodec/allcodecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ void avcodec_register_all(void)
REGISTER_ENCDEC (FLAC, flac);
REGISTER_DECODER(IMC, imc);
REGISTER_ENCDEC (LIBGSM, libgsm);
REGISTER_ENCODER(LIBTHEORA, libtheora);
REGISTER_DECODER(MACE3, mace3);
REGISTER_DECODER(MACE6, mace6);
REGISTER_ENCDEC (MP2, mp2);
Expand Down
1 change: 1 addition & 0 deletions libavcodec/avcodec.h
Original file line number Diff line number Diff line change
Expand Up @@ -2162,6 +2162,7 @@ extern AVCodec h264_encoder;
extern AVCodec huffyuv_encoder;
extern AVCodec jpegls_encoder;
extern AVCodec libgsm_encoder;
extern AVCodec libtheora_encoder;
extern AVCodec ljpeg_encoder;
extern AVCodec mdec_encoder;
extern AVCodec mjpeg_encoder;
Expand Down
278 changes: 278 additions & 0 deletions libavcodec/libtheoraenc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
/*
* Copyright (c) 2006 Paul Richards <paul.richards@gmail.com>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

/*!
* \file theoraenc.c
* \brief Theora encoder using libtheora.
* \author Paul Richards <paul.richards@gmail.com>
*
* A lot of this is copy / paste from other output codecs in
* libavcodec or pure guesswork (or both).
*
* I have used t_ prefixes on variables which are libtheora types
* and o_ prefixes on variables which are libogg types.
*/

/* FFmpeg includes */
#include "avcodec.h"
#include "log.h"

/* libtheora includes */
#include <theora/theora.h>

typedef struct TheoraContext{
theora_state t_state;
} TheoraContext;

/*!
Concatenates an ogg_packet into the extradata.
*/
static int concatenate_packet(unsigned int* offset, AVCodecContext* avc_context, const ogg_packet* packet)
{
char* message = NULL;
uint8_t* newdata = NULL;
int newsize = avc_context->extradata_size + 2 + packet->bytes;

if (packet->bytes < 0) {
message = "ogg_packet has negative size";
} else if (packet->bytes > 0xffff) {
message = "ogg_packet is larger than 65535 bytes";
} else if (newsize < avc_context->extradata_size) {
message = "extradata_size would overflow";
} else {
newdata = av_realloc(avc_context->extradata, newsize);
if (newdata == NULL) {
message = "av_realloc failed";
}
}
if (message != NULL) {
av_log(avc_context, AV_LOG_ERROR, "concatenate_packet failed: %s\n", message);
return -1;
}

avc_context->extradata = newdata;
avc_context->extradata_size = newsize;
avc_context->extradata[ (*offset)++ ] = packet->bytes >> 8;
avc_context->extradata[ (*offset)++ ] = packet->bytes & 0xff;
memcpy( avc_context->extradata + (*offset), packet->packet, packet->bytes );
(*offset) += packet->bytes;
return 0;
}

static int encode_init(AVCodecContext* avc_context)
{
theora_info t_info;
theora_comment t_comment;
ogg_packet o_packet;
unsigned int offset;
TheoraContext *h = avc_context->priv_data;

/* Set up the theora_info struct */
theora_info_init( &t_info );
t_info.width = avc_context->width;
t_info.height = avc_context->height;
t_info.frame_width = avc_context->width;
t_info.frame_height = avc_context->height;
t_info.offset_x = 0;
t_info.offset_y = 0;
t_info.fps_numerator = avc_context->time_base.den;
t_info.fps_denominator = avc_context->time_base.num;
if (avc_context->sample_aspect_ratio.num != 0) {
t_info.aspect_numerator = avc_context->sample_aspect_ratio.num;
t_info.aspect_denominator = avc_context->sample_aspect_ratio.den;
} else {
t_info.aspect_numerator = 1;
t_info.aspect_denominator = 1;
}
t_info.colorspace = OC_CS_UNSPECIFIED;
t_info.pixelformat = OC_PF_420;
t_info.target_bitrate = avc_context->bit_rate;
t_info.keyframe_frequency = avc_context->gop_size;
t_info.keyframe_frequency_force = avc_context->gop_size;
t_info.keyframe_mindistance = avc_context->keyint_min;
t_info.quality = 0;

t_info.quick_p = 1;
t_info.dropframes_p = 0;
t_info.keyframe_auto_p = 1;
t_info.keyframe_data_target_bitrate = t_info.target_bitrate * 1.5;
t_info.keyframe_auto_threshold = 80;
t_info.noise_sensitivity = 1;
t_info.sharpness = 0;

/* Now initialise libtheora */
if (theora_encode_init( &(h->t_state), &t_info ) != 0) {
av_log(avc_context, AV_LOG_ERROR, "theora_encode_init failed\n");
return -1;
}

/* Clear up theora_info struct */
theora_info_clear( &t_info );

/*
Output first header packet consisting of theora
header, comment, and tables.
Each one is prefixed with a 16bit size, then they
are concatenated together into ffmpeg's extradata.
*/
offset = 0;

/* Header */
theora_encode_header( &(h->t_state), &o_packet );
if (concatenate_packet( &offset, avc_context, &o_packet ) != 0) {
return -1;
}

/* Comment */
theora_comment_init( &t_comment );
theora_encode_comment( &t_comment, &o_packet );
if (concatenate_packet( &offset, avc_context, &o_packet ) != 0) {
return -1;
}

/* Tables */
theora_encode_tables( &(h->t_state), &o_packet );
if (concatenate_packet( &offset, avc_context, &o_packet ) != 0) {
return -1;
}

/* Clear up theora_comment struct */
theora_comment_clear( &t_comment );

/* Set up the output AVFrame */
avc_context->coded_frame= avcodec_alloc_frame();

return 0;
}

static int encode_frame(
AVCodecContext* avc_context,
uint8_t *outbuf,
int buf_size,
void *data)
{
yuv_buffer t_yuv_buffer;
TheoraContext *h = avc_context->priv_data;
AVFrame *frame = data;
ogg_packet o_packet;
int result;

assert(avc_context->pix_fmt == PIX_FMT_YUV420P);

/* Copy planes to the theora yuv_buffer */
if (frame->linesize[1] != frame->linesize[2]) {
av_log(avc_context, AV_LOG_ERROR, "U and V stride differ\n");
return -1;
}

t_yuv_buffer.y_width = avc_context->width;
t_yuv_buffer.y_height = avc_context->height;
t_yuv_buffer.y_stride = frame->linesize[0];
t_yuv_buffer.uv_width = t_yuv_buffer.y_width / 2;
t_yuv_buffer.uv_height = t_yuv_buffer.y_height / 2;
t_yuv_buffer.uv_stride = frame->linesize[1];

t_yuv_buffer.y = frame->data[0];
t_yuv_buffer.u = frame->data[1];
t_yuv_buffer.v = frame->data[2];

/* Now call into theora_encode_YUVin */
result = theora_encode_YUVin( &(h->t_state), &t_yuv_buffer );
if (result != 0) {
const char* message;
switch (result) {
case -1:
message = "differing frame sizes";
break;
case OC_EINVAL:
message = "encoder is not ready or is finished";
break;
default:
message = "unknown reason";
break;
}
av_log(avc_context, AV_LOG_ERROR, "theora_encode_YUVin failed (%s) [%d]\n", message, result);
return -1;
}

/* Pick up returned ogg_packet */
result = theora_encode_packetout( &(h->t_state), 0, &o_packet );
switch (result) {
case 0:
/* No packet is ready */
return 0;
case 1:
/* Success, we have a packet */
break;
default:
av_log(avc_context, AV_LOG_ERROR, "theora_encode_packetout failed [%d]\n", result);
return -1;
}

/* Copy ogg_packet content out to buffer */
if (buf_size < o_packet.bytes) {
av_log(avc_context, AV_LOG_ERROR, "encoded frame too large\n");
return -1;
}
memcpy(outbuf, o_packet.packet, o_packet.bytes);

return o_packet.bytes;
}

static int encode_close(AVCodecContext* avc_context)
{
ogg_packet o_packet;
TheoraContext *h = avc_context->priv_data;
int result;
const char* message;

result = theora_encode_packetout( &(h->t_state), 1, &o_packet );
theora_clear( &(h->t_state) );
switch (result) {
case 0:/* No packet is ready */
case -1:/* Encoding finished */
return 0;
case 1:
/* We have a packet */
message = "gave us a packet";
break;
default:
message = "unknown reason";
break;
}
av_log(avc_context, AV_LOG_ERROR, "theora_encode_packetout failed (%s) [%d]\n", message, result);
return -1;
}

static const enum PixelFormat supported_pixel_formats[] = { PIX_FMT_YUV420P, -1 };

/*! AVCodec struct exposed to libavcodec */
AVCodec libtheora_encoder =
{
.name = "libtheora",
.type = CODEC_TYPE_VIDEO,
.id = CODEC_ID_THEORA,
.priv_data_size = sizeof(TheoraContext),
.init = encode_init,
.close = encode_close,
.encode = encode_frame,
.pix_fmts = supported_pixel_formats,
};

0 comments on commit 150d277

Please sign in to comment.