On Sun, 29 Jan 2017, Peter Große wrote:

Also makes sure all streams are assigned to exactly one AdaptationSet.

This patch is originally based partially on code by Vignesh Venkatasubramanian.

Signed-off-by: Peter Große <[email protected]>
---
v2:
* changing default stream assignment moved to separate patch
* removed metadata field from AdaptationSet
* check strtol result in parse_adaptation_set to detect non-numeric input
* move allocation of adaptation set to separate function
* fix string parsing problems pointed out by Martin
---
libavformat/dashenc.c | 201 ++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 170 insertions(+), 31 deletions(-)

diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
index ae1bb0b..94b01e4 100644
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@ -24,6 +24,7 @@
#include <unistd.h>
#endif

+#include "libavutil/avutil.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
@@ -57,9 +58,14 @@ typedef struct Segment {
    int n;
} Segment;

+typedef struct AdaptationSet {
+    char id[10];
+    enum AVMediaType media_type;
+} AdaptationSet;
+
typedef struct OutputStream {
    AVFormatContext *ctx;
-    int ctx_inited;
+    int ctx_inited, as_idx;
    uint8_t iobuf[32768];
    AVIOContext *out;
    int packets_written;
@@ -78,6 +84,9 @@ typedef struct OutputStream {

typedef struct DASHContext {
    const AVClass *class;  /* Class for private options. */
+    char *adaptation_sets;
+    AdaptationSet *as;
+    int nb_as;
    int window_size;
    int extra_window_size;
    int min_seg_duration;
@@ -86,7 +95,7 @@ typedef struct DASHContext {
    int use_timeline;
    int single_file;
    OutputStream *streams;
-    int has_video, has_audio;
+    int has_video;
    int64_t last_duration;
    int64_t total_duration;
    char availability_start_time[100];
@@ -175,6 +184,12 @@ static void dash_free(AVFormatContext *s)
{
    DASHContext *c = s->priv_data;
    int i, j;
+
+    if (c->as) {
+        av_freep(&c->as);
+        c->nb_as = 0;
+    }
+
    if (!c->streams)
        return;
    for (i = 0; i < s->nb_streams; i++) {
@@ -435,12 +450,157 @@ static void format_date_now(char *buf, int size)
    }
}

+static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int 
as_index)
+{
+    DASHContext *c = s->priv_data;
+    AdaptationSet *as = &c->as[as_index];
+    int i;
+
+    avio_printf(out, "\t\t<AdaptationSet id=\"%s\" contentType=\"%s\" segmentAlignment=\"true\" 
bitstreamSwitching=\"true\">\n",
+                as->id, as->media_type == AVMEDIA_TYPE_VIDEO ? "video" : 
"audio");
+
+    for (i = 0; i < s->nb_streams; i++) {
+        OutputStream *os = &c->streams[i];
+
+        if (os->as_idx - 1 != as_index)
+            continue;
+
+        if (as->media_type == AVMEDIA_TYPE_VIDEO) {
+            avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/mp4\" codecs=\"%s\"%s 
width=\"%d\" height=\"%d\">\n",
+                i, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->width, 
s->streams[i]->codecpar->height);
+        } else {
+            avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/mp4\" 
codecs=\"%s\"%s audioSamplingRate=\"%d\">\n",
+                i, os->codec_str, os->bandwidth_str, 
s->streams[i]->codecpar->sample_rate);
+            avio_printf(out, "\t\t\t\t<AudioChannelConfiguration 
schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" 
/>\n",
+                s->streams[i]->codecpar->channels);
+        }
+        output_segment_list(os, out, c);
+        avio_printf(out, "\t\t\t</Representation>\n");
+    }
+    avio_printf(out, "\t\t</AdaptationSet>\n");
+
+    return 0;
+}
+
+static int add_adaptation_set(AVFormatContext *s, AdaptationSet **as, enum 
AVMediaType type)
+{
+    DASHContext *c = s->priv_data;
+
+    void *mem = av_realloc(c->as, sizeof(*c->as) * (c->nb_as + 1));
+    if (!mem)
+        return AVERROR(ENOMEM);
+    c->as = mem;
+    ++c->nb_as;
+
+    *as = &c->as[c->nb_as - 1];
+    memset(*as, 0, sizeof(**as));
+    (*as)->media_type = type;
+
+    return 0;
+}
+
+static int parse_adaptation_sets(AVFormatContext *s)
+{
+    DASHContext *c = s->priv_data;
+    const char *p = c->adaptation_sets;
+    enum { new_set, parse_id, parsing_streams } state;
+    AdaptationSet *as;
+    int i, n, ret;
+    enum AVMediaType types[] = { AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, 0 };
+
+    // default: one AdaptationSet for each media type
+    if (!p) {
+        for (n = 0; types[n]; n++) {
+            int as_idx = 0;
+
+            for (i = 0; i < s->nb_streams; i++) {
+                if (s->streams[i]->codecpar->codec_type != types[n])
+                    continue;
+
+                if (!as_idx) {
+                    if ((ret = add_adaptation_set(s, &as, types[n])) < 0)
+                        return ret;
+                    as_idx = c->nb_as;
+
+                    snprintf(as->id, sizeof(as->id), "%d", i);
+                }
+                c->streams[i].as_idx = as_idx;
+            }
+        }
+    }

When testing this, I noticed a few bugs in the block above:
- It should return from here, not proceed to the block below doing while (*p) which crashes. I added an "goto end" and jumping to the last block to check that all streams were mapped. - AVMEDIA_TYPE_VIDEO actually has got the value 0, so we need to use AVMEDIA_TYPE_UNKNOWN as sentinel.

I've amended the patch with fixes for that.

// Martin
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to