Coverage Report

Created: 2026-05-16 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavformat/msnwc_tcp.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2008  Ramiro Polla
3
 *
4
 * This file is part of FFmpeg.
5
 *
6
 * FFmpeg is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with FFmpeg; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
#include "libavcodec/bytestream.h"
22
#include "avformat.h"
23
#include "demux.h"
24
#include "internal.h"
25
26
6.82G
#define HEADER_SIZE         24
27
28
/*
29
 * Header structure:
30
 *  uint16_t    ss;     // struct size
31
 *  uint16_t    width;  // frame width
32
 *  uint16_t    height; // frame height
33
 *  uint16_t    ff;     // keyframe + some other info(???)
34
 *  uint32_t    size;   // size of data
35
 *  uint32_t    fourcc; // ML20
36
 *  uint32_t    u3;     // ?
37
 *  uint32_t    ts;     // time
38
 */
39
40
static int msnwc_tcp_probe(const AVProbeData *p)
41
971k
{
42
971k
    int i;
43
44
3.41G
    for (i = 0; i + HEADER_SIZE <= p->buf_size; i++) {
45
3.40G
        uint16_t width, height;
46
3.40G
        uint32_t fourcc;
47
3.40G
        const uint8_t *bytestream = p->buf + i;
48
49
3.40G
        if (bytestream_get_le16(&bytestream) != HEADER_SIZE)
50
3.40G
            continue;
51
797k
        width  = bytestream_get_le16(&bytestream);
52
797k
        height = bytestream_get_le16(&bytestream);
53
797k
        if (!(width == 320 &&
54
787k
              height == 240) && !(width == 160 && height == 120))
55
782k
            continue;
56
14.6k
        bytestream += 2; // keyframe
57
14.6k
        bytestream += 4; // size
58
14.6k
        fourcc      = bytestream_get_le32(&bytestream);
59
14.6k
        if (fourcc != MKTAG('M', 'L', '2', '0'))
60
14.4k
            continue;
61
62
156
        if (i) {
63
126
            if (i < 14) /* starts with SwitchBoard connection info */
64
39
                return AVPROBE_SCORE_MAX / 2;
65
87
            else        /* starts in the middle of stream */
66
87
                return AVPROBE_SCORE_MAX / 3;
67
126
        } else {
68
30
            return AVPROBE_SCORE_MAX;
69
30
        }
70
156
    }
71
72
971k
    return 0;
73
971k
}
74
75
static int msnwc_tcp_read_header(AVFormatContext *ctx)
76
2.27k
{
77
2.27k
    AVIOContext *pb = ctx->pb;
78
2.27k
    AVCodecParameters *par;
79
2.27k
    AVStream *st;
80
81
2.27k
    st = avformat_new_stream(ctx, NULL);
82
2.27k
    if (!st)
83
0
        return AVERROR(ENOMEM);
84
85
2.27k
    par             = st->codecpar;
86
2.27k
    par->codec_type = AVMEDIA_TYPE_VIDEO;
87
2.27k
    par->codec_id   = AV_CODEC_ID_MIMIC;
88
2.27k
    par->codec_tag  = MKTAG('M', 'L', '2', '0');
89
90
2.27k
    avpriv_set_pts_info(st, 32, 1, 1000);
91
92
    /* Some files start with "connected\r\n\r\n".
93
     * So skip until we find the first byte of struct size */
94
3.88M
    while(avio_r8(pb) != HEADER_SIZE && !avio_feof(pb)) ;
95
96
2.27k
    if(avio_feof(pb)) {
97
93
        av_log(ctx, AV_LOG_ERROR, "Could not find valid start.\n");
98
93
        return AVERROR_INVALIDDATA;
99
93
    }
100
101
2.18k
    return 0;
102
2.27k
}
103
104
static int msnwc_tcp_read_packet(AVFormatContext *ctx, AVPacket *pkt)
105
278k
{
106
278k
    AVIOContext *pb = ctx->pb;
107
278k
    uint16_t keyframe;
108
278k
    uint32_t size, timestamp;
109
278k
    int ret;
110
111
278k
    avio_skip(pb, 1); /* one byte has been read ahead */
112
278k
    avio_skip(pb, 2);
113
278k
    avio_skip(pb, 2);
114
278k
    keyframe = avio_rl16(pb);
115
278k
    size     = avio_rl32(pb);
116
278k
    avio_skip(pb, 4);
117
278k
    avio_skip(pb, 4);
118
278k
    timestamp = avio_rl32(pb);
119
120
278k
    if (!size)
121
3.12k
        return AVERROR_INVALIDDATA;
122
123
275k
    if ((ret = av_get_packet(pb, pkt, size)) < 0)
124
885
        return ret;
125
126
274k
    avio_skip(pb, 1); /* Read ahead one byte of struct size like read_header */
127
128
274k
    pkt->pts          = timestamp;
129
274k
    pkt->dts          = timestamp;
130
274k
    pkt->stream_index = 0;
131
132
    /* Some aMsn generated videos (or was it Mercury Messenger?) don't set
133
     * this bit and rely on the codec to get keyframe information */
134
274k
    if (keyframe & 1)
135
60.7k
        pkt->flags |= AV_PKT_FLAG_KEY;
136
137
274k
    return HEADER_SIZE + size;
138
275k
}
139
140
const FFInputFormat ff_msnwc_tcp_demuxer = {
141
    .p.name      = "msnwctcp",
142
    .p.long_name = NULL_IF_CONFIG_SMALL("MSN TCP Webcam stream"),
143
    .read_probe  = msnwc_tcp_probe,
144
    .read_header = msnwc_tcp_read_header,
145
    .read_packet = msnwc_tcp_read_packet,
146
};