[FFmpeg-devel] [PATCH] Add a generic hash API.

Nicolas George nicolas.george at normalesup.org
Fri May 10 14:09:13 CEST 2013


Le decadi 20 floréal, an CCXXI, Reimar Döffinger a écrit :
> Also use this API in framemd5.

Could have been split into separate patches.

> Signed-off-by: Reimar Döffinger <Reimar.Doeffinger at gmx.de>
> ---
>  libavformat/md5enc.c |   57 +++++++++++++------
>  libavutil/Makefile   |    1 +
>  libavutil/hash.c     |  153 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  libavutil/hash.h     |   35 ++++++++++++
>  4 files changed, 230 insertions(+), 16 deletions(-)
>  create mode 100644 libavutil/hash.c
>  create mode 100644 libavutil/hash.h
> 
> diff --git a/libavformat/md5enc.c b/libavformat/md5enc.c
> index d5c1fdd..8a2b5db 100644
> --- a/libavformat/md5enc.c
> +++ b/libavformat/md5enc.c
> @@ -19,21 +19,28 @@
>   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>   */
>  
> -#include "libavutil/md5.h"
> +#include "libavutil/avassert.h"
> +#include "libavutil/avstring.h"
> +#include "libavutil/hash.h"
> +#include "libavutil/opt.h"
>  #include "avformat.h"
>  #include "internal.h"
>  
>  struct MD5Context {
> -    struct AVMD5 *md5;
> +    const AVClass *avclass;
> +    struct AVHash *hash;
> +    char *hash_name;
>  };
>  
>  static void md5_finish(struct AVFormatContext *s, char *buf)
>  {
>      struct MD5Context *c = s->priv_data;
> -    uint8_t md5[16];
> +    uint8_t md5[32];
>      int i, offset = strlen(buf);
> -    av_md5_final(c->md5, md5);
> -    for (i = 0; i < sizeof(md5); i++) {
> +    int len = av_hash_get_length(c->hash);
> +    av_assert0(len > 0 && len <= sizeof(md5));
> +    av_hash_final(c->hash, md5);
> +    for (i = 0; i < len; i++) {
>          snprintf(buf + offset, 3, "%02"PRIx8, md5[i]);
>          offset += 2;
>      }
> @@ -44,32 +51,48 @@ static void md5_finish(struct AVFormatContext *s, char *buf)
>      avio_flush(s->pb);
>  }
>  
> +#define OFFSET(x) offsetof(struct MD5Context, x)
> +#define ENC AV_OPT_FLAG_ENCODING_PARAM
> +static const AVOption hash_options[] = {
> +    { "hash", "set hash to use", OFFSET(hash_name), AV_OPT_TYPE_STRING, {.str = "md5"}, 0, 0, ENC },
> +    { NULL },
> +};
> +
> +static const AVClass hashenc_class = {
> +    .class_name = "hash encoder class",
> +    .item_name  = av_default_item_name,
> +    .option     = hash_options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +};
> +
>  #if CONFIG_MD5_MUXER
>  static int write_header(struct AVFormatContext *s)
>  {
>      struct MD5Context *c = s->priv_data;
> -    c->md5 = av_md5_alloc();
> -    if (!c->md5)

> +    c->hash = av_hash_alloc(c->hash_name);
> +    if (!c->hash)
>          return AVERROR(ENOMEM);

Missing useful error message and return code to distinguish between malloc
failure and unknown hash. See below.

> -    av_md5_init(c->md5);
> +    av_hash_init(c->hash);
>      return 0;
>  }
>  
>  static int write_packet(struct AVFormatContext *s, AVPacket *pkt)
>  {
>      struct MD5Context *c = s->priv_data;
> -    av_md5_update(c->md5, pkt->data, pkt->size);
> +    av_hash_update(c->hash, pkt->data, pkt->size);
>      return 0;
>  }
>  
>  static int write_trailer(struct AVFormatContext *s)
>  {
>      struct MD5Context *c = s->priv_data;
> -    char buf[64] = "MD5=";
> +    char buf[128];
> +    av_strlcpy(buf, av_hash_get_name(c->hash), sizeof(buf) - 100);
> +    av_strlcat(buf, "=", sizeof(buf) - 100);
>  
>      md5_finish(s, buf);
>  
> -    av_freep(&c->md5);
> +    av_hash_free(&c->hash);
>      return 0;
>  }
>  
> @@ -83,6 +106,7 @@ AVOutputFormat ff_md5_muxer = {
>      .write_packet      = write_packet,
>      .write_trailer     = write_trailer,
>      .flags             = AVFMT_NOTIMESTAMPS,
> +    .priv_class        = &hashenc_class,
>  };
>  #endif
>  
> @@ -90,8 +114,8 @@ AVOutputFormat ff_md5_muxer = {
>  static int framemd5_write_header(struct AVFormatContext *s)
>  {
>      struct MD5Context *c = s->priv_data;
> -    c->md5 = av_md5_alloc();
> -    if (!c->md5)

> +    c->hash = av_hash_alloc(c->hash_name);
> +    if (!c->hash)
>          return AVERROR(ENOMEM);

Ditto.

>      return ff_framehash_write_header(s);
>  }
> @@ -100,8 +124,8 @@ static int framemd5_write_packet(struct AVFormatContext *s, AVPacket *pkt)
>  {
>      struct MD5Context *c = s->priv_data;
>      char buf[256];
> -    av_md5_init(c->md5);
> -    av_md5_update(c->md5, pkt->data, pkt->size);
> +    av_hash_init(c->hash);
> +    av_hash_update(c->hash, pkt->data, pkt->size);
>  
>      snprintf(buf, sizeof(buf) - 64, "%d, %10"PRId64", %10"PRId64", %8d, %8d, ",
>               pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size);
> @@ -112,7 +136,7 @@ static int framemd5_write_packet(struct AVFormatContext *s, AVPacket *pkt)
>  static int framemd5_write_trailer(struct AVFormatContext *s)
>  {
>      struct MD5Context *c = s->priv_data;
> -    av_freep(&c->md5);
> +    av_hash_free(&c->hash);
>      return 0;
>  }
>  
> @@ -127,5 +151,6 @@ AVOutputFormat ff_framemd5_muxer = {
>      .write_trailer     = framemd5_write_trailer,
>      .flags             = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
>                           AVFMT_TS_NEGATIVE,
> +    .priv_class        = &hashenc_class,
>  };
>  #endif
> diff --git a/libavutil/Makefile b/libavutil/Makefile
> index 665be54..8df69a1 100644
> --- a/libavutil/Makefile
> +++ b/libavutil/Makefile
> @@ -81,6 +81,7 @@ OBJS = adler32.o                                                        \
>         file.o                                                           \
>         float_dsp.o                                                      \
>         frame.o                                                          \
> +       hash.o                                                           \
>         hmac.o                                                           \
>         imgutils.o                                                       \
>         intfloat_readwrite.o                                             \
> diff --git a/libavutil/hash.c b/libavutil/hash.c
> new file mode 100644
> index 0000000..ee71d37
> --- /dev/null
> +++ b/libavutil/hash.c
> @@ -0,0 +1,153 @@
> +/*
> + * Copyright (C) 2013 Reimar Döffinger <Reimar.Doeffinger at gmx.de>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +#include <stdint.h>
> +#include "hash.h"
> +
> +#include "adler32.h"
> +#include "crc.h"
> +#include "md5.h"
> +#include "murmur3.h"
> +#include "sha.h"
> +
> +#include "avstring.h"
> +#include "intreadwrite.h"
> +#include "mem.h"
> +
> +enum hashtype {
> +    MD5,
> +    MURMUR3,
> +    SHA160,
> +    SHA224,
> +    SHA256,
> +    CRC32,
> +    ADLER32,
> +    NUM_HASHES
> +};
> +

> +typedef struct AVHash {
> +    void *ctx;
> +    enum hashtype type;
> +    const AVCRC *crctab;
> +    uint32_t crc;
> +} AVHash;

Nit: AVHashContext for consistency.

> +
> +struct {
> +    const char *name;
> +    int length;
> +} hashdesc[] = {
> +    [MD5]     = {"MD5",     16},
> +    [MURMUR3] = {"murmur3", 16},
> +    [SHA160]  = {"SHA160",  20},
> +    [SHA224]  = {"SHA240",  28},
> +    [SHA256]  = {"SHA256",  32},
> +    [CRC32]   = {"CRC32",    4},
> +    [ADLER32] = {"adler32",  4},
> +};
> +
> +const char *av_hash_names(int i)
> +{
> +    if (i < 0 || i >= NUM_HASHES) return NULL;
> +    return hashdesc[i].name;
> +}
> +
> +const char *av_hash_get_name(const AVHash *ctx)
> +{
> +    return hashdesc[ctx->type].name;
> +}
> +

> +int av_hash_get_length(const AVHash *ctx)
> +{
> +    return hashdesc[ctx->type].length;
> +}

Nit+: size seems more appropriate than length.

> +AVHash *av_hash_alloc(const char *name)

int av_hash_alloc(const char *name, AVHashContext **ctx);

to distinguish between malloc failure (ENOMEM) and unknown algorithm (EINVAL
maybe).

> +{
> +    AVHash *res;
> +    int i;
> +    for (i = 0; i < NUM_HASHES; i++)
> +        if (av_strcasecmp(name, hashdesc[i].name) == 0)
> +            break;
> +    if (i >= NUM_HASHES) return NULL;
> +    res = av_mallocz(sizeof(*res));
> +    if (!res) return NULL;
> +    res->type = i;

> +    switch (i)
> +    {

Nit++: brace on the same line. Same on a few other places.

> +    case MD5:     res->ctx = av_md5_alloc(); break;
> +    case MURMUR3: res->ctx = av_murmur3_alloc(); break;
> +    case SHA160:
> +    case SHA224:
> +    case SHA256:  res->ctx = av_sha_alloc(); break;
> +    case CRC32:   res->crctab = av_crc_get_table(AV_CRC_32_IEEE); break;
> +    case ADLER32: break;
> +    }

It would have been nice to have the actual context structure directly inside
the context to avoid an extra indirection, but since most structures are
declared inside the source file, it does not seem possible in a clean
portable way. Can be enhanced later anyway.

> +    if (i != ADLER32 && i != CRC32 && !res->ctx)
> +        av_freep(&res);
> +    return res;
> +}
> +
> +void av_hash_init(AVHash *ctx)
> +{
> +    switch (ctx->type)
> +    {
> +    case MD5:     av_md5_init(ctx->ctx); break;
> +    case MURMUR3: av_murmur3_init(ctx->ctx); break;
> +    case SHA160:  av_sha_init(ctx->ctx, 160); break;
> +    case SHA224:  av_sha_init(ctx->ctx, 224); break;
> +    case SHA256:  av_sha_init(ctx->ctx, 256); break;
> +    case CRC32:
> +    case ADLER32: ctx->crc = 0; break;
> +    }
> +}
> +
> +void av_hash_update(AVHash *ctx, const uint8_t *src, int len)
> +{
> +    switch (ctx->type)
> +    {
> +    case MD5:     av_md5_update(ctx->ctx, src, len); break;
> +    case MURMUR3: av_murmur3_update(ctx->ctx, src, len); break;
> +    case SHA160:
> +    case SHA224:
> +    case SHA256:  av_sha_update(ctx->ctx, src, len); break;
> +    case CRC32:   ctx->crc = av_crc(ctx->crctab, ctx->crc, src, len); break;
> +    case ADLER32: ctx->crc = av_adler32_update(ctx->crc, src, len); break;
> +    }
> +}
> +
> +void av_hash_final(AVHash *ctx, uint8_t *dst)
> +{
> +    switch (ctx->type)
> +    {
> +    case MD5:     av_md5_final(ctx->ctx, dst); break;
> +    case MURMUR3: av_murmur3_final(ctx->ctx, dst); break;
> +    case SHA160:
> +    case SHA224:
> +    case SHA256:  av_sha_final(ctx->ctx, dst); break;
> +    case CRC32:
> +    case ADLER32: AV_WL32(dst, ctx->crc); break;
> +    }
> +}
> +
> +void av_hash_free(AVHash **ctx)

freep?

> +{
> +    if (*ctx)
> +        av_freep(&(*ctx)->ctx);
> +    av_freep(ctx);
> +}
> diff --git a/libavutil/hash.h b/libavutil/hash.h
> new file mode 100644
> index 0000000..60365fb
> --- /dev/null
> +++ b/libavutil/hash.h
> @@ -0,0 +1,35 @@
> +/*
> + * Copyright (C) 2013 Reimar Döffinger <Reimar.Doeffinger at gmx.de>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#ifndef AVUTIL_HASH_H
> +#define AVUTIL_HASH_H
> +
> +#include <stdint.h>
> +

/**
 * Allocate a hash context for the algorithm specified by name.
 *
 * @return  >= 0 for success, a negative error code for failure
 * @note  The context is not initialized, you must call av_hash_init().
 */

> +struct AVHash *av_hash_alloc(const char *name);

/**
 * Get the name of known hash algorithms.
 *
 * This function can be used to enumerate the algorithms.
 *
 * @param i  index of the hash algorithm, starting from 0
 * @return   a pointer to a static string or NULL if i is out of range
 */

> +const char *av_hash_names(int i);

/**
 * Get the name of the algorithm of a hash context.
 */

> +const char *av_hash_get_name(const struct AVHash *ctx);

/**
 * Get the output size in bytes of a hash context.
 */

> +int av_hash_get_length(const struct AVHash *ctx);

/**
 * Initialize or reset a hash context.
 */

> +void av_hash_init(struct AVHash *ctx);

/**
 * Update a hash context with additional data.
 */

> +void av_hash_update(struct AVHash *ctx, const uint8_t *src, int len);

/**
 * Finalize a hash context and compute the output.
 *
 * @param ctx  hash context
 * @param dst  output buffer, must have the correct size
 */
> +void av_hash_final(struct AVHash *ctx, uint8_t *dst);

/**
 * Free and clear a hash context.
 */

> +void av_hash_free(struct AVHash **ctx);
> +
> +#endif /* AVUTIL_MURMUR3_H */

Regards,

-- 
  Nicolas George
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20130510/4cf511b9/attachment.asc>


More information about the ffmpeg-devel mailing list