2 * Hash/MD5 encoder (for codec/format testing)
3 * Copyright (c) 2009 Reimar Döffinger, based on crcenc (c) 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
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.
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.
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
22 #include "config_components.h"
24 #include "libavutil/avstring.h"
25 #include "libavutil/hash.h"
26 #include "libavutil/intreadwrite.h"
27 #include "libavutil/mem.h"
28 #include "libavutil/opt.h"
34 const AVClass
*avclass
;
35 struct AVHashContext
**hashes
;
41 #define OFFSET(x) offsetof(struct HashContext, x)
42 #define ENC AV_OPT_FLAG_ENCODING_PARAM
43 #define HASH_OPT(defaulttype) \
44 { "hash", "set hash to use", OFFSET(hash_name), AV_OPT_TYPE_STRING, {.str = defaulttype}, 0, 0, ENC }
45 #define FORMAT_VERSION_OPT \
46 { "format_version", "file format version", OFFSET(format_version), AV_OPT_TYPE_INT, {.i64 = 2}, 1, 2, ENC }
48 #if CONFIG_HASH_MUXER || CONFIG_STREAMHASH_MUXER
49 static const AVOption hash_streamhash_options
[] = {
54 static const AVClass hash_streamhashenc_class
= {
55 .class_name
= "(stream) hash muxer",
56 .item_name
= av_default_item_name
,
57 .option
= hash_streamhash_options
,
58 .version
= LIBAVUTIL_VERSION_INT
,
62 #if CONFIG_FRAMEHASH_MUXER
63 static const AVOption framehash_options
[] = {
71 static const AVOption md5_options
[] = {
77 #if CONFIG_FRAMEMD5_MUXER
78 static const AVOption framemd5_options
[] = {
85 #if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER
86 static int hash_init(struct AVFormatContext
*s
)
89 struct HashContext
*c
= s
->priv_data
;
91 c
->hashes
= av_mallocz(sizeof(*c
->hashes
));
93 return AVERROR(ENOMEM
);
94 res
= av_hash_alloc(&c
->hashes
[0], c
->hash_name
);
97 av_hash_init(c
->hashes
[0]);
102 #if CONFIG_STREAMHASH_MUXER
103 static int streamhash_init(struct AVFormatContext
*s
)
106 struct HashContext
*c
= s
->priv_data
;
108 c
->hashes
= av_calloc(s
->nb_streams
, sizeof(*c
->hashes
));
110 return AVERROR(ENOMEM
);
111 for (i
= 0; i
< s
->nb_streams
; i
++) {
112 res
= av_hash_alloc(&c
->hashes
[i
], c
->hash_name
);
116 av_hash_init(c
->hashes
[i
]);
122 #if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER || CONFIG_STREAMHASH_MUXER
123 static char get_media_type_char(enum AVMediaType type
)
126 case AVMEDIA_TYPE_VIDEO
: return 'v';
127 case AVMEDIA_TYPE_AUDIO
: return 'a';
128 case AVMEDIA_TYPE_DATA
: return 'd';
129 case AVMEDIA_TYPE_SUBTITLE
: return 's';
130 case AVMEDIA_TYPE_ATTACHMENT
: return 't';
135 static int hash_write_packet(struct AVFormatContext
*s
, AVPacket
*pkt
)
137 struct HashContext
*c
= s
->priv_data
;
138 av_hash_update(c
->hashes
[c
->per_stream
? pkt
->stream_index
: 0], pkt
->data
, pkt
->size
);
142 static int hash_write_trailer(struct AVFormatContext
*s
)
144 struct HashContext
*c
= s
->priv_data
;
145 int num_hashes
= c
->per_stream
? s
->nb_streams
: 1;
146 for (int i
= 0; i
< num_hashes
; i
++) {
147 char buf
[AV_HASH_MAX_SIZE
*2+128];
149 AVStream
*st
= s
->streams
[i
];
150 snprintf(buf
, sizeof(buf
) - 200, "%d,%c,%s=", i
, get_media_type_char(st
->codecpar
->codec_type
),
151 av_hash_get_name(c
->hashes
[i
]));
153 snprintf(buf
, sizeof(buf
) - 200, "%s=", av_hash_get_name(c
->hashes
[i
]));
155 av_hash_final_hex(c
->hashes
[i
], buf
+ strlen(buf
), sizeof(buf
) - strlen(buf
));
156 av_strlcatf(buf
, sizeof(buf
), "\n");
157 avio_write(s
->pb
, buf
, strlen(buf
));
164 static void hash_free(struct AVFormatContext
*s
)
166 struct HashContext
*c
= s
->priv_data
;
168 int num_hashes
= c
->per_stream
? s
->nb_streams
: 1;
169 for (int i
= 0; i
< num_hashes
; i
++) {
170 av_hash_freep(&c
->hashes
[i
]);
173 av_freep(&c
->hashes
);
176 #if CONFIG_HASH_MUXER
177 const FFOutputFormat ff_hash_muxer
= {
179 .p
.long_name
= NULL_IF_CONFIG_SMALL("Hash testing"),
180 .priv_data_size
= sizeof(struct HashContext
),
181 .p
.audio_codec
= AV_CODEC_ID_PCM_S16LE
,
182 .p
.video_codec
= AV_CODEC_ID_RAWVIDEO
,
184 .write_packet
= hash_write_packet
,
185 .write_trailer
= hash_write_trailer
,
187 .p
.flags
= AVFMT_VARIABLE_FPS
| AVFMT_TS_NONSTRICT
|
189 .p
.priv_class
= &hash_streamhashenc_class
,
194 static const AVClass md5enc_class
= {
195 .class_name
= "MD5 muxer",
196 .item_name
= av_default_item_name
,
197 .option
= md5_options
,
198 .version
= LIBAVUTIL_VERSION_INT
,
201 const FFOutputFormat ff_md5_muxer
= {
203 .p
.long_name
= NULL_IF_CONFIG_SMALL("MD5 testing"),
204 .priv_data_size
= sizeof(struct HashContext
),
205 .p
.audio_codec
= AV_CODEC_ID_PCM_S16LE
,
206 .p
.video_codec
= AV_CODEC_ID_RAWVIDEO
,
208 .write_packet
= hash_write_packet
,
209 .write_trailer
= hash_write_trailer
,
211 .p
.flags
= AVFMT_VARIABLE_FPS
| AVFMT_TS_NONSTRICT
|
213 .p
.priv_class
= &md5enc_class
,
217 #if CONFIG_STREAMHASH_MUXER
218 const FFOutputFormat ff_streamhash_muxer
= {
219 .p
.name
= "streamhash",
220 .p
.long_name
= NULL_IF_CONFIG_SMALL("Per-stream hash testing"),
221 .priv_data_size
= sizeof(struct HashContext
),
222 .p
.audio_codec
= AV_CODEC_ID_PCM_S16LE
,
223 .p
.video_codec
= AV_CODEC_ID_RAWVIDEO
,
224 .init
= streamhash_init
,
225 .write_packet
= hash_write_packet
,
226 .write_trailer
= hash_write_trailer
,
228 .p
.flags
= AVFMT_VARIABLE_FPS
| AVFMT_TS_NONSTRICT
|
230 .p
.priv_class
= &hash_streamhashenc_class
,
234 #if CONFIG_FRAMEHASH_MUXER || CONFIG_FRAMEMD5_MUXER
235 static void framehash_print_extradata(struct AVFormatContext
*s
)
239 for (i
= 0; i
< s
->nb_streams
; i
++) {
240 AVStream
*st
= s
->streams
[i
];
241 AVCodecParameters
*par
= st
->codecpar
;
242 if (par
->extradata
) {
243 struct HashContext
*c
= s
->priv_data
;
244 char buf
[AV_HASH_MAX_SIZE
*2+1];
246 avio_printf(s
->pb
, "#extradata %d, %31d, ", i
, par
->extradata_size
);
247 av_hash_init(c
->hashes
[0]);
248 av_hash_update(c
->hashes
[0], par
->extradata
, par
->extradata_size
);
249 av_hash_final_hex(c
->hashes
[0], buf
, sizeof(buf
));
250 avio_write(s
->pb
, buf
, strlen(buf
));
251 avio_printf(s
->pb
, "\n");
256 static int framehash_init(struct AVFormatContext
*s
)
259 struct HashContext
*c
= s
->priv_data
;
261 c
->hashes
= av_mallocz(sizeof(*c
->hashes
));
263 return AVERROR(ENOMEM
);
264 res
= av_hash_alloc(&c
->hashes
[0], c
->hash_name
);
270 static int framehash_write_header(struct AVFormatContext
*s
)
272 struct HashContext
*c
= s
->priv_data
;
273 avio_printf(s
->pb
, "#format: frame checksums\n");
274 avio_printf(s
->pb
, "#version: %d\n", c
->format_version
);
275 avio_printf(s
->pb
, "#hash: %s\n", av_hash_get_name(c
->hashes
[0]));
276 framehash_print_extradata(s
);
277 ff_framehash_write_header(s
);
278 avio_printf(s
->pb
, "#stream#, dts, pts, duration, size, hash\n");
282 static int framehash_write_packet(struct AVFormatContext
*s
, AVPacket
*pkt
)
284 struct HashContext
*c
= s
->priv_data
;
285 char buf
[AV_HASH_MAX_SIZE
*2+128];
287 av_hash_init(c
->hashes
[0]);
288 av_hash_update(c
->hashes
[0], pkt
->data
, pkt
->size
);
290 snprintf(buf
, sizeof(buf
) - (AV_HASH_MAX_SIZE
* 2 + 1), "%d, %10"PRId64
", %10"PRId64
", %8"PRId64
", %8d, ",
291 pkt
->stream_index
, pkt
->dts
, pkt
->pts
, pkt
->duration
, pkt
->size
);
293 av_hash_final_hex(c
->hashes
[0], buf
+ len
, sizeof(buf
) - len
);
294 avio_write(s
->pb
, buf
, strlen(buf
));
296 if (c
->format_version
> 1 && pkt
->side_data_elems
) {
298 avio_printf(s
->pb
, ", S=%d", pkt
->side_data_elems
);
299 for (i
= 0; i
< pkt
->side_data_elems
; i
++) {
300 av_hash_init(c
->hashes
[0]);
301 if (HAVE_BIGENDIAN
&& pkt
->side_data
[i
].type
== AV_PKT_DATA_PALETTE
) {
302 for (size_t j
= 0; j
< pkt
->side_data
[i
].size
; j
+= sizeof(uint32_t)) {
303 uint32_t data
= AV_RL32(pkt
->side_data
[i
].data
+ j
);
304 av_hash_update(c
->hashes
[0], (uint8_t *)&data
, sizeof(uint32_t));
307 av_hash_update(c
->hashes
[0], pkt
->side_data
[i
].data
, pkt
->side_data
[i
].size
);
308 snprintf(buf
, sizeof(buf
) - (AV_HASH_MAX_SIZE
* 2 + 1),
309 ", %8"SIZE_SPECIFIER
", ", pkt
->side_data
[i
].size
);
311 av_hash_final_hex(c
->hashes
[0], buf
+ len
, sizeof(buf
) - len
);
312 avio_write(s
->pb
, buf
, strlen(buf
));
316 avio_printf(s
->pb
, "\n");
321 #if CONFIG_FRAMEHASH_MUXER
322 static const AVClass framehash_class
= {
323 .class_name
= "frame hash muxer",
324 .item_name
= av_default_item_name
,
325 .option
= framehash_options
,
326 .version
= LIBAVUTIL_VERSION_INT
,
329 const FFOutputFormat ff_framehash_muxer
= {
330 .p
.name
= "framehash",
331 .p
.long_name
= NULL_IF_CONFIG_SMALL("Per-frame hash testing"),
332 .priv_data_size
= sizeof(struct HashContext
),
333 .p
.audio_codec
= AV_CODEC_ID_PCM_S16LE
,
334 .p
.video_codec
= AV_CODEC_ID_RAWVIDEO
,
335 .init
= framehash_init
,
336 .write_header
= framehash_write_header
,
337 .write_packet
= framehash_write_packet
,
339 .p
.flags
= AVFMT_VARIABLE_FPS
| AVFMT_TS_NONSTRICT
|
341 .p
.priv_class
= &framehash_class
,
345 #if CONFIG_FRAMEMD5_MUXER
346 static const AVClass framemd5_class
= {
347 .class_name
= "frame MD5 muxer",
348 .item_name
= av_default_item_name
,
349 .option
= framemd5_options
,
350 .version
= LIBAVUTIL_VERSION_INT
,
353 const FFOutputFormat ff_framemd5_muxer
= {
354 .p
.name
= "framemd5",
355 .p
.long_name
= NULL_IF_CONFIG_SMALL("Per-frame MD5 testing"),
356 .priv_data_size
= sizeof(struct HashContext
),
357 .p
.audio_codec
= AV_CODEC_ID_PCM_S16LE
,
358 .p
.video_codec
= AV_CODEC_ID_RAWVIDEO
,
359 .init
= framehash_init
,
360 .write_header
= framehash_write_header
,
361 .write_packet
= framehash_write_packet
,
363 .p
.flags
= AVFMT_VARIABLE_FPS
| AVFMT_TS_NONSTRICT
|
365 .p
.priv_class
= &framemd5_class
,