| 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 { | ||
| 36 | struct AVHashContext *hash; | ||
| 37 | int64_t stream_size; | ||
| 38 | } *hashes; | ||
| 39 | char *hash_name; | ||
| 40 | int per_stream; | ||
| 41 | int format_version; | ||
| 42 | }; | ||
| 43 | |||
| 44 | #define OFFSET(x) offsetof(struct HashContext, x) | ||
| 45 | #define ENC AV_OPT_FLAG_ENCODING_PARAM | ||
| 46 | #define HASH_OPT(defaulttype) \ | ||
| 47 | { "hash", "set hash to use", OFFSET(hash_name), AV_OPT_TYPE_STRING, {.str = defaulttype}, 0, 0, ENC } | ||
| 48 | #define FORMAT_VERSION_OPT \ | ||
| 49 | { "format_version", "file format version", OFFSET(format_version), AV_OPT_TYPE_INT, {.i64 = 2}, 1, 2, ENC } | ||
| 50 | |||
| 51 | #if CONFIG_FRAMEHASH_MUXER | CONFIG_STREAMHASH_MUXER | CONFIG_HASH_MUXER | ||
| 52 | static const AVOption hash_options[] = { | ||
| 53 | FORMAT_VERSION_OPT, | ||
| 54 | HASH_OPT("sha256"), | ||
| 55 | { NULL }, | ||
| 56 | }; | ||
| 57 | #endif | ||
| 58 | |||
| 59 | #if CONFIG_MD5_MUXER | ||
| 60 | static const AVOption md5_options[] = { | ||
| 61 | HASH_OPT("md5"), | ||
| 62 | { NULL }, | ||
| 63 | }; | ||
| 64 | #endif | ||
| 65 | |||
| 66 | #if CONFIG_FRAMEMD5_MUXER | ||
| 67 | static const AVOption framemd5_options[] = { | ||
| 68 | HASH_OPT("md5"), | ||
| 69 | FORMAT_VERSION_OPT, | ||
| 70 | { NULL }, | ||
| 71 | }; | ||
| 72 | #endif | ||
| 73 | |||
| 74 | #if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER | ||
| 75 | ✗ | static int hash_init(struct AVFormatContext *s) | |
| 76 | { | ||
| 77 | int res; | ||
| 78 | ✗ | struct HashContext *c = s->priv_data; | |
| 79 | ✗ | c->per_stream = 0; | |
| 80 | ✗ | c->hashes = av_mallocz(sizeof(*c->hashes)); | |
| 81 | ✗ | if (!c->hashes) | |
| 82 | ✗ | return AVERROR(ENOMEM); | |
| 83 | ✗ | res = av_hash_alloc(&c->hashes->hash, c->hash_name); | |
| 84 | ✗ | if (res < 0) | |
| 85 | ✗ | return res; | |
| 86 | ✗ | av_hash_init(c->hashes->hash); | |
| 87 | ✗ | return 0; | |
| 88 | } | ||
| 89 | |||
| 90 | ✗ | static int hash_write_packet(struct AVFormatContext *s, AVPacket *pkt) | |
| 91 | { | ||
| 92 | ✗ | struct HashContext *c = s->priv_data; | |
| 93 | ✗ | av_hash_update(c->hashes->hash, pkt->data, pkt->size); | |
| 94 | ✗ | return 0; | |
| 95 | } | ||
| 96 | #endif | ||
| 97 | |||
| 98 | #if CONFIG_STREAMHASH_MUXER || CONFIG_FRAMEHASH_MUXER || CONFIG_FRAMEMD5_MUXER | ||
| 99 | 312 | static void hash_print_extradata(struct AVFormatContext *s) | |
| 100 | { | ||
| 101 | int i; | ||
| 102 | |||
| 103 |
2/2✓ Branch 0 taken 316 times.
✓ Branch 1 taken 312 times.
|
628 | for (i = 0; i < s->nb_streams; i++) { |
| 104 | 316 | const AVStream *st = s->streams[i]; | |
| 105 | 316 | const AVCodecParameters *par = st->codecpar; | |
| 106 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 298 times.
|
316 | if (par->extradata) { |
| 107 | 18 | struct HashContext *c = s->priv_data; | |
| 108 | char buf[AV_HASH_MAX_SIZE*2+1]; | ||
| 109 | |||
| 110 | 18 | av_hash_init(c->hashes->hash); | |
| 111 | 18 | av_hash_update(c->hashes->hash, par->extradata, par->extradata_size); | |
| 112 | 18 | av_hash_final_hex(c->hashes->hash, buf, sizeof(buf)); | |
| 113 | 18 | avio_printf(s->pb, "#extradata %d, %31d, %s\n", i, par->extradata_size, buf); | |
| 114 | } | ||
| 115 | } | ||
| 116 | 312 | } | |
| 117 | #endif | ||
| 118 | |||
| 119 | 2 | static void hash_print_sidedata(struct AVFormatContext *s, | |
| 120 | const AVPacketSideData *sd, int side_data_elems) | ||
| 121 | { | ||
| 122 | 2 | struct HashContext *c = s->priv_data; | |
| 123 | char buf[AV_HASH_MAX_SIZE*2+128]; | ||
| 124 | |||
| 125 | 2 | avio_printf(s->pb, ", S=%d", side_data_elems); | |
| 126 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | for (int i = 0; i < side_data_elems; i++) { |
| 127 | 2 | av_hash_init(c->hashes->hash); | |
| 128 | if (HAVE_BIGENDIAN && sd[i].type == AV_PKT_DATA_PALETTE) { | ||
| 129 | for (size_t j = 0; j < sd[i].size; j += sizeof(uint32_t)) { | ||
| 130 | uint32_t data = AV_RL32(sd[i].data + j); | ||
| 131 | av_hash_update(c->hashes->hash, (uint8_t *)&data, sizeof(uint32_t)); | ||
| 132 | } | ||
| 133 | } else | ||
| 134 | 2 | av_hash_update(c->hashes->hash, sd[i].data, sd[i].size); | |
| 135 | 2 | snprintf(buf, sizeof(buf) - (AV_HASH_MAX_SIZE * 2 + 1), | |
| 136 | 2 | ", %8zu, ", sd[i].size); | |
| 137 | 2 | size_t len = strlen(buf); | |
| 138 | 2 | av_hash_final_hex(c->hashes->hash, buf + len, sizeof(buf) - len); | |
| 139 | 2 | avio_write(s->pb, buf, strlen(buf)); | |
| 140 | } | ||
| 141 | 2 | } | |
| 142 | |||
| 143 | #if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER || CONFIG_STREAMHASH_MUXER | ||
| 144 | ✗ | static char get_media_type_char(enum AVMediaType type) | |
| 145 | { | ||
| 146 | ✗ | switch (type) { | |
| 147 | ✗ | case AVMEDIA_TYPE_VIDEO: return 'v'; | |
| 148 | ✗ | case AVMEDIA_TYPE_AUDIO: return 'a'; | |
| 149 | ✗ | case AVMEDIA_TYPE_DATA: return 'd'; | |
| 150 | ✗ | case AVMEDIA_TYPE_SUBTITLE: return 's'; | |
| 151 | ✗ | case AVMEDIA_TYPE_ATTACHMENT: return 't'; | |
| 152 | ✗ | default: return '?'; | |
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | 4 | static int hash_write_trailer(struct AVFormatContext *s) | |
| 157 | { | ||
| 158 | 4 | struct HashContext *c = s->priv_data; | |
| 159 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | int num_hashes = c->per_stream ? s->nb_streams : 1; |
| 160 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
|
9 | for (int i = 0; i < num_hashes; i++) { |
| 161 | char buf[AV_HASH_MAX_SIZE*2+128]; | ||
| 162 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (c->per_stream) { |
| 163 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (c->format_version < 2) { |
| 164 | ✗ | AVStream *st = s->streams[i]; | |
| 165 | ✗ | snprintf(buf, sizeof(buf) - 200, "%d,%c,%s=", i, get_media_type_char(st->codecpar->codec_type), | |
| 166 | ✗ | av_hash_get_name(c->hashes[i].hash)); | |
| 167 | } else | ||
| 168 | 5 | snprintf(buf, sizeof(buf) - 200, "%d, %11"PRId64", ", i, c->hashes[i].stream_size); | |
| 169 | } else { | ||
| 170 | ✗ | snprintf(buf, sizeof(buf) - 200, "%s=", av_hash_get_name(c->hashes[i].hash)); | |
| 171 | } | ||
| 172 | 5 | av_hash_final_hex(c->hashes[i].hash, buf + strlen(buf), sizeof(buf) - strlen(buf)); | |
| 173 | 5 | avio_write(s->pb, buf, strlen(buf)); | |
| 174 | |||
| 175 |
2/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
|
5 | if (c->per_stream && c->format_version >= 2) { |
| 176 | 5 | const AVStream *st = s->streams[i]; | |
| 177 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (st->codecpar->nb_coded_side_data) |
| 178 | ✗ | hash_print_sidedata(s, st->codecpar->coded_side_data, st->codecpar->nb_coded_side_data); | |
| 179 | } | ||
| 180 | |||
| 181 | 5 | avio_printf(s->pb, "\n"); | |
| 182 | } | ||
| 183 | |||
| 184 | 4 | return 0; | |
| 185 | } | ||
| 186 | #endif | ||
| 187 | |||
| 188 | 312 | static void hash_free(struct AVFormatContext *s) | |
| 189 | { | ||
| 190 | 312 | struct HashContext *c = s->priv_data; | |
| 191 |
1/2✓ Branch 0 taken 312 times.
✗ Branch 1 not taken.
|
312 | if (c->hashes) { |
| 192 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 308 times.
|
312 | int num_hashes = c->per_stream ? s->nb_streams : 1; |
| 193 |
2/2✓ Branch 0 taken 313 times.
✓ Branch 1 taken 312 times.
|
625 | for (int i = 0; i < num_hashes; i++) { |
| 194 | 313 | av_hash_freep(&c->hashes[i].hash); | |
| 195 | } | ||
| 196 | } | ||
| 197 | 312 | av_freep(&c->hashes); | |
| 198 | 312 | } | |
| 199 | |||
| 200 | #if CONFIG_HASH_MUXER | ||
| 201 | static const AVClass hash_hashenc_class = { | ||
| 202 | .class_name = "hash muxer", | ||
| 203 | .item_name = av_default_item_name, | ||
| 204 | .option = &hash_options[1], | ||
| 205 | .version = LIBAVUTIL_VERSION_INT, | ||
| 206 | }; | ||
| 207 | |||
| 208 | const FFOutputFormat ff_hash_muxer = { | ||
| 209 | .p.name = "hash", | ||
| 210 | .p.long_name = NULL_IF_CONFIG_SMALL("Hash testing"), | ||
| 211 | .priv_data_size = sizeof(struct HashContext), | ||
| 212 | .p.audio_codec = AV_CODEC_ID_PCM_S16LE, | ||
| 213 | .p.video_codec = AV_CODEC_ID_RAWVIDEO, | ||
| 214 | .init = hash_init, | ||
| 215 | .write_packet = hash_write_packet, | ||
| 216 | .write_trailer = hash_write_trailer, | ||
| 217 | .deinit = hash_free, | ||
| 218 | .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | | ||
| 219 | AVFMT_TS_NEGATIVE, | ||
| 220 | .p.priv_class = &hash_hashenc_class, | ||
| 221 | }; | ||
| 222 | #endif | ||
| 223 | |||
| 224 | #if CONFIG_MD5_MUXER | ||
| 225 | static const AVClass md5enc_class = { | ||
| 226 | .class_name = "MD5 muxer", | ||
| 227 | .item_name = av_default_item_name, | ||
| 228 | .option = md5_options, | ||
| 229 | .version = LIBAVUTIL_VERSION_INT, | ||
| 230 | }; | ||
| 231 | |||
| 232 | const FFOutputFormat ff_md5_muxer = { | ||
| 233 | .p.name = "md5", | ||
| 234 | .p.long_name = NULL_IF_CONFIG_SMALL("MD5 testing"), | ||
| 235 | .priv_data_size = sizeof(struct HashContext), | ||
| 236 | .p.audio_codec = AV_CODEC_ID_PCM_S16LE, | ||
| 237 | .p.video_codec = AV_CODEC_ID_RAWVIDEO, | ||
| 238 | .init = hash_init, | ||
| 239 | .write_packet = hash_write_packet, | ||
| 240 | .write_trailer = hash_write_trailer, | ||
| 241 | .deinit = hash_free, | ||
| 242 | .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | | ||
| 243 | AVFMT_TS_NEGATIVE, | ||
| 244 | .p.priv_class = &md5enc_class, | ||
| 245 | }; | ||
| 246 | #endif | ||
| 247 | |||
| 248 | #if CONFIG_STREAMHASH_MUXER | ||
| 249 | 4 | static int streamhash_init(struct AVFormatContext *s) | |
| 250 | { | ||
| 251 | int res, i; | ||
| 252 | 4 | struct HashContext *c = s->priv_data; | |
| 253 | 4 | c->per_stream = 1; | |
| 254 | 4 | c->hashes = av_calloc(s->nb_streams, sizeof(*c->hashes)); | |
| 255 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!c->hashes) |
| 256 | ✗ | return AVERROR(ENOMEM); | |
| 257 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
|
9 | for (i = 0; i < s->nb_streams; i++) { |
| 258 | 5 | res = av_hash_alloc(&c->hashes[i].hash, c->hash_name); | |
| 259 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (res < 0) { |
| 260 | ✗ | return res; | |
| 261 | } | ||
| 262 | 5 | av_hash_init(c->hashes[i].hash); | |
| 263 | } | ||
| 264 | 4 | return 0; | |
| 265 | } | ||
| 266 | |||
| 267 | 4 | static int streamhash_write_header(struct AVFormatContext *s) | |
| 268 | { | ||
| 269 | 4 | struct HashContext *c = s->priv_data; | |
| 270 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (c->format_version < 2) |
| 271 | ✗ | return 0; | |
| 272 | 4 | avio_printf(s->pb, "#format: stream checksums\n" | |
| 273 | "#version: %d\n" | ||
| 274 | 4 | "#hash: %s\n", c->format_version, av_hash_get_name(c->hashes->hash)); | |
| 275 | 4 | hash_print_extradata(s); | |
| 276 | 4 | ff_framehash_write_header(s); | |
| 277 | 4 | avio_printf(s->pb, "#stream#, size, hash\n"); | |
| 278 | 4 | av_hash_init(c->hashes->hash); | |
| 279 | 4 | return 0; | |
| 280 | } | ||
| 281 | |||
| 282 | 2866 | static int streamhash_write_packet(struct AVFormatContext *s, AVPacket *pkt) | |
| 283 | { | ||
| 284 | 2866 | struct HashContext *c = s->priv_data; | |
| 285 | 2866 | av_hash_update(c->hashes[pkt->stream_index].hash, pkt->data, pkt->size); | |
| 286 | 2866 | c->hashes[pkt->stream_index].stream_size += pkt->size; | |
| 287 | 2866 | return 0; | |
| 288 | } | ||
| 289 | |||
| 290 | static const AVClass streamhashenc_class = { | ||
| 291 | .class_name = "stream hash muxer", | ||
| 292 | .item_name = av_default_item_name, | ||
| 293 | .option = hash_options, | ||
| 294 | .version = LIBAVUTIL_VERSION_INT, | ||
| 295 | }; | ||
| 296 | |||
| 297 | const FFOutputFormat ff_streamhash_muxer = { | ||
| 298 | .p.name = "streamhash", | ||
| 299 | .p.long_name = NULL_IF_CONFIG_SMALL("Per-stream hash testing"), | ||
| 300 | .priv_data_size = sizeof(struct HashContext), | ||
| 301 | .p.audio_codec = AV_CODEC_ID_PCM_S16LE, | ||
| 302 | .p.video_codec = AV_CODEC_ID_RAWVIDEO, | ||
| 303 | .init = streamhash_init, | ||
| 304 | .write_header = streamhash_write_header, | ||
| 305 | .write_packet = streamhash_write_packet, | ||
| 306 | .write_trailer = hash_write_trailer, | ||
| 307 | .deinit = hash_free, | ||
| 308 | .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | | ||
| 309 | AVFMT_TS_NEGATIVE, | ||
| 310 | .p.priv_class = &streamhashenc_class, | ||
| 311 | }; | ||
| 312 | #endif | ||
| 313 | |||
| 314 | #if CONFIG_FRAMEHASH_MUXER || CONFIG_FRAMEMD5_MUXER | ||
| 315 | 308 | static int framehash_init(struct AVFormatContext *s) | |
| 316 | { | ||
| 317 | int res; | ||
| 318 | 308 | struct HashContext *c = s->priv_data; | |
| 319 | 308 | c->per_stream = 0; | |
| 320 | 308 | c->hashes = av_mallocz(sizeof(*c->hashes)); | |
| 321 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 308 times.
|
308 | if (!c->hashes) |
| 322 | ✗ | return AVERROR(ENOMEM); | |
| 323 | 308 | res = av_hash_alloc(&c->hashes->hash, c->hash_name); | |
| 324 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 308 times.
|
308 | if (res < 0) |
| 325 | ✗ | return res; | |
| 326 | 308 | return 0; | |
| 327 | } | ||
| 328 | |||
| 329 | 308 | static int framehash_write_header(struct AVFormatContext *s) | |
| 330 | { | ||
| 331 | 308 | struct HashContext *c = s->priv_data; | |
| 332 | 308 | avio_printf(s->pb, "#format: frame checksums\n"); | |
| 333 | 308 | avio_printf(s->pb, "#version: %d\n", c->format_version); | |
| 334 | 308 | avio_printf(s->pb, "#hash: %s\n", av_hash_get_name(c->hashes->hash)); | |
| 335 | 308 | hash_print_extradata(s); | |
| 336 | 308 | ff_framehash_write_header(s); | |
| 337 | 308 | avio_printf(s->pb, "#stream#, dts, pts, duration, size, hash\n"); | |
| 338 | 308 | return 0; | |
| 339 | } | ||
| 340 | |||
| 341 | 7242 | static int framehash_write_packet(struct AVFormatContext *s, AVPacket *pkt) | |
| 342 | { | ||
| 343 | 7242 | struct HashContext *c = s->priv_data; | |
| 344 | char buf[AV_HASH_MAX_SIZE*2+128]; | ||
| 345 | int len; | ||
| 346 | 7242 | av_hash_init(c->hashes->hash); | |
| 347 | 7242 | av_hash_update(c->hashes->hash, pkt->data, pkt->size); | |
| 348 | |||
| 349 | 7242 | snprintf(buf, sizeof(buf) - (AV_HASH_MAX_SIZE * 2 + 1), "%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, ", | |
| 350 | pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size); | ||
| 351 | 7242 | len = strlen(buf); | |
| 352 | 7242 | av_hash_final_hex(c->hashes->hash, buf + len, sizeof(buf) - len); | |
| 353 | 7242 | avio_write(s->pb, buf, strlen(buf)); | |
| 354 | |||
| 355 |
3/4✓ Branch 0 taken 7242 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 7240 times.
|
7242 | if (c->format_version > 1 && pkt->side_data_elems) { |
| 356 | 2 | hash_print_sidedata(s, pkt->side_data, pkt->side_data_elems); | |
| 357 | } | ||
| 358 | |||
| 359 | 7242 | avio_printf(s->pb, "\n"); | |
| 360 | 7242 | return 0; | |
| 361 | } | ||
| 362 | #endif | ||
| 363 | |||
| 364 | #if CONFIG_FRAMEHASH_MUXER | ||
| 365 | static const AVClass framehash_class = { | ||
| 366 | .class_name = "frame hash muxer", | ||
| 367 | .item_name = av_default_item_name, | ||
| 368 | .option = hash_options, | ||
| 369 | .version = LIBAVUTIL_VERSION_INT, | ||
| 370 | }; | ||
| 371 | |||
| 372 | const FFOutputFormat ff_framehash_muxer = { | ||
| 373 | .p.name = "framehash", | ||
| 374 | .p.long_name = NULL_IF_CONFIG_SMALL("Per-frame hash testing"), | ||
| 375 | .priv_data_size = sizeof(struct HashContext), | ||
| 376 | .p.audio_codec = AV_CODEC_ID_PCM_S16LE, | ||
| 377 | .p.video_codec = AV_CODEC_ID_RAWVIDEO, | ||
| 378 | .init = framehash_init, | ||
| 379 | .write_header = framehash_write_header, | ||
| 380 | .write_packet = framehash_write_packet, | ||
| 381 | .deinit = hash_free, | ||
| 382 | .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | | ||
| 383 | AVFMT_TS_NEGATIVE, | ||
| 384 | .p.priv_class = &framehash_class, | ||
| 385 | }; | ||
| 386 | #endif | ||
| 387 | |||
| 388 | #if CONFIG_FRAMEMD5_MUXER | ||
| 389 | static const AVClass framemd5_class = { | ||
| 390 | .class_name = "frame MD5 muxer", | ||
| 391 | .item_name = av_default_item_name, | ||
| 392 | .option = framemd5_options, | ||
| 393 | .version = LIBAVUTIL_VERSION_INT, | ||
| 394 | }; | ||
| 395 | |||
| 396 | const FFOutputFormat ff_framemd5_muxer = { | ||
| 397 | .p.name = "framemd5", | ||
| 398 | .p.long_name = NULL_IF_CONFIG_SMALL("Per-frame MD5 testing"), | ||
| 399 | .priv_data_size = sizeof(struct HashContext), | ||
| 400 | .p.audio_codec = AV_CODEC_ID_PCM_S16LE, | ||
| 401 | .p.video_codec = AV_CODEC_ID_RAWVIDEO, | ||
| 402 | .init = framehash_init, | ||
| 403 | .write_header = framehash_write_header, | ||
| 404 | .write_packet = framehash_write_packet, | ||
| 405 | .deinit = hash_free, | ||
| 406 | .p.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | | ||
| 407 | AVFMT_TS_NEGATIVE, | ||
| 408 | .p.priv_class = &framemd5_class, | ||
| 409 | }; | ||
| 410 | #endif | ||
| 411 |