| Index: source/patched-ffmpeg-mt/libavformat/sapdec.c | 
| =================================================================== | 
| --- source/patched-ffmpeg-mt/libavformat/sapdec.c	(revision 0) | 
| +++ source/patched-ffmpeg-mt/libavformat/sapdec.c	(revision 0) | 
| @@ -0,0 +1,239 @@ | 
| +/* | 
| + * Session Announcement Protocol (RFC 2974) demuxer | 
| + * Copyright (c) 2010 Martin Storsjo | 
| + * | 
| + * 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 "avformat.h" | 
| +#include "libavutil/avstring.h" | 
| +#include "libavutil/intreadwrite.h" | 
| +#include "network.h" | 
| +#include "os_support.h" | 
| +#include "internal.h" | 
| +#if HAVE_SYS_SELECT_H | 
| +#include <sys/select.h> | 
| +#endif | 
| +#include <sys/time.h> | 
| + | 
| +struct SAPState { | 
| +    URLContext *ann_fd; | 
| +    AVFormatContext *sdp_ctx; | 
| +    ByteIOContext sdp_pb; | 
| +    uint16_t hash; | 
| +    char *sdp; | 
| +    int eof; | 
| +}; | 
| + | 
| +static int sap_probe(AVProbeData *p) | 
| +{ | 
| +    if (av_strstart(p->filename, "sap:", NULL)) | 
| +        return AVPROBE_SCORE_MAX; | 
| +    return 0; | 
| +} | 
| + | 
| +static int sap_read_close(AVFormatContext *s) | 
| +{ | 
| +    struct SAPState *sap = s->priv_data; | 
| +    if (sap->sdp_ctx) | 
| +        av_close_input_stream(sap->sdp_ctx); | 
| +    if (sap->ann_fd) | 
| +        url_close(sap->ann_fd); | 
| +    av_freep(&sap->sdp); | 
| +    ff_network_close(); | 
| +    return 0; | 
| +} | 
| + | 
| +static int sap_read_header(AVFormatContext *s, | 
| +                           AVFormatParameters *ap) | 
| +{ | 
| +    struct SAPState *sap = s->priv_data; | 
| +    char host[1024], path[1024], url[1024]; | 
| +    uint8_t recvbuf[1500]; | 
| +    int port; | 
| +    int ret, i; | 
| +    AVInputFormat* infmt; | 
| + | 
| +    if (!ff_network_init()) | 
| +        return AVERROR(EIO); | 
| + | 
| +    av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, | 
| +                 path, sizeof(path), s->filename); | 
| +    if (port < 0) | 
| +        port = 9875; | 
| + | 
| +    if (!host[0]) { | 
| +        /* Listen for announcements on sap.mcast.net if no host was specified */ | 
| +        av_strlcpy(host, "224.2.127.254", sizeof(host)); | 
| +    } | 
| + | 
| +    ff_url_join(url, sizeof(url), "udp", NULL, host, port, "?localport=%d", | 
| +                port); | 
| +    ret = url_open(&sap->ann_fd, url, URL_RDONLY); | 
| +    if (ret) | 
| +        goto fail; | 
| + | 
| +    while (1) { | 
| +        int addr_type, auth_len; | 
| +        int pos; | 
| + | 
| +        ret = url_read(sap->ann_fd, recvbuf, sizeof(recvbuf) - 1); | 
| +        if (ret == AVERROR(EAGAIN)) | 
| +            continue; | 
| +        if (ret < 0) | 
| +            goto fail; | 
| +        recvbuf[ret] = '\0'; /* Null terminate for easier parsing */ | 
| +        if (ret < 8) { | 
| +            av_log(s, AV_LOG_WARNING, "Received too short packet\n"); | 
| +            continue; | 
| +        } | 
| + | 
| +        if ((recvbuf[0] & 0xe0) != 0x20) { | 
| +            av_log(s, AV_LOG_WARNING, "Unsupported SAP version packet " | 
| +                                      "received\n"); | 
| +            continue; | 
| +        } | 
| + | 
| +        if (recvbuf[0] & 0x04) { | 
| +            av_log(s, AV_LOG_WARNING, "Received stream deletion " | 
| +                                      "announcement\n"); | 
| +            continue; | 
| +        } | 
| +        addr_type = recvbuf[0] & 0x10; | 
| +        auth_len  = recvbuf[1]; | 
| +        sap->hash = AV_RB16(&recvbuf[2]); | 
| +        pos = 4; | 
| +        if (addr_type) | 
| +            pos += 16; /* IPv6 */ | 
| +        else | 
| +            pos += 4; /* IPv4 */ | 
| +        pos += auth_len * 4; | 
| +        if (pos + 4 >= ret) { | 
| +            av_log(s, AV_LOG_WARNING, "Received too short packet\n"); | 
| +            continue; | 
| +        } | 
| +#define MIME "application/sdp" | 
| +        if (strcmp(&recvbuf[pos], MIME) == 0) { | 
| +            pos += strlen(MIME) + 1; | 
| +        } else if (strncmp(&recvbuf[pos], "v=0\r\n", 5) == 0) { | 
| +            // Direct SDP without a mime type | 
| +        } else { | 
| +            av_log(s, AV_LOG_WARNING, "Unsupported mime type %s\n", | 
| +                                      &recvbuf[pos]); | 
| +            continue; | 
| +        } | 
| + | 
| +        sap->sdp = av_strdup(&recvbuf[pos]); | 
| +        break; | 
| +    } | 
| + | 
| +    av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", sap->sdp); | 
| +    init_put_byte(&sap->sdp_pb, sap->sdp, strlen(sap->sdp), 0, NULL, NULL, | 
| +                  NULL, NULL); | 
| + | 
| +    infmt = av_find_input_format("sdp"); | 
| +    if (!infmt) | 
| +        goto fail; | 
| +    sap->sdp_ctx = avformat_alloc_context(); | 
| +    if (!sap->sdp_ctx) { | 
| +        ret = AVERROR(ENOMEM); | 
| +        goto fail; | 
| +    } | 
| +    sap->sdp_ctx->max_delay = s->max_delay; | 
| +    ap->prealloced_context = 1; | 
| +    ret = av_open_input_stream(&sap->sdp_ctx, &sap->sdp_pb, "temp.sdp", | 
| +                               infmt, ap); | 
| +    if (ret < 0) | 
| +        goto fail; | 
| +    if (sap->sdp_ctx->ctx_flags & AVFMTCTX_NOHEADER) | 
| +        s->ctx_flags |= AVFMTCTX_NOHEADER; | 
| +    for (i = 0; i < sap->sdp_ctx->nb_streams; i++) { | 
| +        AVStream *st = av_new_stream(s, i); | 
| +        if (!st) { | 
| +            ret = AVERROR(ENOMEM); | 
| +            goto fail; | 
| +        } | 
| +        avcodec_copy_context(st->codec, sap->sdp_ctx->streams[i]->codec); | 
| +        st->time_base = sap->sdp_ctx->streams[i]->time_base; | 
| +    } | 
| + | 
| +    return 0; | 
| + | 
| +fail: | 
| +    sap_read_close(s); | 
| +    return ret; | 
| +} | 
| + | 
| +static int sap_fetch_packet(AVFormatContext *s, AVPacket *pkt) | 
| +{ | 
| +    struct SAPState *sap = s->priv_data; | 
| +    int fd = url_get_file_handle(sap->ann_fd); | 
| +    int n, ret; | 
| +    fd_set rfds; | 
| +    struct timeval tv; | 
| +    uint8_t recvbuf[1500]; | 
| + | 
| +    if (sap->eof) | 
| +        return AVERROR_EOF; | 
| + | 
| +    while (1) { | 
| +        FD_ZERO(&rfds); | 
| +        FD_SET(fd, &rfds); | 
| +        tv.tv_sec = tv.tv_usec = 0; | 
| +        n = select(fd + 1, &rfds, NULL, NULL, &tv); | 
| +        if (n <= 0 || !FD_ISSET(fd, &rfds)) | 
| +            break; | 
| +        ret = url_read(sap->ann_fd, recvbuf, sizeof(recvbuf)); | 
| +        if (ret >= 8) { | 
| +            uint16_t hash = AV_RB16(&recvbuf[2]); | 
| +            /* Should ideally check the source IP address, too */ | 
| +            if (recvbuf[0] & 0x04 && hash == sap->hash) { | 
| +                /* Stream deletion */ | 
| +                sap->eof = 1; | 
| +                return AVERROR_EOF; | 
| +            } | 
| +        } | 
| +    } | 
| +    ret = av_read_frame(sap->sdp_ctx, pkt); | 
| +    if (ret < 0) | 
| +        return ret; | 
| +    if (s->ctx_flags & AVFMTCTX_NOHEADER) { | 
| +        while (sap->sdp_ctx->nb_streams > s->nb_streams) { | 
| +            int i = s->nb_streams; | 
| +            AVStream *st = av_new_stream(s, i); | 
| +            if (!st) { | 
| +                av_free_packet(pkt); | 
| +                return AVERROR(ENOMEM); | 
| +            } | 
| +            avcodec_copy_context(st->codec, sap->sdp_ctx->streams[i]->codec); | 
| +            st->time_base = sap->sdp_ctx->streams[i]->time_base; | 
| +        } | 
| +    } | 
| +    return ret; | 
| +} | 
| + | 
| +AVInputFormat sap_demuxer = { | 
| +    "sap", | 
| +    NULL_IF_CONFIG_SMALL("SAP input format"), | 
| +    sizeof(struct SAPState), | 
| +    sap_probe, | 
| +    sap_read_header, | 
| +    sap_fetch_packet, | 
| +    sap_read_close, | 
| +    .flags = AVFMT_NOFILE, | 
| +}; | 
| + | 
|  |