avfilter/avfiltergraph: fix constant string comparision
[ffmpeg.git] / libavformat / hxvs.c
1 /*
2 * HXVS/HXVT IP camera format
3 *
4 * Copyright (c) 2025 Zhao Zhili <quinkblack@foxmail.com>
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 "libavutil/intreadwrite.h"
24
25 #include "avio_internal.h"
26 #include "demux.h"
27 #include "internal.h"
28
29 /*
30 * Ref
31 * https://code.videolan.org/videolan/vlc/-/blob/master/modules/demux/hx.c
32 * https://github.com/francescovannini/ipcam26Xconvert/tree/main
33 */
34
35 /* H.264
36 *
37 * uint32_t tag;
38 * uint32_t width;
39 * uint32_t height;
40 * uint8_t padding[4];
41 */
42 #define HXVS MKTAG('H', 'X', 'V', 'S')
43
44 /* H.265
45 *
46 * Same as HXVS.
47 */
48 #define HXVT MKTAG('H', 'X', 'V', 'T')
49
50 /* video frame
51 *
52 * uint32_t tag;
53 * uint32_t bytes
54 * uint32_t timestamp;
55 * uint32_t flags;
56 * ------------------
57 * uint8_t data[bytes]
58 *
59 * Note: each HXVF contains a single NALU or slice, not a frame.
60 */
61 #define HXVF MKTAG('H', 'X', 'V', 'F')
62
63 /* audio frame
64 *
65 * uint32_t tag;
66 * uint32_t bytes
67 * uint32_t timestamp;
68 * uint32_t flags;
69 * ------------------
70 * uint8_t data[bytes]
71 *
72 * Note: The first four bytes of data is fake start code and NALU type,
73 * which should be skipped.
74 */
75 #define HXAF MKTAG('H', 'X', 'A', 'F')
76
77 /* RAP frame index
78 *
79 * uint32_t tag;
80 * uint32_t bytes
81 * uint32_t duration;
82 * uint32_t flags;
83 */
84 #define HXFI MKTAG('H', 'X', 'F', 'I')
85
86 #define HXFI_TABLE_SIZE 200000
87 #define HXFI_TABLE_COUNT (200000 / 8)
88
89 typedef struct HxvsContext {
90 int video_index;
91 int audio_index;
92 } HxvsContext;
93
94 static int hxvs_probe(const AVProbeData *p)
95 {
96 uint32_t flag = 0;
97 uint32_t bytes;
98
99 for (size_t i = 0; i < p->buf_size; ) {
100 uint32_t tag = AV_RL32(&p->buf[i]);
101
102 // first four bytes must begin with HXVS/HXVT
103 if (i == 0) {
104 if (tag != HXVS && tag != HXVT)
105 return 0;
106 flag |= 1;
107 i += 16;
108 continue;
109 }
110
111 // Got RAP index at the end
112 if (tag == HXFI) {
113 if (flag == 7)
114 return AVPROBE_SCORE_MAX;
115 break;
116 }
117
118 i += 4;
119 if (tag == HXVF || tag == HXAF) {
120 bytes = AV_RL32(&p->buf[i]);
121 i += 12 + bytes;
122 flag |= (tag == HXVF) ? 2 : 4;
123 continue;
124 }
125
126 return 0;
127 }
128
129 // Get audio and video
130 if (flag == 7)
131 return AVPROBE_SCORE_EXTENSION + 10;
132 // Get video only
133 if (flag == 3)
134 return AVPROBE_SCORE_EXTENSION + 2;
135
136 return 0;
137 }
138
139 static int hxvs_create_video_stream(AVFormatContext *s, enum AVCodecID codec_id)
140 {
141 HxvsContext *ctx = s->priv_data;
142 AVIOContext *pb = s->pb;
143 AVStream *vt = avformat_new_stream(s, NULL);
144 if (!vt)
145 return AVERROR(ENOMEM);
146
147 vt->id = 0;
148 vt->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
149 vt->codecpar->codec_id = codec_id;
150 vt->codecpar->width = avio_rl32(pb);
151 vt->codecpar->height = avio_rl32(pb);
152 avpriv_set_pts_info(vt, 32, 1, 1000);
153 ffstream(vt)->need_parsing = AVSTREAM_PARSE_FULL;
154 ctx->video_index = vt->index;
155
156 // skip padding
157 avio_skip(pb, 4);
158
159 return 0;
160 }
161
162 static int hxvs_create_audio_stream(AVFormatContext *s)
163 {
164 HxvsContext *ctx = s->priv_data;
165 AVStream *at = avformat_new_stream(s, NULL);
166 if (!at)
167 return AVERROR(ENOMEM);
168
169 at->id = 1;
170 at->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
171 at->codecpar->codec_id = AV_CODEC_ID_PCM_ALAW;
172 at->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
173 at->codecpar->sample_rate = 8000;
174 avpriv_set_pts_info(at, 32, 1, 1000);
175 ctx->audio_index = at->index;
176
177 return 0;
178 }
179
180 static int hxvs_build_index(AVFormatContext *s)
181 {
182 HxvsContext *ctx = s->priv_data;
183 AVIOContext *pb = s->pb;
184
185 int64_t size = avio_size(pb);
186 if (size < 0)
187 return size;
188 // Don't return error when HXFI is missing
189 int64_t pos = avio_seek(pb, size -(HXFI_TABLE_SIZE + 16), SEEK_SET);
190 if (pos < 0)
191 return 0;
192
193 uint32_t tag = avio_rl32(pb);
194 if (tag != HXFI)
195 return 0;
196 avio_skip(pb, 4);
197 AVStream *st = s->streams[ctx->video_index];
198 st->duration = avio_rl32(pb);
199 avio_skip(pb, 4);
200
201 FFStream *const sti = ffstream(st);
202 uint32_t prev_time;
203 for (int i = 0; i < HXFI_TABLE_COUNT; i++) {
204 uint32_t offset = avio_rl32(pb);
205 // pts = first_frame_pts + time
206 uint32_t time = avio_rl32(pb);
207 av_log(s, AV_LOG_TRACE, "%s/%d: offset %u, time %u\n",
208 av_fourcc2str(HXAF), i, offset, time);
209 if (!offset)
210 break;
211
212 if (!i) {
213 // Get first frame timestamp
214 int64_t save_pos = avio_tell(pb);
215 pos = avio_seek(pb, offset, SEEK_SET);
216 if (pos < 0)
217 return pos;
218 tag = avio_rl32(pb);
219 if (tag != HXVF) {
220 av_log(s, AV_LOG_ERROR, "invalid tag %s at pos %u\n",
221 av_fourcc2str(tag), offset);
222 return AVERROR_INVALIDDATA;
223 }
224 avio_skip(pb, 4);
225 // save first frame timestamp to stream start_time
226 st->start_time = avio_rl32(pb);
227 pos = avio_seek(pb, save_pos, SEEK_SET);
228 if (pos < 0)
229 return pos;
230 } else if (time == prev_time) {
231 // hxvs put SPS, PPS and slice into separate entries with same timestamp.
232 // Only record the first entry.
233 continue;
234 }
235 prev_time = time;
236 int ret = ff_add_index_entry(&sti->index_entries,
237 &sti->nb_index_entries,
238 &sti->index_entries_allocated_size,
239 offset, st->start_time + time,
240 0, 0, AVINDEX_KEYFRAME);
241 if (ret < 0)
242 return ret;
243 }
244
245 return 0;
246 }
247
248 static int hxvs_read_header(AVFormatContext *s)
249 {
250 AVIOContext *pb = s->pb;
251 uint32_t tag = avio_rl32(pb);
252 enum AVCodecID codec_id;
253
254 if (tag == HXVS) {
255 codec_id = AV_CODEC_ID_H264;
256 } else if (tag == HXVT) {
257 codec_id = AV_CODEC_ID_HEVC;
258 } else {
259 av_log(s, AV_LOG_ERROR, "Unknown tag %s\n", av_fourcc2str(tag));
260 return AVERROR_INVALIDDATA;
261 }
262
263 int ret = hxvs_create_video_stream(s, codec_id);
264 if (ret < 0)
265 return ret;
266
267 ret = hxvs_create_audio_stream(s);
268 if (ret < 0)
269 return ret;
270
271 if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
272 int64_t pos = avio_tell(pb);
273 if (pos < 0)
274 return pos;
275
276 ret = hxvs_build_index(s);
277 if (ret < 0)
278 return ret;
279
280 pos = avio_seek(pb, pos, SEEK_SET);
281 if (pos < 0)
282 return ret;
283 }
284
285 return 0;
286 }
287
288 static int hxvs_read_packet(AVFormatContext *s, AVPacket *pkt)
289 {
290 HxvsContext *ctx = s->priv_data;
291 AVIOContext *pb = s->pb;
292 int64_t pos = avio_tell(pb);
293 uint32_t tag = avio_rl32(pb);
294 uint32_t bytes;
295 int ret;
296
297 if (avio_feof(pb) || (tag == HXFI))
298 return AVERROR_EOF;
299
300 if (tag != HXVF && tag != HXAF)
301 return AVERROR_INVALIDDATA;
302
303 bytes = avio_rl32(pb);
304 if (bytes < 4)
305 return AVERROR_INVALIDDATA;
306
307 uint32_t timestamp = avio_rl32(pb);
308 int key_flag = 0;
309 int index;
310 if (tag == HXVF) {
311 if (avio_rl32(pb) == 1)
312 key_flag = AV_PKT_FLAG_KEY;
313 index = ctx->video_index;
314 } else {
315 avio_skip(pb, 8);
316 index = ctx->audio_index;
317 bytes -= 4;
318 }
319
320 ret = av_get_packet(pb, pkt, bytes);
321 if (ret < 0)
322 return ret;
323 pkt->pts = timestamp;
324 pkt->pos = pos;
325 pkt->stream_index = index;
326 pkt->flags |= key_flag;
327
328 return 0;
329 }
330
331 const FFInputFormat ff_hxvs_demuxer = {
332 .p.name = "hxvs",
333 .p.long_name = NULL_IF_CONFIG_SMALL("HXVF/HXVS IP camera format"),
334 .p.extensions = "264,265",
335 .p.flags = AVFMT_GENERIC_INDEX,
336 .read_probe = hxvs_probe,
337 .read_header = hxvs_read_header,
338 .read_packet = hxvs_read_packet,
339 .priv_data_size = sizeof(HxvsContext),
340 };