Coverage Report

Created: 2026-07-25 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavformat/mtv.c
Line
Count
Source
1
/*
2
 * mtv demuxer
3
 * Copyright (c) 2006 Reynaldo H. Verdejo Pinochet
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
 * MTV demuxer.
25
 */
26
27
#include "libavutil/intreadwrite.h"
28
#include "libavutil/mem.h"
29
#include "avformat.h"
30
#include "demux.h"
31
#include "internal.h"
32
33
194k
#define MTV_ASUBCHUNK_DATA_SIZE 500
34
2.80k
#define MTV_HEADER_SIZE 512
35
382k
#define MTV_AUDIO_PADDING_SIZE 12
36
5.69k
#define MTV_IMAGE_DEFAULT_BPP 16
37
2.03k
#define MTV_AUDIO_SAMPLING_RATE 44100
38
39
typedef struct MTVDemuxContext {
40
41
    unsigned int file_size;         ///< filesize, not always right
42
    unsigned int segments;          ///< number of 512 byte segments
43
    unsigned int audio_identifier;  ///< 'MP3' on all files I have seen
44
    unsigned int audio_br;          ///< bitrate of audio channel (mp3)
45
    unsigned int img_colorfmt;      ///< frame colorfmt rgb 565/555
46
    unsigned int img_bpp;           ///< frame bits per pixel
47
    unsigned int img_width;
48
    unsigned int img_height;
49
    unsigned int img_segment_size;  ///< size of image segment
50
    unsigned int video_fps;
51
    unsigned int full_segment_size;
52
53
} MTVDemuxContext;
54
55
static int mtv_probe(const AVProbeData *p)
56
967k
{
57
    /* we need at least 57 bytes from the header
58
     * to try parsing all required fields
59
     */
60
967k
    if (p->buf_size < 57)
61
274k
        return 0;
62
63
    /* Magic is 'AMV' */
64
693k
    if (*p->buf != 'A' || *(p->buf + 1) != 'M' || *(p->buf + 2) != 'V')
65
690k
        return 0;
66
67
    /* Audio magic is always MP3 */
68
2.47k
    if (p->buf[43] != 'M' || p->buf[44] != 'P' || p->buf[45] != '3')
69
725
        return 0;
70
71
    /* Check for nonzero in bpp and (width|height) header fields */
72
1.75k
    if(!(p->buf[51] && AV_RL16(&p->buf[52]) | AV_RL16(&p->buf[54])))
73
270
        return 0;
74
75
    /* If width or height are 0 then imagesize header field should not */
76
1.48k
    if(!AV_RL16(&p->buf[52]) || !AV_RL16(&p->buf[54]))
77
367
    {
78
367
        if(!!AV_RL16(&p->buf[56]))
79
204
            return AVPROBE_SCORE_EXTENSION;
80
163
        else
81
163
            return 0;
82
367
    }
83
84
    /* Image bpp is not an absolutely required
85
     * field as we latter claim it should be 16
86
     * no matter what. All samples in the wild
87
     * are RGB565/555.
88
     */
89
1.11k
    if(p->buf[51] != MTV_IMAGE_DEFAULT_BPP)
90
350
        return AVPROBE_SCORE_EXTENSION / 2;
91
92
    /* We had enough data to parse header values
93
     * but we expect to be able to get 512 bytes
94
     * of header to be sure.
95
     */
96
765
    if (p->buf_size < MTV_HEADER_SIZE)
97
365
        return AVPROBE_SCORE_EXTENSION;
98
99
400
    return AVPROBE_SCORE_MAX;
100
765
}
101
102
static int mtv_read_header(AVFormatContext *s)
103
2.30k
{
104
2.30k
    MTVDemuxContext *mtv = s->priv_data;
105
2.30k
    AVIOContext   *pb  = s->pb;
106
2.30k
    AVStream        *st;
107
2.30k
    unsigned int    audio_subsegments;
108
2.30k
    int64_t ret64;
109
110
2.30k
    avio_skip(pb, 3);
111
2.30k
    mtv->file_size         = avio_rl32(pb);
112
2.30k
    mtv->segments          = avio_rl32(pb);
113
2.30k
    avio_skip(pb, 32);
114
2.30k
    mtv->audio_identifier  = avio_rl24(pb);
115
2.30k
    mtv->audio_br          = avio_rl16(pb);
116
2.30k
    mtv->img_colorfmt      = avio_rl24(pb);
117
2.30k
    mtv->img_bpp           = avio_r8(pb);
118
2.30k
    mtv->img_width         = avio_rl16(pb);
119
2.30k
    mtv->img_height        = avio_rl16(pb);
120
2.30k
    mtv->img_segment_size  = avio_rl16(pb);
121
122
    /* Assume 16bpp even if claimed otherwise.
123
     * We know its going to be RGBG565/555 anyway
124
     */
125
2.30k
    if (mtv->img_bpp != MTV_IMAGE_DEFAULT_BPP) {
126
2.27k
        av_log (s, AV_LOG_WARNING, "Header claims %dbpp (!= 16). Ignoring\n",
127
2.27k
                mtv->img_bpp);
128
2.27k
        mtv->img_bpp = MTV_IMAGE_DEFAULT_BPP;
129
2.27k
    }
130
131
    /* Calculate width and height if missing from header */
132
133
2.30k
    if (!mtv->img_width && mtv->img_height > 0 && mtv->img_bpp >= 8)
134
58
        mtv->img_width=mtv->img_segment_size / (mtv->img_bpp>>3)
135
58
                        / mtv->img_height;
136
137
2.30k
    if (!mtv->img_height && mtv->img_width > 0 && mtv->img_bpp >= 8)
138
56
        mtv->img_height=mtv->img_segment_size / (mtv->img_bpp>>3)
139
56
                        / mtv->img_width;
140
141
2.30k
    if(!mtv->img_height || !mtv->img_width || !mtv->img_segment_size){
142
240
        av_log(s, AV_LOG_ERROR, "width or height or segment_size is invalid and I cannot calculate them from other information\n");
143
240
        return AVERROR_INVALIDDATA;
144
240
    }
145
146
2.06k
    avio_skip(pb, 4);
147
2.06k
    audio_subsegments = avio_rl16(pb);
148
149
2.06k
    if (audio_subsegments == 0) {
150
26
        avpriv_request_sample(s, "MTV files without audio");
151
26
        return AVERROR_PATCHWELCOME;
152
26
    }
153
154
2.03k
    mtv->full_segment_size =
155
2.03k
        audio_subsegments * (MTV_AUDIO_PADDING_SIZE + MTV_ASUBCHUNK_DATA_SIZE) +
156
2.03k
        mtv->img_segment_size;
157
2.03k
    mtv->video_fps         = (mtv->audio_br / 4) / audio_subsegments;
158
159
    // FIXME Add sanity check here
160
161
    // all systems go! init decoders
162
163
    // video - raw rgb565
164
165
2.03k
    st = avformat_new_stream(s, NULL);
166
2.03k
    if(!st)
167
0
        return AVERROR(ENOMEM);
168
169
2.03k
    avpriv_set_pts_info(st, 64, 1, mtv->video_fps);
170
2.03k
    st->codecpar->codec_type      = AVMEDIA_TYPE_VIDEO;
171
2.03k
    st->codecpar->codec_id        = AV_CODEC_ID_RAWVIDEO;
172
2.03k
    st->codecpar->format          = AV_PIX_FMT_RGB565BE;
173
2.03k
    st->codecpar->width           = mtv->img_width;
174
2.03k
    st->codecpar->height          = mtv->img_height;
175
2.03k
    st->codecpar->extradata       = av_strdup("BottomUp");
176
2.03k
    if (!st->codecpar->extradata)
177
0
        return AVERROR(ENOMEM);
178
2.03k
    st->codecpar->extradata_size  = 9;
179
180
    // audio - mp3
181
182
2.03k
    st = avformat_new_stream(s, NULL);
183
2.03k
    if(!st)
184
0
        return AVERROR(ENOMEM);
185
186
2.03k
    avpriv_set_pts_info(st, 64, 1, MTV_AUDIO_SAMPLING_RATE);
187
2.03k
    st->codecpar->codec_type      = AVMEDIA_TYPE_AUDIO;
188
2.03k
    st->codecpar->codec_id        = AV_CODEC_ID_MP3;
189
2.03k
    st->codecpar->bit_rate        = mtv->audio_br;
190
2.03k
    ffstream(st)->need_parsing    = AVSTREAM_PARSE_FULL;
191
192
    // Jump over header
193
194
2.03k
    if ((ret64 = avio_seek(pb, MTV_HEADER_SIZE, SEEK_SET)) < 0)
195
64
        return (int)ret64;
196
197
1.97k
    return 0;
198
199
2.03k
}
200
201
static int mtv_read_packet(AVFormatContext *s, AVPacket *pkt)
202
282k
{
203
282k
    MTVDemuxContext *mtv = s->priv_data;
204
282k
    AVIOContext *pb = s->pb;
205
282k
    int ret;
206
207
282k
    if((avio_tell(pb) - ffformatcontext(s)->data_offset + mtv->img_segment_size) % mtv->full_segment_size)
208
192k
    {
209
192k
        avio_skip(pb, MTV_AUDIO_PADDING_SIZE);
210
211
192k
        ret = av_get_packet(pb, pkt, MTV_ASUBCHUNK_DATA_SIZE);
212
192k
        if(ret < 0)
213
5.00k
            return ret;
214
215
187k
        pkt->pos -= MTV_AUDIO_PADDING_SIZE;
216
187k
        pkt->stream_index = 1;
217
218
187k
    }else
219
89.8k
    {
220
89.8k
        ret = av_get_packet(pb, pkt, mtv->img_segment_size);
221
89.8k
        if(ret < 0)
222
81
            return ret;
223
224
89.7k
        pkt->stream_index = 0;
225
89.7k
    }
226
227
277k
    return ret;
228
282k
}
229
230
const FFInputFormat ff_mtv_demuxer = {
231
    .p.name         = "mtv",
232
    .p.long_name    = NULL_IF_CONFIG_SMALL("MTV"),
233
    .priv_data_size = sizeof(MTVDemuxContext),
234
    .read_probe     = mtv_probe,
235
    .read_header    = mtv_read_header,
236
    .read_packet    = mtv_read_packet,
237
};