diff --git a/libavformat/Makefile b/libavformat/Makefile index 62dc41c762..9833ebb095 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -450,6 +450,7 @@ OBJS-$(CONFIG_WEBM_DASH_MANIFEST_DEMUXER)+= matroskadec.o matroska.o \ oggparsevorbis.o vorbiscomment.o \ flac_picture.o replaygain.o OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o matroska.o +OBJS-$(CONFIG_WEBP_MUXER) += webpenc.o OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index bf81737688..4d7ef2025e 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -310,6 +310,7 @@ void av_register_all(void) REGISTER_DEMUXER (WC3, wc3); REGISTER_MUXER (WEBM, webm); REGISTER_MUXDEMUX(WEBM_DASH_MANIFEST, webm_dash_manifest); + REGISTER_MUXER (WEBP, webp); REGISTER_MUXDEMUX(WEBVTT, webvtt); REGISTER_DEMUXER (WSAUD, wsaud); REGISTER_DEMUXER (WSVQA, wsvqa); diff --git a/libavformat/version.h b/libavformat/version.h index ab6d134a19..aba15cb50c 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -30,8 +30,8 @@ #include "libavutil/version.h" #define LIBAVFORMAT_VERSION_MAJOR 56 -#define LIBAVFORMAT_VERSION_MINOR 11 -#define LIBAVFORMAT_VERSION_MICRO 102 +#define LIBAVFORMAT_VERSION_MINOR 12 +#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ diff --git a/libavformat/webpenc.c b/libavformat/webpenc.c new file mode 100644 index 0000000000..5c0b8a327b --- /dev/null +++ b/libavformat/webpenc.c @@ -0,0 +1,156 @@ +/* + * webp muxer + * Copyright (c) 2014 Michael Niedermayer + * + * 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 "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" + +typedef struct WebpContext{ + AVClass *class; + int frame_count; + AVPacket last_pkt; + int loop; +} WebpContext; + +static int webp_write_header(AVFormatContext *s) +{ + AVStream *st; + + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "Only exactly 1 stream is supported\n"); + return AVERROR(EINVAL); + } + st = s->streams[0]; + if (st->codec->codec_id != AV_CODEC_ID_WEBP) { + av_log(s, AV_LOG_ERROR, "Only WebP is supported\n"); + return AVERROR(EINVAL); + } + avpriv_set_pts_info(st, 24, 1, 1000); + + avio_write(s->pb, "RIFF\0\0\0\0WEBP", 12); + + return 0; +} + +static int flush(AVFormatContext *s, int trailer, int64_t pts) +{ + WebpContext *w = s->priv_data; + AVStream *st = s->streams[0]; + + if (w->last_pkt.size) { + int skip = 0; + + if (AV_RL32(w->last_pkt.data) == AV_RL32("RIFF")) + skip = 12; + if (AV_RL32(w->last_pkt.data + skip) == AV_RL32("VP8X")) + skip += AV_RL32(w->last_pkt.data + skip + 4) + 8; + + w->frame_count ++; + if (w->frame_count == 1 && !trailer) { + avio_write(s->pb, "VP8X", 4); + avio_wl32(s->pb, 10); + avio_w8(s->pb, 2+16); + avio_wl24(s->pb, 0); + avio_wl24(s->pb, st->codec->width - 1); + avio_wl24(s->pb, st->codec->height - 1); + + avio_write(s->pb, "ANIM", 4); + avio_wl32(s->pb, 6); + avio_wl32(s->pb, 0xFFFFFFFF); + avio_wl16(s->pb, w->loop); + } + + if (w->frame_count > trailer) { + avio_write(s->pb, "ANMF", 4); + avio_wl32(s->pb, 16 + w->last_pkt.size - skip); + avio_wl24(s->pb, 0); + avio_wl24(s->pb, 0); + avio_wl24(s->pb, st->codec->width - 1); + avio_wl24(s->pb, st->codec->height - 1); + if (w->last_pkt.pts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE) { + avio_wl24(s->pb, pts - w->last_pkt.pts); + } else + avio_wl24(s->pb, w->last_pkt.duration); + avio_w8(s->pb, 0); + } + avio_write(s->pb, w->last_pkt.data + skip, w->last_pkt.size - skip); + av_free_packet(&w->last_pkt); + } + + return 0; +} + +static int webp_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + WebpContext *w = s->priv_data; + int ret; + + if ((ret = flush(s, 0, pkt->pts)) < 0) + return ret; + + av_copy_packet(&w->last_pkt, pkt); + + return 0; +} + +static int webp_write_trailer(AVFormatContext *s) +{ + WebpContext *w = s->priv_data; + unsigned filesize; + int ret; + + if ((ret = flush(s, 1, AV_NOPTS_VALUE)) < 0) + return ret; + + filesize = avio_tell(s->pb); + avio_seek(s->pb, 4, SEEK_SET); + avio_wl32(s->pb, filesize - 8); + + return 0; +} + +#define OFFSET(x) offsetof(WebpContext, x) +#define ENC AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "loop", "Number of times to loop the output: 0 - infinite loop", OFFSET(loop), + AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 65535, ENC }, + { NULL }, +}; + +static const AVClass webp_muxer_class = { + .class_name = "WebP muxer", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, + .option = options, +}; +AVOutputFormat ff_webp_muxer = { + .name = "webp", + .long_name = NULL_IF_CONFIG_SMALL("WebP"), + .extensions = "webp", + .priv_data_size = sizeof(WebpContext), + .video_codec = AV_CODEC_ID_WEBP, + .write_header = webp_write_header, + .write_packet = webp_write_packet, + .write_trailer = webp_write_trailer, + .priv_class = &webp_muxer_class, + .flags = AVFMT_VARIABLE_FPS, +};