| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Hash/MD5 encoder (for codec/format testing) | ||
| 3 | * Copyright (c) 2009 Reimar Döffinger, based on crcenc (c) 2002 Fabrice Bellard | ||
| 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 "config_components.h" | ||
| 23 | |||
| 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" | ||
| 29 | #include "avformat.h" | ||
| 30 | #include "internal.h" | ||
| 31 | #include "mux.h" | ||
| 32 | |||
| 33 | struct HashContext { | ||
| 34 | const AVClass *avclass; | ||
| 35 | struct AVHashContext **hashes; | ||
| 36 | char *hash_name; | ||
| 37 | int per_stream; | ||
| 38 | int format_version; | ||
| 39 | }; | ||
| 40 | |||
| 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 } | ||
| 47 | |||
| 48 | #if CONFIG_HASH_MUXER || CONFIG_STREAMHASH_MUXER | ||
| 49 | static const AVOption hash_streamhash_options[] = { | ||
| 50 | HASH_OPT("sha256"), | ||
| 51 | { NULL }, | ||
| 52 | }; | ||
| 53 | |||
| 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, | ||
| 59 | }; | ||
| 60 | #endif | ||
| 61 | |||
| 62 | #if CONFIG_FRAMEHASH_MUXER | ||
| 63 | static const AVOption framehash_options[] = { | ||
| 64 | HASH_OPT("sha256"), | ||
| 65 | FORMAT_VERSION_OPT, | ||
| 66 | { NULL }, | ||
| 67 | }; | ||
| 68 | #endif | ||
| 69 | |||
| 70 | #if CONFIG_MD5_MUXER | ||
| 71 | static const AVOption md5_options[] = { | ||
| 72 | HASH_OPT("md5"), | ||
| 73 | { NULL }, | ||
| 74 | }; | ||
| 75 | #endif | ||
| 76 | |||
| 77 | #if CONFIG_FRAMEMD5_MUXER | ||
| 78 | static const AVOption framemd5_options[] = { | ||
| 79 | HASH_OPT("md5"), | ||
| 80 | FORMAT_VERSION_OPT, | ||
| 81 | { NULL }, | ||
| 82 | }; | ||
| 83 | #endif | ||
| 84 | |||
| 85 | #if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER | ||
| 86 | ✗ | static int hash_init(struct AVFormatContext *s) | |
| 87 | { | ||
| 88 | int res; | ||
| 89 | ✗ | struct HashContext *c = s->priv_data; | |
| 90 | ✗ | c->per_stream = 0; | |
| 91 | ✗ | c->hashes = av_mallocz(sizeof(*c->hashes)); | |
| 92 | ✗ | if (!c->hashes) | |
| 93 | ✗ | return AVERROR(ENOMEM); | |
| 94 | ✗ | res = av_hash_alloc(&c->hashes[0], c->hash_name); | |
| 95 | ✗ | if (res < 0) | |
| 96 | ✗ | return res; | |
| 97 | ✗ | av_hash_init(c->hashes[0]); | |
| 98 | ✗ | return 0; | |
| 99 | } | ||
| 100 | #endif | ||
| 101 | |||
| 102 | #if CONFIG_STREAMHASH_MUXER | ||
| 103 | 1 | static int streamhash_init(struct AVFormatContext *s) | |
| 104 | { | ||
| 105 | int res, i; | ||
| 106 | 1 | struct HashContext *c = s->priv_data; | |
| 107 | 1 | c->per_stream = 1; | |
| 108 | 1 | c->hashes = av_calloc(s->nb_streams, sizeof(*c->hashes)); | |
| 109 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!c->hashes) |
| 110 | ✗ | return AVERROR(ENOMEM); | |
| 111 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | for (i = 0; i < s->nb_streams; i++) { |
| 112 | 2 | res = av_hash_alloc(&c->hashes[i], c->hash_name); | |
| 113 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (res < 0) { |
| 114 | ✗ | return res; | |
| 115 | } | ||
| 116 | 2 | av_hash_init(c->hashes[i]); | |
| 117 | } | ||
| 118 | 1 | return 0; | |
| 119 | } | ||
| 120 | #endif | ||
| 121 | |||
| 122 | #if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER || CONFIG_STREAMHASH_MUXER | ||
| 123 | 2 | static char get_media_type_char(enum AVMediaType type) | |
| 124 | { | ||
| 125 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
2 | switch (type) { |
| 126 | ✗ | case AVMEDIA_TYPE_VIDEO: return 'v'; | |
| 127 | 2 | 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'; | |
| 131 | ✗ | default: return '?'; | |
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | 518 | static int hash_write_packet(struct AVFormatContext *s, AVPacket *pkt) | |
| 136 | { | ||
| 137 | 518 | struct HashContext *c = s->priv_data; | |
| 138 |
1/2✓ Branch 0 taken 518 times.
✗ Branch 1 not taken.
|
518 | av_hash_update(c->hashes[c->per_stream ? pkt->stream_index : 0], pkt->data, pkt->size); |
| 139 | 518 | return 0; | |
| 140 | } | ||
| 141 | |||
| 142 | 1 | static int hash_write_trailer(struct AVFormatContext *s) | |
| 143 | { | ||
| 144 | 1 | struct HashContext *c = s->priv_data; | |
| 145 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | int num_hashes = c->per_stream ? s->nb_streams : 1; |
| 146 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | for (int i = 0; i < num_hashes; i++) { |
| 147 | char buf[AV_HASH_MAX_SIZE*2+128]; | ||
| 148 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (c->per_stream) { |
| 149 | 2 | AVStream *st = s->streams[i]; | |
| 150 | 2 | snprintf(buf, sizeof(buf) - 200, "%d,%c,%s=", i, get_media_type_char(st->codecpar->codec_type), | |
| 151 | 2 | av_hash_get_name(c->hashes[i])); | |
| 152 | } else { | ||
| 153 | ✗ | snprintf(buf, sizeof(buf) - 200, "%s=", av_hash_get_name(c->hashes[i])); | |
| 154 | } | ||
| 155 | 2 | av_hash_final_hex(c->hashes[i], buf + strlen(buf), sizeof(buf) - strlen(buf)); | |
| 156 | 2 | av_strlcatf(buf, sizeof(buf), "\n"); | |
| 157 | 2 | avio_write(s->pb, buf, strlen(buf)); | |
| 158 | } | ||
| 159 | |||
| 160 | 1 | return 0; | |
| 161 | } | ||
| 162 | #endif | ||
| 163 | |||
| 164 | 327 | static void hash_free(struct AVFormatContext *s) | |
| 165 | { | ||
| 166 | 327 | struct HashContext *c = s->priv_data; | |
| 167 |
1/2✓ Branch 0 taken 327 times.
✗ Branch 1 not taken.
|
327 | if (c->hashes) { |
| 168 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 326 times.
|
327 | int num_hashes = c->per_stream ? s->nb_streams : 1; |
| 169 |
2/2✓ Branch 0 taken 328 times.
✓ Branch 1 taken 327 times.
|
655 | for (int i = 0; i < num_hashes; i++) { |
| 170 | 328 | av_hash_freep(&c->hashes[i]); | |
| 171 | } | ||
| 172 | } | ||
| 173 | 327 | av_freep(&c->hashes); | |
| 174 | 327 | } | |
| 175 | |||
| 176 | #if CONFIG_HASH_MUXER | ||
| 177 | const FFOutputFormat ff_hash_muxer = { | ||
| 178 | .p.name = "hash", | ||
| 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, | ||
| 183 | .init = hash_init, | ||
| 184 | .write_packet = hash_write_packet, | ||
| 185 | .write_trailer = hash_write_trailer, | ||
| 186 | .deinit = hash_free, | ||
| 187 | .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | | ||
| 188 | AVFMT_TS_NEGATIVE, | ||
| 189 | .p.priv_class = &hash_streamhashenc_class, | ||
| 190 | }; | ||
| 191 | #endif | ||
| 192 | |||
| 193 | #if CONFIG_MD5_MUXER | ||
| 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, | ||
| 199 | }; | ||
| 200 | |||
| 201 | const FFOutputFormat ff_md5_muxer = { | ||
| 202 | .p.name = "md5", | ||
| 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, | ||
| 207 | .init = hash_init, | ||
| 208 | .write_packet = hash_write_packet, | ||
| 209 | .write_trailer = hash_write_trailer, | ||
| 210 | .deinit = hash_free, | ||
| 211 | .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | | ||
| 212 | AVFMT_TS_NEGATIVE, | ||
| 213 | .p.priv_class = &md5enc_class, | ||
| 214 | }; | ||
| 215 | #endif | ||
| 216 | |||
| 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, | ||
| 227 | .deinit = hash_free, | ||
| 228 | .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | | ||
| 229 | AVFMT_TS_NEGATIVE, | ||
| 230 | .p.priv_class = &hash_streamhashenc_class, | ||
| 231 | }; | ||
| 232 | #endif | ||
| 233 | |||
| 234 | #if CONFIG_FRAMEHASH_MUXER || CONFIG_FRAMEMD5_MUXER | ||
| 235 | 326 | static void framehash_print_extradata(struct AVFormatContext *s) | |
| 236 | { | ||
| 237 | int i; | ||
| 238 | |||
| 239 |
2/2✓ Branch 0 taken 329 times.
✓ Branch 1 taken 326 times.
|
655 | for (i = 0; i < s->nb_streams; i++) { |
| 240 | 329 | AVStream *st = s->streams[i]; | |
| 241 | 329 | AVCodecParameters *par = st->codecpar; | |
| 242 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 311 times.
|
329 | if (par->extradata) { |
| 243 | 18 | struct HashContext *c = s->priv_data; | |
| 244 | char buf[AV_HASH_MAX_SIZE*2+1]; | ||
| 245 | |||
| 246 | 18 | avio_printf(s->pb, "#extradata %d, %31d, ", i, par->extradata_size); | |
| 247 | 18 | av_hash_init(c->hashes[0]); | |
| 248 | 18 | av_hash_update(c->hashes[0], par->extradata, par->extradata_size); | |
| 249 | 18 | av_hash_final_hex(c->hashes[0], buf, sizeof(buf)); | |
| 250 | 18 | avio_write(s->pb, buf, strlen(buf)); | |
| 251 | 18 | avio_printf(s->pb, "\n"); | |
| 252 | } | ||
| 253 | } | ||
| 254 | 326 | } | |
| 255 | |||
| 256 | 326 | static int framehash_init(struct AVFormatContext *s) | |
| 257 | { | ||
| 258 | int res; | ||
| 259 | 326 | struct HashContext *c = s->priv_data; | |
| 260 | 326 | c->per_stream = 0; | |
| 261 | 326 | c->hashes = av_mallocz(sizeof(*c->hashes)); | |
| 262 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 326 times.
|
326 | if (!c->hashes) |
| 263 | ✗ | return AVERROR(ENOMEM); | |
| 264 | 326 | res = av_hash_alloc(&c->hashes[0], c->hash_name); | |
| 265 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 326 times.
|
326 | if (res < 0) |
| 266 | ✗ | return res; | |
| 267 | 326 | return 0; | |
| 268 | } | ||
| 269 | |||
| 270 | 326 | static int framehash_write_header(struct AVFormatContext *s) | |
| 271 | { | ||
| 272 | 326 | struct HashContext *c = s->priv_data; | |
| 273 | 326 | avio_printf(s->pb, "#format: frame checksums\n"); | |
| 274 | 326 | avio_printf(s->pb, "#version: %d\n", c->format_version); | |
| 275 | 326 | avio_printf(s->pb, "#hash: %s\n", av_hash_get_name(c->hashes[0])); | |
| 276 | 326 | framehash_print_extradata(s); | |
| 277 | 326 | ff_framehash_write_header(s); | |
| 278 | 326 | avio_printf(s->pb, "#stream#, dts, pts, duration, size, hash\n"); | |
| 279 | 326 | return 0; | |
| 280 | } | ||
| 281 | |||
| 282 | 6484 | static int framehash_write_packet(struct AVFormatContext *s, AVPacket *pkt) | |
| 283 | { | ||
| 284 | 6484 | struct HashContext *c = s->priv_data; | |
| 285 | char buf[AV_HASH_MAX_SIZE*2+128]; | ||
| 286 | int len; | ||
| 287 | 6484 | av_hash_init(c->hashes[0]); | |
| 288 | 6484 | av_hash_update(c->hashes[0], pkt->data, pkt->size); | |
| 289 | |||
| 290 | 6484 | 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); | ||
| 292 | 6484 | len = strlen(buf); | |
| 293 | 6484 | av_hash_final_hex(c->hashes[0], buf + len, sizeof(buf) - len); | |
| 294 | 6484 | avio_write(s->pb, buf, strlen(buf)); | |
| 295 | |||
| 296 |
2/4✓ Branch 0 taken 6484 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6484 times.
|
6484 | if (c->format_version > 1 && pkt->side_data_elems) { |
| 297 | int i; | ||
| 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)); | ||
| 305 | } | ||
| 306 | } else | ||
| 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); | |
| 310 | ✗ | len = strlen(buf); | |
| 311 | ✗ | av_hash_final_hex(c->hashes[0], buf + len, sizeof(buf) - len); | |
| 312 | ✗ | avio_write(s->pb, buf, strlen(buf)); | |
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | 6484 | avio_printf(s->pb, "\n"); | |
| 317 | 6484 | return 0; | |
| 318 | } | ||
| 319 | #endif | ||
| 320 | |||
| 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, | ||
| 327 | }; | ||
| 328 | |||
| 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, | ||
| 338 | .deinit = hash_free, | ||
| 339 | .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | | ||
| 340 | AVFMT_TS_NEGATIVE, | ||
| 341 | .p.priv_class = &framehash_class, | ||
| 342 | }; | ||
| 343 | #endif | ||
| 344 | |||
| 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, | ||
| 351 | }; | ||
| 352 | |||
| 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, | ||
| 362 | .deinit = hash_free, | ||
| 363 | .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | | ||
| 364 | AVFMT_TS_NEGATIVE, | ||
| 365 | .p.priv_class = &framemd5_class, | ||
| 366 | }; | ||
| 367 | #endif | ||
| 368 |