FFmpeg
hxvs.c
Go to the documentation of this file.
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 {
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 
140 {
141  HxvsContext *ctx = s->priv_data;
142  AVIOContext *pb = s->pb;
144  if (!vt)
145  return AVERROR(ENOMEM);
146 
147  vt->id = 0;
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);
154  ctx->video_index = vt->index;
155 
156  // skip padding
157  avio_skip(pb, 4);
158 
159  return 0;
160 }
161 
163 {
164  HxvsContext *ctx = s->priv_data;
166  if (!at)
167  return AVERROR(ENOMEM);
168 
169  at->id = 1;
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 
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",
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;
237  &sti->nb_index_entries,
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 
249 {
250  AVIOContext *pb = s->pb;
251  uint32_t tag = avio_rl32(pb);
252  enum AVCodecID codec_id;
253 
254  if (tag == HXVS) {
256  } else if (tag == HXVT) {
258  } else {
259  av_log(s, AV_LOG_ERROR, "Unknown tag %s\n", av_fourcc2str(tag));
260  return AVERROR_INVALIDDATA;
261  }
262 
264  if (ret < 0)
265  return ret;
266 
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 
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 
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;
326  pkt->flags |= key_flag;
327 
328  return 0;
329 }
330 
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 };
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
AVCodecParameters::codec_type
enum AVMediaType codec_type
General type of the encoded data.
Definition: codec_par.h:51
avformat_new_stream
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:57
int64_t
long long int64_t
Definition: coverity.c:34
HxvsContext
Definition: hxvs.c:89
HxvsContext::audio_index
int audio_index
Definition: hxvs.c:91
HXAF
#define HXAF
Definition: hxvs.c:75
avio_size
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition: aviobuf.c:326
AV_PKT_FLAG_KEY
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition: packet.h:613
AVINDEX_KEYFRAME
#define AVINDEX_KEYFRAME
Definition: avformat.h:606
AVPROBE_SCORE_MAX
#define AVPROBE_SCORE_MAX
maximum score
Definition: avformat.h:463
avpriv_set_pts_info
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition: avformat.c:777
ffstream
static av_always_inline FFStream * ffstream(AVStream *st)
Definition: internal.h:347
HXFI
#define HXFI
Definition: hxvs.c:84
FFStream::index_entries_allocated_size
unsigned int index_entries_allocated_size
Definition: internal.h:187
avio_tell
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition: avio.h:494
AVFMT_GENERIC_INDEX
#define AVFMT_GENERIC_INDEX
Use generic index building code.
Definition: avformat.h:479
ff_hxvs_demuxer
const FFInputFormat ff_hxvs_demuxer
Definition: hxvs.c:331
AVStream::duration
int64_t duration
Decoding: duration of the stream, in stream time base.
Definition: avformat.h:803
AV_LOG_TRACE
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition: log.h:236
pkt
AVPacket * pkt
Definition: movenc.c:60
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
intreadwrite.h
s
#define s(width, name)
Definition: cbs_vp9.c:198
AVInputFormat::name
const char * name
A comma separated list of short names for the format.
Definition: avformat.h:549
AVMEDIA_TYPE_AUDIO
@ AVMEDIA_TYPE_AUDIO
Definition: avutil.h:201
AVCodecParameters::width
int width
Video only.
Definition: codec_par.h:134
ctx
AVFormatContext * ctx
Definition: movenc.c:49
codec_id
enum AVCodecID codec_id
Definition: vaapi_decode.c:410
AV_CODEC_ID_H264
@ AV_CODEC_ID_H264
Definition: codec_id.h:79
HxvsContext::video_index
int video_index
Definition: hxvs.c:90
FFStream::need_parsing
enum AVStreamParseType need_parsing
Definition: internal.h:314
AVFormatContext
Format I/O context.
Definition: avformat.h:1264
AV_CODEC_ID_PCM_ALAW
@ AV_CODEC_ID_PCM_ALAW
Definition: codec_id.h:344
internal.h
AVStream::codecpar
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:767
NULL
#define NULL
Definition: coverity.c:32
HXVS
#define HXVS
Definition: hxvs.c:42
hxvs_create_video_stream
static int hxvs_create_video_stream(AVFormatContext *s, enum AVCodecID codec_id)
Definition: hxvs.c:139
HXFI_TABLE_COUNT
#define HXFI_TABLE_COUNT
Definition: hxvs.c:87
FFStream::nb_index_entries
int nb_index_entries
Definition: internal.h:186
AVProbeData
This structure contains the data a format has to probe a file.
Definition: avformat.h:451
AVCodecParameters::ch_layout
AVChannelLayout ch_layout
Audio only.
Definition: codec_par.h:180
index
int index
Definition: gxfenc.c:90
AVPROBE_SCORE_EXTENSION
#define AVPROBE_SCORE_EXTENSION
score for file extension
Definition: avformat.h:461
AVCodecParameters::sample_rate
int sample_rate
Audio only.
Definition: codec_par.h:184
HXFI_TABLE_SIZE
#define HXFI_TABLE_SIZE
Definition: hxvs.c:86
AVCodecID
AVCodecID
Identify the syntax and semantics of the bitstream.
Definition: codec_id.h:49
avio_rl32
unsigned int avio_rl32(AVIOContext *s)
Definition: aviobuf.c:733
AVIOContext
Bytestream IO Context.
Definition: avio.h:160
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:94
AVIOContext::seekable
int seekable
A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
Definition: avio.h:261
AVChannelLayout
An AVChannelLayout holds information about the channel layout of audio data.
Definition: channel_layout.h:319
FFStream
Definition: internal.h:128
HXVT
#define HXVT
Definition: hxvs.c:48
size
int size
Definition: twinvq_data.h:10344
hxvs_read_packet
static int hxvs_read_packet(AVFormatContext *s, AVPacket *pkt)
Definition: hxvs.c:288
FFInputFormat::p
AVInputFormat p
The public AVInputFormat.
Definition: demux.h:51
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
AVPacket::flags
int flags
A combination of AV_PKT_FLAG values.
Definition: packet.h:564
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
AVPacket::pts
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: packet.h:551
avio_internal.h
HXVF
#define HXVF
Definition: hxvs.c:61
AVCodecParameters::height
int height
Definition: codec_par.h:135
AV_CODEC_ID_HEVC
@ AV_CODEC_ID_HEVC
Definition: codec_id.h:228
hxvs_create_audio_stream
static int hxvs_create_audio_stream(AVFormatContext *s)
Definition: hxvs.c:162
demux.h
hxvs_probe
static int hxvs_probe(const AVProbeData *p)
Definition: hxvs.c:94
hxvs_build_index
static int hxvs_build_index(AVFormatContext *s)
Definition: hxvs.c:180
ff_add_index_entry
int ff_add_index_entry(AVIndexEntry **index_entries, int *nb_index_entries, unsigned int *index_entries_allocated_size, int64_t pos, int64_t timestamp, int size, int distance, int flags)
Internal version of av_add_index_entry.
Definition: seek.c:64
av_get_packet
int av_get_packet(AVIOContext *s, AVPacket *pkt, int size)
Allocate and read the payload of a packet and initialize its fields with default values.
Definition: utils.c:94
tag
uint32_t tag
Definition: movenc.c:1957
AVStream::id
int id
Format-specific stream ID.
Definition: avformat.h:756
ret
ret
Definition: filter_design.txt:187
AVStream
Stream structure.
Definition: avformat.h:744
avio_seek
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:236
pos
unsigned int pos
Definition: spdifenc.c:414
flag
#define flag(name)
Definition: cbs_av1.c:496
AV_RL32
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_RL32
Definition: bytestream.h:92
AVStream::index
int index
stream index in AVFormatContext
Definition: avformat.h:750
AVIO_SEEKABLE_NORMAL
#define AVIO_SEEKABLE_NORMAL
Seeking works like for a local file.
Definition: avio.h:41
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
AVPacket::stream_index
int stream_index
Definition: packet.h:560
avio_skip
int64_t avio_skip(AVIOContext *s, int64_t offset)
Skip given number of bytes forward.
Definition: aviobuf.c:321
FFStream::index_entries
AVIndexEntry * index_entries
Only used if the format does not support seeking natively.
Definition: internal.h:184
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:200
AV_CHANNEL_LAYOUT_MONO
#define AV_CHANNEL_LAYOUT_MONO
Definition: channel_layout.h:394
AVCodecParameters::codec_id
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: codec_par.h:55
AVPacket
This structure stores compressed data.
Definition: packet.h:535
AVPacket::pos
int64_t pos
byte position in stream, -1 if unknown
Definition: packet.h:578
FFInputFormat
Definition: demux.h:47
AVSTREAM_PARSE_FULL
@ AVSTREAM_PARSE_FULL
full parsing and repack
Definition: avformat.h:589
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
AVStream::start_time
int64_t start_time
Decoding: pts of the first frame of the stream in presentation order, in stream time base.
Definition: avformat.h:793
hxvs_read_header
static int hxvs_read_header(AVFormatContext *s)
Definition: hxvs.c:248
av_fourcc2str
#define av_fourcc2str(fourcc)
Definition: avutil.h:347
avio_feof
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:349