blob: c534ab117d94e2aee590f03641681d5031aa83c4
1 | /* |
2 | * On2 VP8 parser for Ogg |
3 | * Copyright (C) 2013 James Almer |
4 | * |
5 | * This file is part of FFmpeg. |
6 | * |
7 | * FFmpeg is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * |
12 | * FFmpeg is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with FFmpeg; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ |
21 | |
22 | #include "libavutil/intreadwrite.h" |
23 | |
24 | #include "avformat.h" |
25 | #include "internal.h" |
26 | #include "oggdec.h" |
27 | |
28 | #define VP8_HEADER_SIZE 26 |
29 | |
30 | static int vp8_header(AVFormatContext *s, int idx) |
31 | { |
32 | struct ogg *ogg = s->priv_data; |
33 | struct ogg_stream *os = ogg->streams + idx; |
34 | uint8_t *p = os->buf + os->pstart; |
35 | AVStream *st = s->streams[idx]; |
36 | AVRational framerate; |
37 | |
38 | if (os->psize < 7 || p[0] != 0x4f) |
39 | return 0; |
40 | |
41 | switch (p[5]){ |
42 | case 0x01: |
43 | if (os->psize < VP8_HEADER_SIZE) { |
44 | av_log(s, AV_LOG_ERROR, "Invalid OggVP8 header packet"); |
45 | return AVERROR_INVALIDDATA; |
46 | } |
47 | |
48 | if (p[6] != 1) { |
49 | av_log(s, AV_LOG_WARNING, |
50 | "Unknown OggVP8 version %d.%d\n", p[6], p[7]); |
51 | return AVERROR_INVALIDDATA; |
52 | } |
53 | |
54 | st->codecpar->width = AV_RB16(p + 8); |
55 | st->codecpar->height = AV_RB16(p + 10); |
56 | st->sample_aspect_ratio.num = AV_RB24(p + 12); |
57 | st->sample_aspect_ratio.den = AV_RB24(p + 15); |
58 | framerate.num = AV_RB32(p + 18); |
59 | framerate.den = AV_RB32(p + 22); |
60 | |
61 | avpriv_set_pts_info(st, 64, framerate.den, framerate.num); |
62 | st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
63 | st->codecpar->codec_id = AV_CODEC_ID_VP8; |
64 | st->need_parsing = AVSTREAM_PARSE_HEADERS; |
65 | break; |
66 | case 0x02: |
67 | if (p[6] != 0x20) |
68 | return AVERROR_INVALIDDATA; |
69 | ff_vorbis_stream_comment(s, st, p + 7, os->psize - 7); |
70 | break; |
71 | default: |
72 | av_log(s, AV_LOG_ERROR, "Unknown VP8 header type 0x%02X\n", p[5]); |
73 | return AVERROR_INVALIDDATA; |
74 | } |
75 | |
76 | return 1; |
77 | } |
78 | |
79 | static uint64_t vp8_gptopts(AVFormatContext *s, int idx, |
80 | uint64_t granule, int64_t *dts) |
81 | { |
82 | struct ogg *ogg = s->priv_data; |
83 | struct ogg_stream *os = ogg->streams + idx; |
84 | |
85 | int invcnt = !((granule >> 30) & 3); |
86 | // If page granule is that of an invisible vp8 frame, its pts will be |
87 | // that of the end of the next visible frame. We subtract 1 for those |
88 | // to prevent messing up pts calculations. |
89 | uint64_t pts = (granule >> 32) - invcnt; |
90 | uint32_t dist = (granule >> 3) & 0x07ffffff; |
91 | |
92 | if (!dist) |
93 | os->pflags |= AV_PKT_FLAG_KEY; |
94 | |
95 | if (dts) |
96 | *dts = pts; |
97 | |
98 | return pts; |
99 | } |
100 | |
101 | static int vp8_packet(AVFormatContext *s, int idx) |
102 | { |
103 | struct ogg *ogg = s->priv_data; |
104 | struct ogg_stream *os = ogg->streams + idx; |
105 | uint8_t *p = os->buf + os->pstart; |
106 | |
107 | if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && |
108 | !(os->flags & OGG_FLAG_EOS)) { |
109 | int seg; |
110 | int duration; |
111 | uint8_t *last_pkt = p; |
112 | uint8_t *next_pkt; |
113 | |
114 | seg = os->segp; |
115 | duration = (last_pkt[0] >> 4) & 1; |
116 | next_pkt = last_pkt += os->psize; |
117 | for (; seg < os->nsegs; seg++) { |
118 | if (os->segments[seg] < 255) { |
119 | duration += (last_pkt[0] >> 4) & 1; |
120 | last_pkt = next_pkt + os->segments[seg]; |
121 | } |
122 | next_pkt += os->segments[seg]; |
123 | } |
124 | os->lastpts = |
125 | os->lastdts = vp8_gptopts(s, idx, os->granule, NULL) - duration; |
126 | if(s->streams[idx]->start_time == AV_NOPTS_VALUE) { |
127 | s->streams[idx]->start_time = os->lastpts; |
128 | if (s->streams[idx]->duration) |
129 | s->streams[idx]->duration -= s->streams[idx]->start_time; |
130 | } |
131 | } |
132 | |
133 | if (os->psize > 0) |
134 | os->pduration = (p[0] >> 4) & 1; |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | const struct ogg_codec ff_vp8_codec = { |
140 | .magic = "OVP80", |
141 | .magicsize = 5, |
142 | .header = vp8_header, |
143 | .packet = vp8_packet, |
144 | .gptopts = vp8_gptopts, |
145 | .nb_header = 1, |
146 | }; |
147 |