| @@ -63,6 +63,7 @@ version next: | |||||
| - Smooth Streaming live segmenter muxer | - Smooth Streaming live segmenter muxer | ||||
| - F4V muxer | - F4V muxer | ||||
| - sendcmd and asendcmd filters | - sendcmd and asendcmd filters | ||||
| - WebVTT demuxer and decoder (simple tags supported) | |||||
| version 0.11: | version 0.11: | ||||
| @@ -898,6 +898,7 @@ performance on systems without hardware floating point support). | |||||
| @item SubRip (SRT) @tab X @tab X @tab X @tab X | @item SubRip (SRT) @tab X @tab X @tab X @tab X | ||||
| @item SubViewer @tab @tab X @tab @tab X | @item SubViewer @tab @tab X @tab @tab X | ||||
| @item 3GPP Timed Text @tab @tab @tab X @tab X | @item 3GPP Timed Text @tab @tab @tab X @tab X | ||||
| @item WebVTT @tab @tab X @tab @tab X | |||||
| @item XSUB @tab @tab @tab X @tab X | @item XSUB @tab @tab @tab X @tab X | ||||
| @end multitable | @end multitable | ||||
| @@ -463,6 +463,7 @@ OBJS-$(CONFIG_VP6_DECODER) += vp6.o vp56.o vp56data.o vp56dsp.o \ | |||||
| OBJS-$(CONFIG_VP8_DECODER) += vp8.o vp8dsp.o vp56rac.o | OBJS-$(CONFIG_VP8_DECODER) += vp8.o vp8dsp.o vp56rac.o | ||||
| OBJS-$(CONFIG_VQA_DECODER) += vqavideo.o | OBJS-$(CONFIG_VQA_DECODER) += vqavideo.o | ||||
| OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o | OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o | ||||
| OBJS-$(CONFIG_WEBVTT_DECODER) += webvttdec.o | |||||
| OBJS-$(CONFIG_WMALOSSLESS_DECODER) += wmalosslessdec.o wma_common.o | OBJS-$(CONFIG_WMALOSSLESS_DECODER) += wmalosslessdec.o wma_common.o | ||||
| OBJS-$(CONFIG_WMAPRO_DECODER) += wmaprodec.o wma.o wma_common.o | OBJS-$(CONFIG_WMAPRO_DECODER) += wmaprodec.o wma.o wma_common.o | ||||
| OBJS-$(CONFIG_WMAV1_DECODER) += wmadec.o wma.o wma_common.o aactab.o | OBJS-$(CONFIG_WMAV1_DECODER) += wmadec.o wma.o wma_common.o aactab.o | ||||
| @@ -422,6 +422,7 @@ void avcodec_register_all(void) | |||||
| REGISTER_ENCDEC (SRT, srt); | REGISTER_ENCDEC (SRT, srt); | ||||
| REGISTER_ENCDEC (SUBRIP, subrip); | REGISTER_ENCDEC (SUBRIP, subrip); | ||||
| REGISTER_DECODER (SUBVIEWER, subviewer); | REGISTER_DECODER (SUBVIEWER, subviewer); | ||||
| REGISTER_DECODER (WEBVTT, webvtt); | |||||
| REGISTER_ENCDEC (XSUB, xsub); | REGISTER_ENCDEC (XSUB, xsub); | ||||
| /* external libraries */ | /* external libraries */ | ||||
| @@ -448,6 +448,7 @@ enum AVCodecID { | |||||
| AV_CODEC_ID_REALTEXT = MKBETAG('R','T','X','T'), | AV_CODEC_ID_REALTEXT = MKBETAG('R','T','X','T'), | ||||
| AV_CODEC_ID_SUBVIEWER = MKBETAG('S','u','b','V'), | AV_CODEC_ID_SUBVIEWER = MKBETAG('S','u','b','V'), | ||||
| AV_CODEC_ID_SUBRIP = MKBETAG('S','R','i','p'), | AV_CODEC_ID_SUBRIP = MKBETAG('S','R','i','p'), | ||||
| AV_CODEC_ID_WEBVTT = MKBETAG('W','V','T','T'), | |||||
| /* other specific kind of codecs (generally used for attachments) */ | /* other specific kind of codecs (generally used for attachments) */ | ||||
| AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs. | AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs. | ||||
| @@ -2342,6 +2342,12 @@ static const AVCodecDescriptor codec_descriptors[] = { | |||||
| .name = "subviewer", | .name = "subviewer", | ||||
| .long_name = NULL_IF_CONFIG_SMALL("SubViewer subtitle"), | .long_name = NULL_IF_CONFIG_SMALL("SubViewer subtitle"), | ||||
| }, | }, | ||||
| { | |||||
| .id = AV_CODEC_ID_WEBVTT, | |||||
| .type = AVMEDIA_TYPE_SUBTITLE, | |||||
| .name = "webvtt", | |||||
| .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"), | |||||
| }, | |||||
| { | { | ||||
| .id = AV_CODEC_ID_BINTEXT, | .id = AV_CODEC_ID_BINTEXT, | ||||
| .type = AVMEDIA_TYPE_VIDEO, | .type = AVMEDIA_TYPE_VIDEO, | ||||
| @@ -27,7 +27,7 @@ | |||||
| */ | */ | ||||
| #define LIBAVCODEC_VERSION_MAJOR 54 | #define LIBAVCODEC_VERSION_MAJOR 54 | ||||
| #define LIBAVCODEC_VERSION_MINOR 57 | |||||
| #define LIBAVCODEC_VERSION_MINOR 58 | |||||
| #define LIBAVCODEC_VERSION_MICRO 100 | #define LIBAVCODEC_VERSION_MICRO 100 | ||||
| #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ | #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ | ||||
| @@ -0,0 +1,100 @@ | |||||
| /* | |||||
| * Copyright (c) 2012 Clément Bœsch | |||||
| * | |||||
| * 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 | |||||
| */ | |||||
| /** | |||||
| * @file | |||||
| * WebVTT subtitle decoder | |||||
| * @see http://dev.w3.org/html5/webvtt/ | |||||
| * @todo need to support extended markups and cue settings | |||||
| */ | |||||
| #include "avcodec.h" | |||||
| #include "ass.h" | |||||
| #include "libavutil/bprint.h" | |||||
| static const struct { | |||||
| const char *from; | |||||
| const char *to; | |||||
| } webvtt_tag_replace[] = { | |||||
| {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"}, | |||||
| {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"}, | |||||
| {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"}, | |||||
| {"{", "\\{"}, {"}", "\\}"}, // escape to avoid ASS markup conflicts | |||||
| }; | |||||
| static int webvtt_event_to_ass(AVBPrint *buf, const char *p) | |||||
| { | |||||
| int i, skip = 0; | |||||
| while (*p) { | |||||
| for (i = 0; i < FF_ARRAY_ELEMS(webvtt_tag_replace); i++) { | |||||
| const char *from = webvtt_tag_replace[i].from; | |||||
| const size_t len = strlen(from); | |||||
| if (!strncmp(p, from, len)) { | |||||
| av_bprintf(buf, "%s", webvtt_tag_replace[i].to); | |||||
| p += len; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (!*p) | |||||
| break; | |||||
| if (*p == '<') | |||||
| skip = 1; | |||||
| else if (*p == '>') | |||||
| skip = 0; | |||||
| else if (p[0] == '\n' && p[1]) | |||||
| av_bprintf(buf, "\\N"); | |||||
| else if (!skip && *p != '\r') | |||||
| av_bprint_chars(buf, *p, 1); | |||||
| p++; | |||||
| } | |||||
| av_bprintf(buf, "\r\n"); | |||||
| return 0; | |||||
| } | |||||
| static int webvtt_decode_frame(AVCodecContext *avctx, | |||||
| void *data, int *got_sub_ptr, AVPacket *avpkt) | |||||
| { | |||||
| AVSubtitle *sub = data; | |||||
| const char *ptr = avpkt->data; | |||||
| AVBPrint buf; | |||||
| av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); | |||||
| if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr)) { | |||||
| int ts_start = av_rescale_q(avpkt->pts, avctx->time_base, (AVRational){1,100}); | |||||
| int ts_duration = avpkt->duration != -1 ? | |||||
| av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,100}) : -1; | |||||
| ff_ass_add_rect(sub, buf.str, ts_start, ts_duration, 0); | |||||
| } | |||||
| *got_sub_ptr = sub->num_rects > 0; | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| return avpkt->size; | |||||
| } | |||||
| AVCodec ff_webvtt_decoder = { | |||||
| .name = "webvtt", | |||||
| .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"), | |||||
| .type = AVMEDIA_TYPE_SUBTITLE, | |||||
| .id = AV_CODEC_ID_WEBVTT, | |||||
| .decode = webvtt_decode_frame, | |||||
| .init = ff_ass_subtitle_header_default, | |||||
| }; | |||||
| @@ -355,6 +355,7 @@ OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o | |||||
| OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ | OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ | ||||
| isom.o avc.o \ | isom.o avc.o \ | ||||
| flacenc_header.o avlanguage.o | flacenc_header.o avlanguage.o | ||||
| OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o | |||||
| OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o | OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o | ||||
| OBJS-$(CONFIG_WSVQA_DEMUXER) += westwood_vqa.o | OBJS-$(CONFIG_WSVQA_DEMUXER) += westwood_vqa.o | ||||
| OBJS-$(CONFIG_WTV_DEMUXER) += wtvdec.o wtv.o asfdec.o asf.o asfcrypt.o \ | OBJS-$(CONFIG_WTV_DEMUXER) += wtvdec.o wtv.o asfdec.o asf.o asfcrypt.o \ | ||||
| @@ -252,6 +252,7 @@ void av_register_all(void) | |||||
| REGISTER_MUXDEMUX (WAV, wav); | REGISTER_MUXDEMUX (WAV, wav); | ||||
| REGISTER_DEMUXER (WC3, wc3); | REGISTER_DEMUXER (WC3, wc3); | ||||
| REGISTER_MUXER (WEBM, webm); | REGISTER_MUXER (WEBM, webm); | ||||
| REGISTER_DEMUXER (WEBVTT, webvtt); | |||||
| REGISTER_DEMUXER (WSAUD, wsaud); | REGISTER_DEMUXER (WSAUD, wsaud); | ||||
| REGISTER_DEMUXER (WSVQA, wsvqa); | REGISTER_DEMUXER (WSVQA, wsvqa); | ||||
| REGISTER_MUXDEMUX (WTV, wtv); | REGISTER_MUXDEMUX (WTV, wtv); | ||||
| @@ -30,8 +30,8 @@ | |||||
| #include "libavutil/avutil.h" | #include "libavutil/avutil.h" | ||||
| #define LIBAVFORMAT_VERSION_MAJOR 54 | #define LIBAVFORMAT_VERSION_MAJOR 54 | ||||
| #define LIBAVFORMAT_VERSION_MINOR 27 | |||||
| #define LIBAVFORMAT_VERSION_MICRO 101 | |||||
| #define LIBAVFORMAT_VERSION_MINOR 28 | |||||
| #define LIBAVFORMAT_VERSION_MICRO 100 | |||||
| #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ | #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ | ||||
| LIBAVFORMAT_VERSION_MINOR, \ | LIBAVFORMAT_VERSION_MINOR, \ | ||||
| @@ -0,0 +1,188 @@ | |||||
| /* | |||||
| * Copyright (c) 2012 Clément Bœsch | |||||
| * | |||||
| * 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 | |||||
| */ | |||||
| /** | |||||
| * @file | |||||
| * WebVTT subtitle demuxer | |||||
| * @see http://dev.w3.org/html5/webvtt/ | |||||
| */ | |||||
| #include "avformat.h" | |||||
| #include "internal.h" | |||||
| #include "subtitles.h" | |||||
| #include "libavutil/bprint.h" | |||||
| #include "libavutil/intreadwrite.h" | |||||
| typedef struct { | |||||
| FFDemuxSubtitlesQueue q; | |||||
| } WebVTTContext; | |||||
| static int webvtt_probe(AVProbeData *p) | |||||
| { | |||||
| const uint8_t *ptr = p->buf; | |||||
| if (AV_RB24(ptr) == 0xEFBBBF) | |||||
| ptr += 3; /* skip UTF-8 BOM */ | |||||
| if (!strncmp(ptr, "WEBVTT", 6) && | |||||
| (!ptr[6] || strchr("\n\r\t ", ptr[6]))) | |||||
| return AVPROBE_SCORE_MAX; | |||||
| return 0; | |||||
| } | |||||
| static int64_t read_ts(const char *s) | |||||
| { | |||||
| int hh, mm, ss, ms; | |||||
| if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600 + mm*60 + ss) * 1000 + ms; | |||||
| if (sscanf(s, "%u:%u.%u", &mm, &ss, &ms) == 3) return ( mm*60 + ss) * 1000 + ms; | |||||
| return AV_NOPTS_VALUE; | |||||
| } | |||||
| static int64_t extract_cue(AVBPrint *buf, AVIOContext *pb) | |||||
| { | |||||
| int prev_chr_is_eol = 0; | |||||
| int64_t pos = avio_tell(pb); | |||||
| av_bprint_clear(buf); | |||||
| for (;;) { | |||||
| char c = avio_r8(pb); | |||||
| if (!c) | |||||
| break; | |||||
| if (c == '\r' || c == '\n') { | |||||
| if (prev_chr_is_eol) | |||||
| break; | |||||
| prev_chr_is_eol = (c == '\n'); | |||||
| } else | |||||
| prev_chr_is_eol = 0; | |||||
| if (c != '\r') | |||||
| av_bprint_chars(buf, c, 1); | |||||
| } | |||||
| av_bprint_chars(buf, '\0', 1); | |||||
| return pos; | |||||
| } | |||||
| static int webvtt_read_header(AVFormatContext *s) | |||||
| { | |||||
| WebVTTContext *webvtt = s->priv_data; | |||||
| AVBPrint header, cue; | |||||
| int res = 0; | |||||
| AVStream *st = avformat_new_stream(s, NULL); | |||||
| if (!st) | |||||
| return AVERROR(ENOMEM); | |||||
| avpriv_set_pts_info(st, 64, 1, 1000); | |||||
| st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; | |||||
| st->codec->codec_id = AV_CODEC_ID_WEBVTT; | |||||
| av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); | |||||
| av_bprint_init(&cue, 0, AV_BPRINT_SIZE_UNLIMITED); | |||||
| for (;;) { | |||||
| int i, len; | |||||
| int64_t pos = extract_cue(&cue, s->pb); | |||||
| AVPacket *sub; | |||||
| const char *p = cue.str; | |||||
| const char *identifier = p; | |||||
| //const char *settings = NULL; | |||||
| int64_t ts_start, ts_end; | |||||
| if (!*p) // EOF | |||||
| break; | |||||
| /* ignore header chunk */ | |||||
| if (!strncmp(p, "\xEF\xBB\xBFWEBVTT", 9) || | |||||
| !strncmp(p, "WEBVTT", 6)) | |||||
| continue; | |||||
| /* optional cue identifier (can be a number like in SRT or some kind of | |||||
| * chaptering id), silently skip it */ | |||||
| for (i = 0; p[i] && p[i] != '\n'; i++) { | |||||
| if (!strncmp(p + i, "-->", 3)) { | |||||
| identifier = NULL; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (identifier) | |||||
| p += strcspn(p, "\n"); | |||||
| /* cue timestamps */ | |||||
| if ((ts_start = read_ts(p)) == AV_NOPTS_VALUE) | |||||
| break; | |||||
| if (!(p = strstr(p, "-->"))) | |||||
| break; | |||||
| p += 3; | |||||
| do p++; while (*p == ' ' || *p == '\t'); | |||||
| if ((ts_end = read_ts(p)) == AV_NOPTS_VALUE) | |||||
| break; | |||||
| /* optional cue settings, TODO: store in side_data */ | |||||
| p += strcspn(p, "\n\t "); | |||||
| while (*p == '\t' || *p == ' ') | |||||
| p++; | |||||
| if (*p != '\n') { | |||||
| //settings = p; | |||||
| p += strcspn(p, "\n"); | |||||
| } | |||||
| if (*p == '\n') | |||||
| p++; | |||||
| /* create packet */ | |||||
| len = cue.str + cue.len - p - 1; | |||||
| sub = ff_subtitles_queue_insert(&webvtt->q, p, len, 0); | |||||
| if (!sub) { | |||||
| res = AVERROR(ENOMEM); | |||||
| goto end; | |||||
| } | |||||
| sub->pos = pos; | |||||
| sub->pts = ts_start; | |||||
| sub->duration = ts_end - ts_start; | |||||
| } | |||||
| ff_subtitles_queue_finalize(&webvtt->q); | |||||
| end: | |||||
| av_bprint_finalize(&cue, NULL); | |||||
| av_bprint_finalize(&header, NULL); | |||||
| return res; | |||||
| } | |||||
| static int webvtt_read_packet(AVFormatContext *s, AVPacket *pkt) | |||||
| { | |||||
| WebVTTContext *webvtt = s->priv_data; | |||||
| return ff_subtitles_queue_read_packet(&webvtt->q, pkt); | |||||
| } | |||||
| static int webvtt_read_close(AVFormatContext *s) | |||||
| { | |||||
| WebVTTContext *webvtt = s->priv_data; | |||||
| ff_subtitles_queue_clean(&webvtt->q); | |||||
| return 0; | |||||
| } | |||||
| AVInputFormat ff_webvtt_demuxer = { | |||||
| .name = "webvtt", | |||||
| .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"), | |||||
| .priv_data_size = sizeof(WebVTTContext), | |||||
| .read_probe = webvtt_probe, | |||||
| .read_header = webvtt_read_header, | |||||
| .read_packet = webvtt_read_packet, | |||||
| .read_close = webvtt_read_close, | |||||
| .flags = AVFMT_GENERIC_INDEX, | |||||
| .extensions = "vtt", | |||||
| }; | |||||
| @@ -25,5 +25,8 @@ fate-sub-subripenc: CMD = md5 -i $(SAMPLES)/sub/MovText_capability_tester.mp4 -s | |||||
| FATE_SUBTITLES += fate-sub-subviewer | FATE_SUBTITLES += fate-sub-subviewer | ||||
| fate-sub-subviewer: CMD = md5 -i $(SAMPLES)/sub/SubViewer_capability_tester.sub -f ass | fate-sub-subviewer: CMD = md5 -i $(SAMPLES)/sub/SubViewer_capability_tester.sub -f ass | ||||
| FATE_SUBTITLES += fate-sub-webvtt | |||||
| fate-sub-webvtt: CMD = md5 -i $(SAMPLES)/sub/WebVTT_capability_tester.vtt -f ass | |||||
| FATE_SAMPLES_FFMPEG += $(FATE_SUBTITLES) | FATE_SAMPLES_FFMPEG += $(FATE_SUBTITLES) | ||||
| fate-subtitles: $(FATE_SUBTITLES) | fate-subtitles: $(FATE_SUBTITLES) | ||||
| @@ -0,0 +1 @@ | |||||
| 5384a70c89ddca4b007fb7ffba95cffb | |||||