Commit 5118cae6 authored by Baptiste Coudurier's avatar Baptiste Coudurier

ogg muxer

Originally committed as revision 10999 to svn://svn.ffmpeg.org/ffmpeg/trunk
parent 96e2fbf2
...@@ -103,6 +103,7 @@ version <next> ...@@ -103,6 +103,7 @@ version <next>
- Beam Software SIFF demuxer and decoder - Beam Software SIFF demuxer and decoder
- libvorbis Vorbis decoding removed in favor of native decoder - libvorbis Vorbis decoding removed in favor of native decoder
- IntraX8 (J-Frame) sub-decoder for WMV2 and VC-1 - IntraX8 (J-Frame) sub-decoder for WMV2 and VC-1
- OGG muxer
version 0.4.9-pre1: version 0.4.9-pre1:
......
...@@ -237,6 +237,7 @@ Muxers/Demuxers: ...@@ -237,6 +237,7 @@ Muxers/Demuxers:
nut.c Michael Niedermayer nut.c Michael Niedermayer
nuv.c Reimar Doeffinger nuv.c Reimar Doeffinger
ogg2.c, ogg2.h Mans Rullgard ogg2.c, ogg2.h Mans Rullgard
oggenc.c Baptiste Coudurier
oggparsevorbis.c Mans Rullgard oggparsevorbis.c Mans Rullgard
oggparseogm.c Mans Rullgard oggparseogm.c Mans Rullgard
psxstr.c Mike Melanson psxstr.c Mike Melanson
......
...@@ -292,6 +292,9 @@ OBJS-$(CONFIG_ADPCM_XA_DECODER) += adpcm.o ...@@ -292,6 +292,9 @@ OBJS-$(CONFIG_ADPCM_XA_DECODER) += adpcm.o
OBJS-$(CONFIG_ADPCM_YAMAHA_DECODER) += adpcm.o OBJS-$(CONFIG_ADPCM_YAMAHA_DECODER) += adpcm.o
OBJS-$(CONFIG_ADPCM_YAMAHA_ENCODER) += adpcm.o OBJS-$(CONFIG_ADPCM_YAMAHA_ENCODER) += adpcm.o
# libavformat dependencies
OBJS-$(CONFIG_OGG_MUXER) += xiph.o
# external codec libraries # external codec libraries
OBJS-$(CONFIG_LIBA52) += liba52.o OBJS-$(CONFIG_LIBA52) += liba52.o
OBJS-$(CONFIG_LIBAMR) += libamr.o OBJS-$(CONFIG_LIBAMR) += libamr.o
......
...@@ -113,6 +113,7 @@ OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \ ...@@ -113,6 +113,7 @@ OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \
oggparseflac.o \ oggparseflac.o \
oggparseogm.o \ oggparseogm.o \
riff.o riff.o
OBJS-$(CONFIG_OGG_MUXER) += oggenc.o
OBJS-$(CONFIG_OSS_DEMUXER) += audio.o OBJS-$(CONFIG_OSS_DEMUXER) += audio.o
OBJS-$(CONFIG_OSS_MUXER) += audio.o OBJS-$(CONFIG_OSS_MUXER) += audio.o
OBJS-$(CONFIG_PSP_MUXER) += movenc.o riff.o isom.o OBJS-$(CONFIG_PSP_MUXER) += movenc.o riff.o isom.o
......
...@@ -122,7 +122,7 @@ void av_register_all(void) ...@@ -122,7 +122,7 @@ void av_register_all(void)
REGISTER_MUXER (NULL, null); REGISTER_MUXER (NULL, null);
REGISTER_MUXDEMUX (NUT, nut); REGISTER_MUXDEMUX (NUT, nut);
REGISTER_DEMUXER (NUV, nuv); REGISTER_DEMUXER (NUV, nuv);
REGISTER_DEMUXER (OGG, ogg); REGISTER_MUXDEMUX (OGG, ogg);
REGISTER_MUXDEMUX (OSS, oss); REGISTER_MUXDEMUX (OSS, oss);
REGISTER_MUXDEMUX (PCM_ALAW, pcm_alaw); REGISTER_MUXDEMUX (PCM_ALAW, pcm_alaw);
REGISTER_MUXDEMUX (PCM_MULAW, pcm_mulaw); REGISTER_MUXDEMUX (PCM_MULAW, pcm_mulaw);
......
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
#ifndef FFMPEG_AVFORMAT_H #ifndef FFMPEG_AVFORMAT_H
#define FFMPEG_AVFORMAT_H #define FFMPEG_AVFORMAT_H
#define LIBAVFORMAT_VERSION_INT ((51<<16)+(18<<8)+0) #define LIBAVFORMAT_VERSION_INT ((51<<16)+(19<<8)+0)
#define LIBAVFORMAT_VERSION 51.18.0 #define LIBAVFORMAT_VERSION 51.19.0
#define LIBAVFORMAT_BUILD LIBAVFORMAT_VERSION_INT #define LIBAVFORMAT_BUILD LIBAVFORMAT_VERSION_INT
#define LIBAVFORMAT_IDENT "Lavf" AV_STRINGIFY(LIBAVFORMAT_VERSION) #define LIBAVFORMAT_IDENT "Lavf" AV_STRINGIFY(LIBAVFORMAT_VERSION)
......
/*
* Ogg muxer
* Copyright (c) 2007 Baptiste Coudurier <baptiste dot coudurier at free dot fr>
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
#include "crc.h"
#include "xiph.h"
#include "bytestream.h"
typedef struct {
int64_t duration;
unsigned page_counter;
uint8_t *header[3];
int header_len[3];
/** for theora granule */
int kfgshift;
int64_t last_kf_pts;
int vrev;
} OGGStreamContext;
static void ogg_update_checksum(AVFormatContext *s, offset_t crc_offset)
{
offset_t pos = url_ftell(&s->pb);
uint32_t checksum = get_checksum(&s->pb);
url_fseek(&s->pb, crc_offset, SEEK_SET);
put_be32(&s->pb, checksum);
url_fseek(&s->pb, pos, SEEK_SET);
}
static int ogg_write_page(AVFormatContext *s, const uint8_t *data, int size,
int64_t granule, int stream_index, int flags)
{
OGGStreamContext *oggstream = s->streams[stream_index]->priv_data;
offset_t crc_offset;
int page_segments, i;
size = FFMIN(size, 255*255);
page_segments = FFMIN((size/255)+!!size, 255);
init_checksum(&s->pb, ff_crc04C11DB7_update, 0);
put_tag(&s->pb, "OggS");
put_byte(&s->pb, 0);
put_byte(&s->pb, flags);
put_le64(&s->pb, granule);
put_le32(&s->pb, stream_index);
put_le32(&s->pb, oggstream->page_counter++);
crc_offset = url_ftell(&s->pb);
put_le32(&s->pb, 0); // crc
put_byte(&s->pb, page_segments);
for (i = 0; i < page_segments-1; i++)
put_byte(&s->pb, 255);
if (size) {
put_byte(&s->pb, size - (page_segments-1)*255);
put_buffer(&s->pb, data, size);
}
ogg_update_checksum(s, crc_offset);
put_flush_packet(&s->pb);
return size;
}
static int ogg_build_flac_headers(const uint8_t *extradata, int extradata_size,
OGGStreamContext *oggstream, int bitexact)
{
const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT;
uint8_t *p;
if (extradata_size != 34)
return -1;
oggstream->header_len[0] = 79;
oggstream->header[0] = av_mallocz(79); // per ogg flac specs
p = oggstream->header[0];
bytestream_put_byte(&p, 0x7F);
bytestream_put_buffer(&p, "FLAC", 4);
bytestream_put_byte(&p, 1); // major version
bytestream_put_byte(&p, 0); // minor version
bytestream_put_be16(&p, 1); // headers packets without this one
bytestream_put_buffer(&p, "fLaC", 4);
bytestream_put_byte(&p, 0x00); // streaminfo
bytestream_put_be24(&p, 34);
bytestream_put_buffer(&p, extradata, 34);
oggstream->header_len[1] = 1+3+4+strlen(vendor)+4;
oggstream->header[1] = av_mallocz(oggstream->header_len[1]);
p = oggstream->header[1];
bytestream_put_byte(&p, 0x84); // last metadata block and vorbis comment
bytestream_put_be24(&p, oggstream->header_len[1] - 4);
bytestream_put_le32(&p, strlen(vendor));
bytestream_put_buffer(&p, vendor, strlen(vendor));
bytestream_put_le32(&p, 0); // user comment list length
return 0;
}
static int ogg_write_header(AVFormatContext *s)
{
OGGStreamContext *oggstream;
int i, j;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
if (st->codec->codec_type == CODEC_TYPE_AUDIO)
av_set_pts_info(st, 64, 1, st->codec->sample_rate);
else if (st->codec->codec_type == CODEC_TYPE_VIDEO)
av_set_pts_info(st, 64, st->codec->time_base.num, st->codec->time_base.den);
if (st->codec->codec_id != CODEC_ID_VORBIS &&
st->codec->codec_id != CODEC_ID_THEORA &&
st->codec->codec_id != CODEC_ID_FLAC) {
av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i);
return -1;
}
if (!st->codec->extradata || !st->codec->extradata_size) {
av_log(s, AV_LOG_ERROR, "No extradata present\n");
return -1;
}
oggstream = av_mallocz(sizeof(*oggstream));
st->priv_data = oggstream;
if (st->codec->codec_id == CODEC_ID_FLAC) {
if (ogg_build_flac_headers(st->codec->extradata, st->codec->extradata_size,
oggstream, st->codec->flags & CODEC_FLAG_BITEXACT) < 0) {
av_log(s, AV_LOG_ERROR, "Extradata corrupted\n");
av_freep(&st->priv_data);
}
} else {
if (ff_split_xiph_headers(st->codec->extradata, st->codec->extradata_size,
st->codec->codec_id == CODEC_ID_VORBIS ? 30 : 42,
oggstream->header, oggstream->header_len) < 0) {
av_log(s, AV_LOG_ERROR, "Extradata corrupted\n");
av_freep(&st->priv_data);
return -1;
}
if (st->codec->codec_id == CODEC_ID_THEORA) {
/** KFGSHIFT is the width of the less significant section of the granule position
The less significant section is the frame count since the last keyframe */
oggstream->kfgshift = ((oggstream->header[0][40]&3)<<3)|(oggstream->header[0][41]>>5);
oggstream->vrev = oggstream->header[0][9];
av_log(s, AV_LOG_DEBUG, "theora kfgshift %d, vrev %d\n",
oggstream->kfgshift, oggstream->vrev);
}
}
}
for (i = 0; i < 3; i++) {
for (j = 0; j < s->nb_streams; j++) {
AVStream *st = s->streams[j];
OGGStreamContext *oggstream = st->priv_data;
if (oggstream && oggstream->header_len[i]) {
ogg_write_page(s, oggstream->header[i], oggstream->header_len[i],
0, st->index, i ? 0 : 2); // bos
}
}
}
return 0;
}
static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt)
{
AVStream *st = s->streams[pkt->stream_index];
OGGStreamContext *oggstream = st->priv_data;
uint8_t *ptr = pkt->data;
int ret, size = pkt->size;
int64_t granule;
if (st->codec->codec_id == CODEC_ID_THEORA) {
int64_t pts = oggstream->vrev < 1 ? pkt->pts : pkt->pts + pkt->duration;
int pframe_count;
if (pkt->flags & PKT_FLAG_KEY)
oggstream->last_kf_pts = pts;
pframe_count = pts - oggstream->last_kf_pts;
// prevent frame count from overflow if key frame flag is not set
if (pframe_count >= (1<<oggstream->kfgshift)) {
oggstream->last_kf_pts += pframe_count;
pframe_count = 0;
}
granule = (oggstream->last_kf_pts<<oggstream->kfgshift) | pframe_count;
} else
granule = pkt->pts + pkt->duration;
oggstream->duration = granule;
do {
ret = ogg_write_page(s, ptr, size, granule, pkt->stream_index, ptr != pkt->data);
ptr += ret; size -= ret;
} while (size > 0 || ret == 255*255); // need to output a last nil page
return 0;
}
static int ogg_write_trailer(AVFormatContext *s)
{
int i;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
OGGStreamContext *oggstream = st->priv_data;
ogg_write_page(s, NULL, 0, oggstream->duration, i, 4); // eos
if (st->codec->codec_id == CODEC_ID_FLAC) {
av_free(oggstream->header[0]);
av_free(oggstream->header[1]);
}
av_freep(&st->priv_data);
}
return 0;
}
AVOutputFormat ogg_muxer = {
"ogg",
"Ogg format",
"application/ogg",
"ogg",
0,
CODEC_ID_FLAC,
CODEC_ID_THEORA,
ogg_write_header,
ogg_write_packet,
ogg_write_trailer,
};
...@@ -85,6 +85,9 @@ ae3a23a7ea13c92a2909445ca8144dcd *./tests/data/b-libav.aif ...@@ -85,6 +85,9 @@ ae3a23a7ea13c92a2909445ca8144dcd *./tests/data/b-libav.aif
8d117c49d6b210abe783d1b0b897cec7 *./tests/data/b-libav.voc 8d117c49d6b210abe783d1b0b897cec7 *./tests/data/b-libav.voc
32768 ./tests/data/b-libav.voc 32768 ./tests/data/b-libav.voc
./tests/data/b-libav.voc CRC=0x49972c8c ./tests/data/b-libav.voc CRC=0x49972c8c
f77f5d44edf767bcc47959787eaaf188 *./tests/data/b-libav.ogg
23651 ./tests/data/b-libav.ogg
./tests/data/b-libav.ogg CRC=0x93baa056
ce356ce2708cb6033ab5d762da93cfd4 *./tests/data/b-libav-yuv420p.yuv ce356ce2708cb6033ab5d762da93cfd4 *./tests/data/b-libav-yuv420p.yuv
304128 ./tests/data/b-libav-yuv420p.yuv 304128 ./tests/data/b-libav-yuv420p.yuv
ce356ce2708cb6033ab5d762da93cfd4 *./tests/data/b-libav-yuv422p.yuv ce356ce2708cb6033ab5d762da93cfd4 *./tests/data/b-libav-yuv422p.yuv
......
...@@ -715,6 +715,9 @@ do_audio_only aif ...@@ -715,6 +715,9 @@ do_audio_only aif
# voc # voc
do_audio_only voc do_audio_only voc
# ogg
do_audio_only ogg
#################### ####################
# pix_fmt conversions # pix_fmt conversions
conversions="yuv420p yuv422p yuv444p yuyv422 yuv410p yuv411p yuvj420p \ conversions="yuv420p yuv422p yuv444p yuyv422 yuv410p yuv411p yuvj420p \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment