Coverage Report

Created: 2026-04-01 07:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavformat/hnm.c
Line
Count
Source
1
/*
2
 * Cryo Interactive Entertainment HNM4 demuxer
3
 *
4
 * Copyright (c) 2012 David Kment
5
 *
6
 * This file is part of FFmpeg .
7
 *
8
 * FFmpeg  is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * FFmpeg  is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with FFmpeg ; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22
23
#include <inttypes.h>
24
25
#include "libavutil/intreadwrite.h"
26
#include "avformat.h"
27
#include "demux.h"
28
#include "internal.h"
29
30
919k
#define HNM4_TAG MKTAG('H', 'N', 'M', '4')
31
32
#define HNM4_SAMPLE_RATE 22050
33
1.39k
#define HNM4_FRAME_FPS 24
34
35
16.4k
#define HNM4_CHUNK_ID_PL 19536
36
280k
#define HNM4_CHUNK_ID_IZ 23113
37
117k
#define HNM4_CHUNK_ID_IU 21833
38
278
#define HNM4_CHUNK_ID_SD 17491
39
40
typedef struct Hnm4DemuxContext {
41
    uint32_t frames;
42
    uint32_t currentframe;
43
    uint32_t superchunk_remaining;
44
} Hnm4DemuxContext;
45
46
static int hnm_probe(const AVProbeData *p)
47
959k
{
48
959k
    if (p->buf_size < 4)
49
39.2k
        return 0;
50
51
    // check for HNM4 header.
52
    // currently only HNM v4/v4A is supported
53
919k
    if (AV_RL32(&p->buf[0]) == HNM4_TAG)
54
142
        return AVPROBE_SCORE_MAX;
55
56
919k
    return 0;
57
919k
}
58
59
static int hnm_read_header(AVFormatContext *s)
60
1.60k
{
61
1.60k
    Hnm4DemuxContext *hnm = s->priv_data;
62
1.60k
    AVIOContext *pb = s->pb;
63
1.60k
    unsigned width, height;
64
1.60k
    AVStream *vst;
65
1.60k
    int ret;
66
67
1.60k
    avio_skip(pb, 8);
68
1.60k
    width          = avio_rl16(pb);
69
1.60k
    height         = avio_rl16(pb);
70
1.60k
    avio_rl32(pb); // filesize
71
1.60k
    hnm->frames    = avio_rl32(pb);
72
1.60k
    avio_skip(pb, 44);
73
74
1.60k
    if (width  < 256 || width  > 640 ||
75
1.42k
        height < 150 || height > 480) {
76
205
        av_log(s, AV_LOG_ERROR,
77
205
               "invalid resolution: %ux%u\n", width, height);
78
205
        return AVERROR_INVALIDDATA;
79
205
    }
80
81
1.39k
    if (!(vst = avformat_new_stream(s, NULL)))
82
0
        return AVERROR(ENOMEM);
83
84
1.39k
    vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
85
1.39k
    vst->codecpar->codec_id   = AV_CODEC_ID_HNM4_VIDEO;
86
1.39k
    vst->codecpar->codec_tag  = 0;
87
1.39k
    vst->codecpar->width      = width;
88
1.39k
    vst->codecpar->height     = height;
89
1.39k
    if ((ret = ff_alloc_extradata(vst->codecpar, 1)) < 0)
90
0
        return ret;
91
92
    // TODO: find a better way to detect HNM4A
93
1.39k
    vst->codecpar->extradata[0] = width == 640 ? 0x4a : 0x40;
94
95
1.39k
    vst->start_time = 0;
96
97
1.39k
    avpriv_set_pts_info(vst, 33, 1, HNM4_FRAME_FPS);
98
99
1.39k
    return 0;
100
1.39k
}
101
102
static int hnm_read_packet(AVFormatContext *s, AVPacket *pkt)
103
149k
{
104
149k
    Hnm4DemuxContext *hnm = s->priv_data;
105
149k
    AVIOContext *pb = s->pb;
106
149k
    int ret = 0;
107
108
149k
    uint32_t superchunk_size, chunk_size;
109
149k
    uint16_t chunk_id;
110
111
149k
    if (hnm->currentframe == hnm->frames || pb->eof_reached)
112
2.28k
        return AVERROR_EOF;
113
114
147k
    if (hnm->superchunk_remaining == 0) {
115
        /* parse next superchunk */
116
7.76k
        superchunk_size = avio_rl24(pb);
117
7.76k
        if (superchunk_size < 4)
118
99
            return AVERROR_INVALIDDATA;
119
7.66k
        avio_skip(pb, 1);
120
121
7.66k
        hnm->superchunk_remaining = superchunk_size - 4;
122
7.66k
    }
123
124
147k
    chunk_size = avio_rl24(pb);
125
147k
    avio_skip(pb, 1);
126
147k
    chunk_id = avio_rl16(pb);
127
147k
    avio_skip(pb, 2);
128
129
147k
    if (chunk_size > hnm->superchunk_remaining || chunk_size < 8) {
130
111k
        av_log(s, AV_LOG_ERROR,
131
111k
               "invalid chunk size: %"PRIu32", offset: %"PRId64"\n",
132
111k
               chunk_size, avio_tell(pb));
133
111k
        avio_skip(pb, hnm->superchunk_remaining - 8);
134
111k
        hnm->superchunk_remaining = 0;
135
111k
    }
136
137
147k
    switch (chunk_id) {
138
16.4k
    case HNM4_CHUNK_ID_PL:
139
89.7k
    case HNM4_CHUNK_ID_IZ:
140
95.3k
    case HNM4_CHUNK_ID_IU:
141
95.3k
        avio_seek(pb, -8, SEEK_CUR);
142
95.3k
        ret += av_get_packet(pb, pkt, chunk_size);
143
95.3k
        hnm->superchunk_remaining -= chunk_size;
144
95.3k
        if (chunk_id == HNM4_CHUNK_ID_IZ || chunk_id == HNM4_CHUNK_ID_IU)
145
78.9k
            hnm->currentframe++;
146
95.3k
        break;
147
148
278
    case HNM4_CHUNK_ID_SD:
149
278
        avio_skip(pb, chunk_size - 8);
150
278
        hnm->superchunk_remaining -= chunk_size;
151
278
        break;
152
153
51.3k
    default:
154
51.3k
        av_log(s, AV_LOG_WARNING, "unknown chunk found: %"PRIu16", offset: %"PRId64"\n",
155
51.3k
               chunk_id, avio_tell(pb));
156
51.3k
        avio_skip(pb, chunk_size - 8);
157
51.3k
        hnm->superchunk_remaining -= chunk_size;
158
51.3k
        break;
159
147k
    }
160
161
147k
    return ret;
162
147k
}
163
164
const FFInputFormat ff_hnm_demuxer = {
165
    .p.name         = "hnm",
166
    .p.long_name    = NULL_IF_CONFIG_SMALL("Cryo HNM v4"),
167
    .p.flags        = AVFMT_NO_BYTE_SEEK | AVFMT_NOGENSEARCH | AVFMT_NOBINSEARCH,
168
    .priv_data_size = sizeof(Hnm4DemuxContext),
169
    .read_probe     = hnm_probe,
170
    .read_header    = hnm_read_header,
171
    .read_packet    = hnm_read_packet,
172
};