Coverage Report

Created: 2025-08-28 07:12

/src/ffmpeg/libavformat/mxg.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * MxPEG clip file demuxer
3
 * Copyright (c) 2010 Anatoly Nenashev
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/channel_layout.h"
23
#include "libavutil/internal.h"
24
#include "libavutil/intreadwrite.h"
25
#include "libavutil/mem.h"
26
#include "libavcodec/mjpeg.h"
27
#include "avformat.h"
28
#include "demux.h"
29
#include "internal.h"
30
#include "avio.h"
31
32
37.2k
#define DEFAULT_PACKET_SIZE 1024
33
8.83M
#define OVERREAD_SIZE 3
34
35
typedef struct MXGContext {
36
    uint8_t *buffer;
37
    uint8_t *buffer_ptr;
38
    uint8_t *soi_ptr;
39
    unsigned int buffer_size;
40
    int64_t dts;
41
    unsigned int cache_size;
42
} MXGContext;
43
44
static int mxg_read_header(AVFormatContext *s)
45
7.01k
{
46
7.01k
    AVStream *video_st, *audio_st;
47
7.01k
    MXGContext *mxg = s->priv_data;
48
49
    /* video parameters will be extracted from the compressed bitstream */
50
7.01k
    video_st = avformat_new_stream(s, NULL);
51
7.01k
    if (!video_st)
52
0
        return AVERROR(ENOMEM);
53
7.01k
    video_st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
54
7.01k
    video_st->codecpar->codec_id = AV_CODEC_ID_MXPEG;
55
7.01k
    avpriv_set_pts_info(video_st, 64, 1, 1000000);
56
57
7.01k
    audio_st = avformat_new_stream(s, NULL);
58
7.01k
    if (!audio_st)
59
0
        return AVERROR(ENOMEM);
60
7.01k
    audio_st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
61
7.01k
    audio_st->codecpar->codec_id = AV_CODEC_ID_PCM_ALAW;
62
7.01k
    audio_st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
63
7.01k
    audio_st->codecpar->sample_rate = 8000;
64
7.01k
    audio_st->codecpar->bits_per_coded_sample = 8;
65
7.01k
    audio_st->codecpar->block_align = 1;
66
7.01k
    avpriv_set_pts_info(audio_st, 64, 1, 1000000);
67
68
7.01k
    mxg->soi_ptr = mxg->buffer_ptr = mxg->buffer = 0;
69
7.01k
    mxg->buffer_size = 0;
70
7.01k
    mxg->dts = AV_NOPTS_VALUE;
71
7.01k
    mxg->cache_size = 0;
72
73
7.01k
    return 0;
74
7.01k
}
75
76
static uint8_t* mxg_find_startmarker(uint8_t *p, uint8_t *end)
77
2.92M
{
78
7.42M
    for (; p < end - 3; p += 4) {
79
7.40M
        uint32_t x = AV_RN32(p);
80
81
7.40M
        if (x & (~(x+0x01010101)) & 0x80808080) {
82
2.89M
            if (p[0] == 0xff) {
83
1.37M
                return p;
84
1.51M
            } else if (p[1] == 0xff) {
85
206k
                return p+1;
86
1.31M
            } else if (p[2] == 0xff) {
87
1.27M
                return p+2;
88
1.27M
            } else if (p[3] == 0xff) {
89
37.4k
                return p+3;
90
37.4k
            }
91
2.89M
        }
92
7.40M
    }
93
94
41.7k
    for (; p < end; ++p) {
95
21.3k
        if (*p == 0xff) return p;
96
21.3k
    }
97
98
20.3k
    return end;
99
26.2k
}
100
101
static int mxg_update_cache(AVFormatContext *s, unsigned int cache_size)
102
41.8k
{
103
41.8k
    MXGContext *mxg = s->priv_data;
104
41.8k
    unsigned int current_pos = mxg->buffer_ptr - mxg->buffer;
105
41.8k
    unsigned int soi_pos;
106
41.8k
    uint8_t *buffer;
107
41.8k
    int ret;
108
109
    /* reallocate internal buffer */
110
41.8k
    if (current_pos > current_pos + cache_size)
111
0
        return AVERROR(ENOMEM);
112
41.8k
    soi_pos = mxg->soi_ptr - mxg->buffer;
113
41.8k
    buffer = av_fast_realloc(mxg->buffer, &mxg->buffer_size,
114
41.8k
                             current_pos + cache_size +
115
41.8k
                             AV_INPUT_BUFFER_PADDING_SIZE);
116
41.8k
    if (!buffer)
117
0
        return AVERROR(ENOMEM);
118
41.8k
    mxg->buffer = buffer;
119
41.8k
    mxg->buffer_ptr = mxg->buffer + current_pos;
120
41.8k
    if (mxg->soi_ptr) mxg->soi_ptr = mxg->buffer + soi_pos;
121
122
    /* get data */
123
41.8k
    ret = avio_read(s->pb, mxg->buffer_ptr + mxg->cache_size,
124
41.8k
                     cache_size - mxg->cache_size);
125
41.8k
    if (ret < 0)
126
5.64k
        return ret;
127
128
36.1k
    mxg->cache_size += ret;
129
130
36.1k
    memset(mxg->buffer_ptr + mxg->cache_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
131
132
36.1k
    return ret;
133
41.8k
}
134
135
static int mxg_read_packet(AVFormatContext *s, AVPacket *pkt)
136
1.12M
{
137
1.12M
    int ret;
138
1.12M
    unsigned int size;
139
1.12M
    uint8_t *startmarker_ptr, *end, *search_end, marker;
140
1.12M
    MXGContext *mxg = s->priv_data;
141
142
2.93M
    while (!avio_feof(s->pb) && !s->pb->error){
143
2.92M
        if (mxg->cache_size <= OVERREAD_SIZE) {
144
            /* update internal buffer */
145
37.2k
            ret = mxg_update_cache(s, DEFAULT_PACKET_SIZE + OVERREAD_SIZE);
146
37.2k
            if (ret < 0)
147
5.23k
                return ret;
148
37.2k
        }
149
2.92M
        end = mxg->buffer_ptr + mxg->cache_size;
150
151
        /* find start marker - 0xff */
152
2.92M
        if (mxg->cache_size > OVERREAD_SIZE) {
153
2.92M
            search_end = end - OVERREAD_SIZE;
154
2.92M
            startmarker_ptr = mxg_find_startmarker(mxg->buffer_ptr, search_end);
155
2.92M
        } else {
156
145
            search_end = end;
157
145
            startmarker_ptr = mxg_find_startmarker(mxg->buffer_ptr, search_end);
158
145
            if (startmarker_ptr >= search_end - 1 ||
159
145
                *(startmarker_ptr + 1) != EOI) break;
160
145
        }
161
162
2.92M
        if (startmarker_ptr != search_end) { /* start marker found */
163
2.90M
            marker = *(startmarker_ptr + 1);
164
2.90M
            mxg->buffer_ptr = startmarker_ptr + 2;
165
2.90M
            mxg->cache_size = end - mxg->buffer_ptr;
166
167
2.90M
            if (marker == SOI) {
168
1.11M
                mxg->soi_ptr = startmarker_ptr;
169
1.78M
            } else if (marker == EOI) {
170
1.11M
                if (!mxg->soi_ptr) {
171
15.5k
                    av_log(s, AV_LOG_WARNING, "Found EOI before SOI, skipping\n");
172
15.5k
                    continue;
173
15.5k
                }
174
175
1.09M
                size = mxg->buffer_ptr - mxg->soi_ptr;
176
1.09M
                ret = av_new_packet(pkt, size);
177
1.09M
                if (ret < 0)
178
78
                    return ret;
179
1.09M
                memcpy(pkt->data, mxg->soi_ptr, size);
180
181
1.09M
                pkt->pts = pkt->dts = mxg->dts;
182
1.09M
                pkt->stream_index = 0;
183
184
1.09M
                if (mxg->soi_ptr - mxg->buffer > mxg->cache_size) {
185
47.7k
                    if (mxg->cache_size > 0) {
186
47.7k
                        memmove(mxg->buffer, mxg->buffer_ptr, mxg->cache_size);
187
47.7k
                    }
188
189
47.7k
                    mxg->buffer_ptr = mxg->buffer;
190
47.7k
                }
191
1.09M
                mxg->soi_ptr = 0;
192
193
1.09M
                return pkt->size;
194
1.09M
            } else if ( (SOF0 <= marker && marker <= SOF15) ||
195
669k
                        (SOS  <= marker && marker <= COM) ) {
196
                /* all other markers that start marker segment also contain
197
                   length value (see specification for JPEG Annex B.1) */
198
72.6k
                size = AV_RB16(mxg->buffer_ptr);
199
72.6k
                if (size < 2)
200
138
                    return AVERROR(EINVAL);
201
202
72.5k
                if (mxg->cache_size < size) {
203
4.57k
                    ret = mxg_update_cache(s, size);
204
4.57k
                    if (ret < 0)
205
406
                        return ret;
206
4.16k
                    startmarker_ptr = mxg->buffer_ptr - 2;
207
4.16k
                    mxg->cache_size = 0;
208
67.9k
                } else {
209
67.9k
                    mxg->cache_size -= size;
210
67.9k
                }
211
212
72.1k
                mxg->buffer_ptr += size;
213
214
72.1k
                if (marker == APP13 && size >= 16) { /* audio data */
215
13.9k
                    ret = av_new_packet(pkt, size - 14);
216
13.9k
                    if (ret < 0)
217
0
                        return ret;
218
13.9k
                    memcpy(pkt->data, startmarker_ptr + 16, size - 14);
219
220
                    /* time (GMT) of first sample in usec since 1970, little-endian */
221
13.9k
                    pkt->pts = pkt->dts = AV_RL64(startmarker_ptr + 8);
222
13.9k
                    pkt->stream_index = 1;
223
224
13.9k
                    if (startmarker_ptr - mxg->buffer > mxg->cache_size) {
225
3.07k
                        if (mxg->cache_size > 0) {
226
2.20k
                            memcpy(mxg->buffer, mxg->buffer_ptr, mxg->cache_size);
227
2.20k
                        }
228
3.07k
                        mxg->buffer_ptr = mxg->buffer;
229
3.07k
                    }
230
231
13.9k
                    return pkt->size;
232
58.1k
                } else if (marker == COM && size >= 18 &&
233
58.1k
                           !strncmp(startmarker_ptr + 4, "MXF", 3)) {
234
                    /* time (GMT) of video frame in usec since 1970, little-endian */
235
52.6k
                    mxg->dts = AV_RL64(startmarker_ptr + 12);
236
52.6k
                }
237
72.1k
            }
238
2.90M
        } else {
239
            /* start marker not found */
240
20.2k
            mxg->buffer_ptr = search_end;
241
20.2k
            mxg->cache_size = OVERREAD_SIZE;
242
20.2k
        }
243
2.92M
    }
244
245
7.77k
    return AVERROR_EOF;
246
1.12M
}
247
248
static int mxg_close(struct AVFormatContext *s)
249
7.01k
{
250
7.01k
    MXGContext *mxg = s->priv_data;
251
7.01k
    av_freep(&mxg->buffer);
252
7.01k
    return 0;
253
7.01k
}
254
255
const FFInputFormat ff_mxg_demuxer = {
256
    .p.name         = "mxg",
257
    .p.long_name    = NULL_IF_CONFIG_SMALL("MxPEG clip"),
258
    .p.extensions   = "mxg",
259
    .priv_data_size = sizeof(MXGContext),
260
    .read_header    = mxg_read_header,
261
    .read_packet    = mxg_read_packet,
262
    .read_close     = mxg_close,
263
};