FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/vivo.c
Date: 2026-10-20 22:17:59
Exec Total Coverage
Lines: 15 182 8.2%
Functions: 1 4 25.0%
Branches: 11 116 9.5%

Line Branch Exec Source
1 /*
2 * Vivo stream demuxer
3 * Copyright (c) 2009 Daniel Verkamp <daniel at drv.nu>
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 /**
23 * @file
24 * @brief Vivo stream demuxer
25 * @author Daniel Verkamp <daniel at drv.nu>
26 * @sa http://wiki.multimedia.cx/index.php?title=Vivo
27 */
28
29 #include "libavutil/avstring.h"
30 #include "libavutil/parseutils.h"
31 #include "avformat.h"
32 #include "avio_internal.h"
33 #include "demux.h"
34 #include "internal.h"
35
36 typedef struct VivoContext {
37 int version;
38
39 int type;
40 int sequence;
41 int length;
42 int duration;
43
44 uint8_t text[1024 + 1];
45 } VivoContext;
46
47 8203 static int vivo_probe(const AVProbeData *p)
48 {
49 8203 const unsigned char *buf = p->buf;
50 8203 unsigned c, length = 0;
51
52 // stream must start with packet of type 0 and sequence number 0
53
2/2
✓ Branch 0 taken 6591 times.
✓ Branch 1 taken 1612 times.
8203 if (*buf++ != 0)
54 6591 return 0;
55
56 // read at most 2 bytes of coded length
57 1612 c = *buf++;
58 1612 length = c & 0x7F;
59
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1605 times.
1612 if (c & 0x80) {
60 7 c = *buf++;
61 7 length = (length << 7) | (c & 0x7F);
62 }
63
6/6
✓ Branch 0 taken 1608 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1605 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1601 times.
✓ Branch 5 taken 4 times.
1612 if (c & 0x80 || length > 1024 || length < 21)
64 1608 return 0;
65
66 4 buf += 2;
67
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (memcmp(buf, "Version:Vivo/", 13))
68 4 return 0;
69 ✗ buf += 13;
70
71 ✗ if (*buf < '0' || *buf > '2')
72 ✗ return 0;
73
74 ✗ return AVPROBE_SCORE_MAX;
75 }
76
77 ✗ static int vivo_get_packet_header(AVFormatContext *s)
78 {
79 ✗ VivoContext *vivo = s->priv_data;
80 ✗ AVIOContext *pb = s->pb;
81 ✗ unsigned c, get_length = 0;
82
83 ✗ if (avio_feof(pb))
84 ✗ return AVERROR_EOF;
85
86 ✗ c = avio_r8(pb);
87 ✗ if (c == 0x82) {
88 ✗ get_length = 1;
89 ✗ c = avio_r8(pb);
90 }
91
92 ✗ vivo->type = c >> 4;
93 ✗ vivo->sequence = c & 0xF;
94
95 ✗ switch (vivo->type) {
96 ✗ case 0: get_length = 1; break;
97 ✗ case 1: vivo->length = 128; break;
98 ✗ case 2: get_length = 1; break;
99 ✗ case 3: vivo->length = 40; break;
100 ✗ case 4: vivo->length = 24; break;
101 ✗ default:
102 ✗ av_log(s, AV_LOG_ERROR, "unknown packet type %d\n", vivo->type);
103 ✗ return AVERROR_INVALIDDATA;
104 }
105
106 ✗ if (get_length) {
107 ✗ c = avio_r8(pb);
108 ✗ vivo->length = c & 0x7F;
109 ✗ if (c & 0x80) {
110 ✗ c = avio_r8(pb);
111 ✗ vivo->length = (vivo->length << 7) | (c & 0x7F);
112
113 ✗ if (c & 0x80) {
114 ✗ av_log(s, AV_LOG_ERROR, "coded length is more than two bytes\n");
115 ✗ return AVERROR_INVALIDDATA;
116 }
117 }
118 }
119
120 ✗ return 0;
121 }
122
123 ✗ static int vivo_read_header(AVFormatContext *s)
124 {
125 ✗ VivoContext *vivo = s->priv_data;
126 ✗ AVRational fps = { 0 };
127 AVStream *ast, *vst;
128 unsigned char *line, *line_end, *key, *value;
129 long value_int;
130 int ret, value_used;
131 ✗ int64_t duration = 0;
132 char *end_value;
133
134 ✗ vst = avformat_new_stream(s, NULL);
135 ✗ ast = avformat_new_stream(s, NULL);
136 ✗ if (!ast || !vst)
137 ✗ return AVERROR(ENOMEM);
138
139 ✗ ast->codecpar->sample_rate = 8000;
140
141 while (1) {
142 ✗ if ((ret = vivo_get_packet_header(s)) < 0)
143 ✗ return ret;
144
145 // done reading all text header packets?
146 ✗ if (vivo->sequence || vivo->type)
147 break;
148
149 ✗ if (vivo->length <= 1024) {
150 ✗ if ((ret = ffio_read_size(s->pb, vivo->text, vivo->length)) < 0)
151 ✗ return ret;
152 ✗ vivo->text[vivo->length] = 0;
153 } else {
154 ✗ av_log(s, AV_LOG_WARNING, "too big header, skipping\n");
155 ✗ avio_skip(s->pb, vivo->length);
156 ✗ continue;
157 }
158
159 ✗ line = vivo->text;
160 ✗ while (*line) {
161 ✗ line_end = strstr(line, "\r\n");
162 ✗ if (!line_end)
163 ✗ break;
164
165 ✗ *line_end = 0;
166 ✗ key = line;
167 ✗ line = line_end + 2; // skip \r\n
168
169 ✗ if (line_end == key) // skip blank lines
170 ✗ continue;
171
172 ✗ value = strchr(key, ':');
173 ✗ if (!value) {
174 ✗ av_log(s, AV_LOG_WARNING, "missing colon in key:value pair '%s'\n",
175 key);
176 ✗ continue;
177 }
178
179 ✗ *value++ = 0;
180
181 ✗ av_log(s, AV_LOG_DEBUG, "header: '%s' = '%s'\n", key, value);
182
183 ✗ value_int = strtol(value, &end_value, 10);
184 ✗ value_used = 0;
185 ✗ if (*end_value == 0) { // valid integer
186 ✗ av_log(s, AV_LOG_DEBUG, "got a valid integer (%ld)\n", value_int);
187 ✗ value_used = 1;
188 ✗ if (!strcmp(key, "Duration")) {
189 ✗ duration = value_int;
190 ✗ } else if (!strcmp(key, "Width")) {
191 ✗ vst->codecpar->width = value_int;
192 ✗ } else if (!strcmp(key, "Height")) {
193 ✗ vst->codecpar->height = value_int;
194 ✗ } else if (!strcmp(key, "TimeUnitNumerator")) {
195 ✗ fps.num = value_int / 1000;
196 ✗ } else if (!strcmp(key, "TimeUnitDenominator")) {
197 ✗ fps.den = value_int;
198 ✗ } else if (!strcmp(key, "SamplingFrequency")) {
199 ✗ ast->codecpar->sample_rate = value_int;
200 ✗ } else if (!strcmp(key, "NominalBitrate")) {
201 ✗ } else if (!strcmp(key, "Length")) {
202 // size of file
203 } else {
204 ✗ value_used = 0;
205 }
206 }
207
208 ✗ if (!strcmp(key, "Version")) {
209 ✗ if (sscanf(value, "Vivo/%d.", &vivo->version) != 1)
210 ✗ return AVERROR_INVALIDDATA;
211 ✗ value_used = 1;
212 ✗ } else if (!strcmp(key, "FPS")) {
213 double d;
214 ✗ if (av_sscanf(value, "%lf", &d) != 1)
215 ✗ return AVERROR_INVALIDDATA;
216
217 ✗ value_used = 1;
218 ✗ if (!fps.num && !fps.den)
219 ✗ fps = av_inv_q(av_d2q(d, 10000));
220 }
221
222 ✗ if (!value_used)
223 ✗ av_dict_set(&s->metadata, key, value, 0);
224 }
225 }
226 ✗ if (!fps.num || !fps.den)
227 ✗ fps = (AVRational){ 1, 25 };
228
229 ✗ avpriv_set_pts_info(ast, 64, 1, ast->codecpar->sample_rate);
230 ✗ avpriv_set_pts_info(vst, 64, fps.num, fps.den);
231 ✗ if (duration)
232 ✗ s->duration = av_rescale(duration, 1000, 1);
233
234 ✗ vst->start_time = 0;
235 ✗ vst->codecpar->codec_tag = 0;
236 ✗ vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
237
238 ✗ if (vivo->version == 1) {
239 ✗ vst->codecpar->codec_id = AV_CODEC_ID_H263;
240 ✗ ast->codecpar->codec_id = AV_CODEC_ID_G723_1;
241 ✗ ast->codecpar->bits_per_coded_sample = 8;
242 ✗ ast->codecpar->block_align = 24;
243 ✗ ast->codecpar->bit_rate = 6400;
244 } else {
245 ✗ ast->codecpar->codec_id = AV_CODEC_ID_SIREN;
246 ✗ ast->codecpar->bits_per_coded_sample = 16;
247 ✗ ast->codecpar->block_align = 40;
248 ✗ ast->codecpar->bit_rate = 6400;
249 ✗ vivo->duration = 320;
250 }
251
252 ✗ ast->start_time = 0;
253 ✗ ast->codecpar->codec_tag = 0;
254 ✗ ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
255 ✗ ast->codecpar->ch_layout.nb_channels = 1;
256
257 ✗ return 0;
258 }
259
260 ✗ static int vivo_read_packet(AVFormatContext *s, AVPacket *pkt)
261 {
262 ✗ VivoContext *vivo = s->priv_data;
263 ✗ AVIOContext *pb = s->pb;
264 ✗ unsigned old_sequence = vivo->sequence, old_type = vivo->type;
265 ✗ int stream_index, duration, ret = 0;
266
267 ✗ restart:
268
269 ✗ if (avio_feof(pb))
270 ✗ return AVERROR_EOF;
271
272 ✗ switch (vivo->type) {
273 ✗ case 0:
274 ✗ avio_skip(pb, vivo->length);
275 ✗ if ((ret = vivo_get_packet_header(s)) < 0)
276 ✗ return ret;
277 ✗ goto restart;
278 ✗ case 1:
279 case 2: // video
280 ✗ stream_index = 0;
281 ✗ duration = 1;
282 ✗ break;
283 ✗ case 3:
284 case 4: // audio
285 ✗ stream_index = 1;
286 ✗ duration = vivo->duration;
287 ✗ break;
288 ✗ default:
289 ✗ av_log(s, AV_LOG_ERROR, "unknown packet type %d\n", vivo->type);
290 ✗ return AVERROR_INVALIDDATA;
291 }
292
293 ✗ if ((ret = av_get_packet(pb, pkt, vivo->length)) < 0)
294 ✗ return ret;
295
296 // get next packet header
297 ✗ if ((ret = vivo_get_packet_header(s)) < 0)
298 ✗ return ret;
299
300 ✗ while (vivo->sequence == old_sequence &&
301 ✗ (((vivo->type - 1) >> 1) == ((old_type - 1) >> 1))) {
302 ✗ if (avio_feof(pb)) {
303 ✗ return AVERROR_EOF;
304 }
305
306 ✗ if ((ret = av_append_packet(pb, pkt, vivo->length)) < 0)
307 ✗ return ret;
308
309 // get next packet header
310 ✗ if ((ret = vivo_get_packet_header(s)) < 0)
311 ✗ return ret;
312 }
313
314 ✗ pkt->stream_index = stream_index;
315 ✗ pkt->duration = duration;
316
317 ✗ return ret;
318 }
319
320 const FFInputFormat ff_vivo_demuxer = {
321 .p.name = "vivo",
322 .p.long_name = NULL_IF_CONFIG_SMALL("Vivo"),
323 .p.extensions = "viv",
324 .priv_data_size = sizeof(VivoContext),
325 .read_probe = vivo_probe,
326 .read_header = vivo_read_header,
327 .read_packet = vivo_read_packet,
328 };
329