Ticket #4566: propagate-metadata-changes-to-icecast-outputs.patch

File propagate-metadata-changes-to-icecast-outputs.patch, 6.9 KB (added by Cristian Onet, 3 years ago)

Patch that forwards StreamTitle as icecast mertadata

  • fftools/ffmpeg.c

    diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
    index e7384f052a..ba89270092 100644
    a b static int transcode(void)  
    43414341        av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
    43424342    }
    43434343
     4344    // check if metadata propagation is required
     4345    int propagate_metadata_updates = 0;
     4346    for (i = 0; i < nb_output_files; i++) {
     4347        if (strncmp(output_files[i]->ctx->url, "icecast://", 10) == 0) {
     4348            propagate_metadata_updates = 1;
     4349            break;
     4350        }
     4351    }
     4352
    43444353    timer_start = av_gettime_relative();
    43454354
    43464355#if HAVE_THREADS
    static int transcode(void)  
    43704379
    43714380        /* dump report by using the output first video and audio streams */
    43724381        print_report(0, timer_start, cur_time);
     4382
     4383        for (i = 0; i < nb_input_streams && propagate_metadata_updates; i++) {
     4384            ist = input_streams[i];
     4385            if (input_files[ist->file_index]->ctx->event_flags & AVFMT_EVENT_FLAG_METADATA_UPDATED) {
     4386                input_files[ist->file_index]->ctx->event_flags &= ~AVFMT_EVENT_FLAG_METADATA_UPDATED;
     4387                av_log(NULL, AV_LOG_TRACE, "Input metadata update detected on stream: %d\n", ist->file_index);
     4388                for (i = 0; i < nb_output_files; i++) {
     4389                    av_dict_copy(&output_files[i]->ctx->metadata, input_files[ist->file_index]->ctx->metadata, 0);
     4390                    output_files[i]->ctx->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
     4391                }
     4392            }
     4393        }
    43734394    }
    43744395#if HAVE_THREADS
    43754396    free_input_threads();
  • fftools/ffmpeg_opt.c

    diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
    index 6e18a4a23e..c0ca635fda 100644
    a b  
    3333#include "opt_common.h"
    3434
    3535#include "libavformat/avformat.h"
     36#include "libavformat/url.h"
    3637
    3738#include "libavcodec/avcodec.h"
    3839#include "libavcodec/bsf.h"
    loop_end:  
    28452846            print_error(filename, err);
    28462847            exit_program(1);
    28472848        }
     2849        // set the AVFormatContext parent reference in URLContext
     2850        if (strncmp(oc->url, "icecast://", 10) == 0) {
     2851            av_log(NULL, AV_LOG_TRACE, "Set AVFormatContext parent reference in URLContext\n");
     2852            ((URLContext *)oc->pb->opaque)->ctx = oc;
     2853        }
    28482854    } else if (strcmp(oc->oformat->name, "image2")==0 && !av_filename_number_test(filename))
    28492855        assert_file_overwrite(filename);
    28502856
  • libavformat/icecast.c

    diff --git a/libavformat/icecast.c b/libavformat/icecast.c
    index b06c53cabd..2524688255 100644
    a b typedef struct IcecastContext {  
    3333    URLContext *hd;
    3434    int send_started;
    3535    char *user;
     36    char *metadata_url;
    3637    // Options
    3738    char *content_type;
    3839    char *description;
    static int icecast_open(URLContext *h, const char *uri, int flags)  
    163164        goto cleanup;
    164165    }
    165166
     167    // Build URI for metadata updates
     168    ff_url_join(h_url, sizeof(h_url),
     169                s->tls ? "https" : "http",
     170                auth, host, port, "/admin/metadata?mode=updinfo&charset=UTF-8&mount=%s", path);
     171    s->metadata_url = av_strdup(h_url);
     172
    166173    // Build new URI for passing to http protocol
    167174    ff_url_join(h_url, sizeof(h_url),
    168175                s->tls ? "https" : "http",
    cleanup:  
    178185    return ret;
    179186}
    180187
     188static int metadata_update_interrupt_cb(void *ctx) {
     189    return 0;
     190}
     191
     192const AVIOInterruptCB icecast_metadata_int_cb = { metadata_update_interrupt_cb, NULL };
     193
     194// escape the song name (like bprint_escaped_path but also escapes '&' and '#')
     195static void escape_metadata(char *buffer, int size, const char *song) {
     196#define NEEDS_ESCAPE(ch) \
     197    ((ch) <= ' ' || (ch) >= '\x7f' || \
     198     (ch) == '"' || (ch) == '%' || (ch) == '<' || (ch) == '>' || (ch) == '\\' || \
     199     (ch) == '^' || (ch) == '`' || (ch) == '{' || (ch) == '}' || (ch) == '|' || \
     200     (ch) == '&' || (ch) == '#')
     201    char *q = buffer;
     202    while (*song && q - buffer < size - 4) {
     203        if (song[0] == '%' && av_isxdigit(song[1]) && av_isxdigit(song[2])) {
     204            *q++ = *song++;
     205            *q++ = *song++;
     206            *q++ = *song++;
     207        } else if (NEEDS_ESCAPE(*song)) {
     208            q += snprintf(q, 4, "%%%02X", (uint8_t)*song++);
     209        } else {
     210            *q++ = *song++;
     211        }
     212    }
     213    // terminate the string
     214    if (q - buffer < size) {
     215        *q = '\0';
     216    } else {
     217        buffer[size - 1] = '\0';
     218    }
     219}
     220
     221static void update_metadata(IcecastContext *s, const char *song) {
     222    // escape the song name
     223    char escaped_song[1024];
     224    escape_metadata(&escaped_song, sizeof(escaped_song), song);
     225
     226    // build the metadata update url
     227    char h_url[2048];
     228    snprintf(h_url, sizeof(h_url), "%s&song=%s", s->metadata_url, &escaped_song);
     229    av_log(s, AV_LOG_TRACE, "StreamTitle update: '%s', url: '%s'\n", song, h_url);
     230
     231    // call the metadata update url
     232    AVDictionary *opt_dict = NULL;
     233    av_dict_set(&opt_dict, "auth_type", "basic", 0);
     234    if (NOT_EMPTY(s->user_agent))
     235        av_dict_set(&opt_dict, "user_agent", s->user_agent, 0);
     236    AVIOContext *avio = NULL;
     237    int ret = avio_open2(&avio, h_url, AVIO_FLAG_READ, &icecast_metadata_int_cb, &opt_dict);
     238    if (ret < 0) {
     239        av_log(NULL, AV_LOG_WARNING, "Failed to open metadata update URL \"%s\": %s\n", h_url, av_err2str(ret));
     240    }
     241    if ((ret = avio_closep(&avio)) < 0) {
     242        av_log(NULL, AV_LOG_ERROR, "Error closing metadata update URL \"%s\": %s\n", h_url, av_err2str(ret));
     243    }
     244    av_dict_free(&opt_dict);
     245}
     246
    181247static int icecast_write(URLContext *h, const uint8_t *buf, int size)
    182248{
    183249    IcecastContext *s = h->priv_data;
    static int icecast_write(URLContext *h, const uint8_t *buf, int size)  
    202268            }
    203269        }
    204270    }
     271    if (h->ctx && h->ctx->event_flags & AVFMT_EVENT_FLAG_METADATA_UPDATED) {
     272        h->ctx->event_flags &= ~AVFMT_EVENT_FLAG_METADATA_UPDATED;
     273        AVDictionaryEntry *song_entry = av_dict_get(h->ctx->metadata, "StreamTitle",  NULL, 0);
     274        if (song_entry) {
     275            update_metadata(s, song_entry->value);
     276        }
     277    }
    205278    return ffurl_write(s->hd, buf, size);
    206279}
    207280
  • libavformat/url.h

    diff --git a/libavformat/url.h b/libavformat/url.h
    index 3cfe3ecc5c..c5f13fbb59 100644
    a b  
    3333#define URL_PROTOCOL_FLAG_NETWORK       2 /*< The protocol uses network */
    3434
    3535extern const AVClass ffurl_context_class;
     36typedef struct AVFormatContext AVFormatContext;
    3637
    3738typedef struct URLContext {
    3839    const AVClass *av_class;    /**< information for av_log(). Set by url_open(). */
    typedef struct URLContext {  
    4849    const char *protocol_whitelist;
    4950    const char *protocol_blacklist;
    5051    int min_packet_size;        /**< if non zero, the stream is packetized with this min packet size */
     52    AVFormatContext *ctx;       /**< reference to the format context */
    5153} URLContext;
    5254
    5355typedef struct URLProtocol {