diff --git a/libavformat/avformat.h b/libavformat/avformat.h index fbe65f509c..ddb3ffadb4 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -23,7 +23,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 52 #define LIBAVFORMAT_VERSION_MINOR 39 -#define LIBAVFORMAT_VERSION_MICRO 0 +#define LIBAVFORMAT_VERSION_MICRO 1 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c index 9ab1ac0aff..669d3a0bb4 100644 --- a/libavformat/oggenc.c +++ b/libavformat/oggenc.c @@ -83,10 +83,31 @@ static int ogg_write_page(AVFormatContext *s, const uint8_t *data, int size, return size; } +static uint8_t *ogg_write_vorbiscomment(int offset, int bitexact, + int *header_len) +{ + const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; + int size; + uint8_t *p, *p0; + + size = offset + 4 + strlen(vendor) + 4; + p = av_mallocz(size); + if (!p) + return NULL; + p0 = p; + + p += offset; + bytestream_put_le32(&p, strlen(vendor)); + bytestream_put_buffer(&p, vendor, strlen(vendor)); + bytestream_put_le32(&p, 0); // user comment list length + + *header_len = size; + return p0; +} + static int ogg_build_flac_headers(AVCodecContext *avctx, OGGStreamContext *oggstream, int bitexact) { - const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; enum FLACExtradataFormat format; uint8_t *streaminfo; uint8_t *p; @@ -111,16 +132,40 @@ static int ogg_build_flac_headers(AVCodecContext *avctx, bytestream_put_buffer(&p, streaminfo, FLAC_STREAMINFO_SIZE); // second packet: VorbisComment - oggstream->header_len[1] = 1+3+4+strlen(vendor)+4; - oggstream->header[1] = av_mallocz(oggstream->header_len[1]); - p = oggstream->header[1]; + p = ogg_write_vorbiscomment(4, bitexact, &oggstream->header_len[1]); if (!p) return AVERROR_NOMEM; + oggstream->header[1] = p; 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; +} + +#define SPEEX_HEADER_SIZE 80 + +static int ogg_build_speex_headers(AVCodecContext *avctx, + OGGStreamContext *oggstream, int bitexact) +{ + uint8_t *p; + + if (avctx->extradata_size < SPEEX_HEADER_SIZE) + return -1; + + // first packet: Speex header + p = av_mallocz(SPEEX_HEADER_SIZE); + if (!p) + return AVERROR_NOMEM; + oggstream->header[0] = p; + oggstream->header_len[0] = SPEEX_HEADER_SIZE; + bytestream_put_buffer(&p, avctx->extradata, SPEEX_HEADER_SIZE); + AV_WL32(&oggstream->header[0][68], 0); // set extra_headers to 0 + + // second packet: VorbisComment + p = ogg_write_vorbiscomment(0, bitexact, &oggstream->header_len[1]); + if (!p) + return AVERROR_NOMEM; + oggstream->header[1] = p; return 0; } @@ -137,6 +182,7 @@ static int ogg_write_header(AVFormatContext *s) 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_SPEEX && st->codec->codec_id != CODEC_ID_FLAC) { av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i); return -1; @@ -156,6 +202,14 @@ static int ogg_write_header(AVFormatContext *s) av_freep(&st->priv_data); return err; } + } else if (st->codec->codec_id == CODEC_ID_SPEEX) { + int err = ogg_build_speex_headers(st->codec, oggstream, + st->codec->flags & CODEC_FLAG_BITEXACT); + if (err) { + av_log(s, AV_LOG_ERROR, "Error writing Speex headers\n"); + av_freep(&st->priv_data); + return err; + } } else { if (ff_split_xiph_headers(st->codec->extradata, st->codec->extradata_size, st->codec->codec_id == CODEC_ID_VORBIS ? 30 : 42, @@ -281,7 +335,8 @@ static int ogg_write_trailer(AVFormatContext *s) for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; OGGStreamContext *oggstream = st->priv_data; - if (st->codec->codec_id == CODEC_ID_FLAC) { + if (st->codec->codec_id == CODEC_ID_FLAC || + st->codec->codec_id == CODEC_ID_SPEEX) { av_free(oggstream->header[0]); av_free(oggstream->header[1]); } @@ -294,7 +349,7 @@ AVOutputFormat ogg_muxer = { "ogg", NULL_IF_CONFIG_SMALL("Ogg"), "application/ogg", - "ogg,ogv", + "ogg,ogv,spx", 0, CODEC_ID_FLAC, CODEC_ID_THEORA,