[FFmpeg-devel,3/4] avformat/hashenc: add option to create hash per stream

Submitted by Moritz Barsnick on Aug. 11, 2019, 12:47 p.m.

Details

Message ID 08b3f8a989581481678dcd0ce5a06105269cdccf.1565527501.git.barsnick@gmx.net
State New
Headers show

Commit Message

Moritz Barsnick Aug. 11, 2019, 12:47 p.m.
Non-frame based muxers only, the frame based ones are already per
stream.

Signed-off-by: Moritz Barsnick <barsnick@gmx.net>
---
 doc/muxers.texi       |  5 +++++
 libavformat/hashenc.c | 41 +++++++++++++++++++++++++++++------------
 2 files changed, 34 insertions(+), 12 deletions(-)

--
2.20.1

Patch hide | download patch | download mbox

diff --git a/doc/muxers.texi b/doc/muxers.texi
index bc38cf6029..34ca7f07cb 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -511,6 +511,11 @@  Supported values include @code{MD5}, @code{murmur3}, @code{RIPEMD128},
 @code{SHA224}, @code{SHA256} (default), @code{SHA512/224}, @code{SHA512/256},
 @code{SHA384}, @code{SHA512}, @code{CRC32} and @code{adler32}.

+@item per_stream @var{bool}
+Whether to calculate a hash per stream, instead of combined over all
+packets' payload. Each stream's hash is prefixed with its stream index.
+Default is @code{false}.
+
 @end table

 @subsection Examples
diff --git a/libavformat/hashenc.c b/libavformat/hashenc.c
index 96e00f580c..394b8a0fce 100644
--- a/libavformat/hashenc.c
+++ b/libavformat/hashenc.c
@@ -31,6 +31,7 @@  struct HashContext {
     const AVClass *avclass;
     struct AVHashContext **hashes;
     char *hash_name;
+    int per_stream;
     int format_version;
 };

@@ -40,10 +41,13 @@  struct HashContext {
     { "hash", "set hash to use", OFFSET(hash_name), AV_OPT_TYPE_STRING, {.str = defaulttype}, 0, 0, ENC }
 #define FORMAT_VERSION_OPT \
     { "format_version", "file format version", OFFSET(format_version), AV_OPT_TYPE_INT, {.i64 = 2}, 1, 2, ENC }
+#define PER_STREAM_OPT \
+    { "per_stream", "whether to calculate a hash per stream", OFFSET(per_stream), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, ENC }

 #if CONFIG_HASH_MUXER
 static const AVOption hash_options[] = {
     HASH_OPT("sha256"),
+    PER_STREAM_OPT,
     { NULL },
 };
 #endif
@@ -59,6 +63,7 @@  static const AVOption framehash_options[] = {
 #if CONFIG_MD5_MUXER
 static const AVOption md5_options[] = {
     HASH_OPT("md5"),
+    PER_STREAM_OPT,
     { NULL },
 };
 #endif
@@ -74,39 +79,51 @@  static const AVOption framemd5_options[] = {
 #if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER
 static int hash_write_header(struct AVFormatContext *s)
 {
-    int res;
+    int i, res;
     struct HashContext *c = s->priv_data;
-    c->hashes = av_malloc_array(1, sizeof(c->hashes));
+    int num_hashes = c->per_stream ? s->nb_streams : 1;
+    c->hashes = av_malloc_array(num_hashes, sizeof(c->hashes));
     if (!c->hashes)
         return AVERROR(ENOMEM);
-    res = av_hash_alloc(&c->hashes[0], c->hash_name);
-    if (res < 0) {
-        av_freep(&c->hashes);
-        return res;
+    for (i = 0; i < num_hashes; i++) {
+    res = av_hash_alloc(&c->hashes[i], c->hash_name);
+    if (res < 0)
+        goto err;
+    av_hash_init(c->hashes[i]);
     }
-    av_hash_init(c->hashes[0]);
     return 0;
+err:
+    for (int j = 0; j < i; j++)
+        av_hash_freep(&c->hashes[j]);
+    av_freep(&c->hashes);
+    return res;
 }

 static int hash_write_packet(struct AVFormatContext *s, AVPacket *pkt)
 {
     struct HashContext *c = s->priv_data;
-    av_hash_update(c->hashes[0], pkt->data, pkt->size);
+    av_hash_update(c->hashes[c->per_stream ? pkt->stream_index : 0], pkt->data, pkt->size);
     return 0;
 }

 static int hash_write_trailer(struct AVFormatContext *s)
 {
     struct HashContext *c = s->priv_data;
+    int num_hashes = c->per_stream ? s->nb_streams : 1;
+    for (int i = 0; i < num_hashes; i++) {
     char buf[AV_HASH_MAX_SIZE*2+128];
-    snprintf(buf, sizeof(buf) - 200, "%s=", av_hash_get_name(c->hashes[0]));
-
-    av_hash_final_hex(c->hashes[0], buf + strlen(buf), sizeof(buf) - strlen(buf));
+    if (c->per_stream) {
+        snprintf(buf, sizeof(buf) - 200, "%d,%s=", i, av_hash_get_name(c->hashes[i]));
+    } else {
+        snprintf(buf, sizeof(buf) - 200, "%s=", av_hash_get_name(c->hashes[i]));
+    }
+    av_hash_final_hex(c->hashes[i], buf + strlen(buf), sizeof(buf) - strlen(buf));
     av_strlcatf(buf, sizeof(buf), "\n");
     avio_write(s->pb, buf, strlen(buf));
     avio_flush(s->pb);

-    av_hash_freep(&c->hashes[0]);
+    av_hash_freep(&c->hashes[i]);
+    }
     av_freep(&c->hashes);
     return 0;
 }