[FFmpeg-soc] [soc]: r5825 - mms/mmsh.c

spyfeng subversion at mplayerhq.hu
Sun Jun 6 19:17:38 CEST 2010


Author: spyfeng
Date: Sun Jun  6 19:17:38 2010
New Revision: 5825

Log:
add primary version for mmsh.c which can't pass the compile.
TODO:
1, parse the asf header.
2, finish mmsh_read().
3, compile and fix bugs.
4, extract common codes with mmst.c

Added:
   mms/mmsh.c

Added: mms/mmsh.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ mms/mmsh.c	Sun Jun  6 19:17:38 2010	(r5825)
@@ -0,0 +1,367 @@
+/*
+ * MMS protocol over HTTP
+ * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com>
+ *
+ * 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 <string.h>
+
+#define CHUNK_TYPE_DATA        0x4424
+#define CHUNK_TYPE_ASF_HEADER  0x4824
+
+#define USERAGENT "User-Agent: NSPlayer/4.1.0.3856\r\n"
+#define CLIENTGUID "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n"
+
+static const char* mmsh_first_request =
+    "GET %s HTTP/1.0\r\n"
+    "Accept: */*\r\n"
+    USERAGENT
+    "Host: %s:%d\r\n"
+    "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%u,max-duration=0\r\n"
+    CLIENTGUID
+    "Connection: Close\r\n\r\n";
+
+static const char* mmsh_seekable_request =
+    "GET %s HTTP/1.0\r\n"
+    "Accept: */*\r\n"
+    USERAGENT
+    "Host: %s:%d\r\n"
+    "Pragma: no-cache,rate=1.000000,stream-time=%u,stream-offset=%u:%u,request-context=%u,max-duration=%u\r\n"
+    CLIENTGUID
+    "Pragma: xPlayStrm=1\r\n"
+    "Pragma: stream-switch-count=%d\r\n"
+    "Pragma: stream-switch-entry=%s\r\n" /*  ffff:1:0 ffff:2:0 */
+    "Connection: Close\r\n\r\n";
+
+static const char* mmsh_live_request =
+    "GET %s HTTP/1.0\r\n"
+    "Accept: */*\r\n"
+    USERAGENT
+    "Host: %s:%d\r\n"
+    "Pragma: no-cache,rate=1.000000,request-context=%u\r\n"
+    "Pragma: xPlayStrm=1\r\n"
+    CLIENTGUID
+    "Pragma: stream-switch-count=%d\r\n"
+    "Pragma: stream-switch-entry=%s\r\n"
+    "Connection: Close\r\n\r\n";
+
+typedef struct
+{
+    URLContext *mms_hd;
+    uint8_t out_buffer[1024];             ///< Buffer for outgoing packet.
+    uint8_t in_buffer[1024]; //TODO, maybe reused by out_buffer.
+    uint8_t *asf_header_pos;
+    uint8_t *http_header_data;
+    int content_length;
+    int asf_header_len;
+    int asf_header_remaining_len;
+    int asf_data_remaining_len;
+
+    char path[256];
+    char host[128];
+    int seekable;
+    int stream_num;
+
+    char stream_selection[10 * MAX_STREAMS];
+}MMSHContext;
+
+static int mmsh_close(URLContext *h)
+{
+    MMSHContext *mms = (MMSHContext *)h->priv_data;
+    if(mms->mms_hd)
+        url_close(mms->mms_hd);
+    av_freep(&h->priv_data);
+    av_freep(&mms->http_header_data);
+    //TODO free other alloced mem.
+    return 0;
+}
+
+static int send_pack(MMSHContext *mms)
+{
+    int len, result;
+    len = strlen(mms->out_buffer);
+    result = url_write(mms->mms_hd, mms->out_buffer, len)
+    if(result != len) {
+        dprintf(NULL,"send pack failed!return len %d != %d\n", result, len);
+        return AVERROR_IO;
+    }
+    return 0;
+}
+
+static char* find_str(char * dst, const char *str, const int len)
+{
+    char *p = NULL;
+    if(strncasecmp(dst, str, len) == 0) {
+        p=dst[len];
+        while(isspace(*p))
+            p++;
+    }
+    return p;
+}
+
+static int get_and_parse_http_header(MMSHContext *mms)
+{
+    int len = 0, line_num = 0;
+    int http_code;
+    char content_type[128]={'\0'};
+    char *p, *pos;
+    for(;;) {
+        if(url_read(mms->mms_hd, mms->in_buffer[len], 1) != 1) {
+            dprintf(NULL, "recv http header failed!\n");
+            return AVERROR(EIO);
+        }
+
+        if(mms->in_buffer[len] != 0x0A) {
+            len++;
+            if(len >= sizeof(mms->in_buffer)) {
+                dprintf(NULL, "recv http header overwrite the buffer!\n");
+                return -1;
+            }
+        } else {
+            mms->in_buffer[len--] = '\0';
+            if (len >= 0 && mms->in_buffer[len] == 0x0D) {
+                line_num++;
+                mms->in_buffer[len--] = '\0';
+                if(len < 0) {
+                    return 0; // \r\n\r\n is the end of http header
+                } else {
+                    len = 0;// begin to read next http header line
+                }
+            }
+            if (line_num == 1) {
+                p = mms->in_buffer;
+                while(!isspace(*p) && *p != '\0')
+                    p++;
+                while(isspace(*p))
+                    p++;
+                http_code = strtol(p, NULL, 10);
+                dprintf(NULL, "mmsh protocol http_code=%d\n", http_code);
+                if(http_code != 200) {
+                    return -1;
+                }
+            } else {
+                if ((p = find_str(mms->in_buffer, "Content-Type:", 13)) != NULL) {
+                    strncpy(content_type, p, sizeof(content_type));
+                    dprintf(NULL, "Content-Type:%s\n", content_type);
+                    if(strcmp(content_type, "application/x-mms-framed") != 0
+                        && strcmp(content_type, "application/octet-stream") != 0
+                        && strcmp(content_type, "application/vnd.ms.wms-hdr.asfv1") != 0) {
+                        return -1;
+                    }
+                } else if((p = find_str(mms->in_buffer, "Content-Length:", 15)) != NULL) {
+                    mms->content_length = atoi(*p);
+                } else if(p = find_str(mms->in_buffer, "Pragma:", 7) != NULL) {
+                    pos = strstr(p, "features=");
+                    if (pos){
+                        if(strstr(pos, "seekable")) {
+                            mms->seekable = 1;
+                        } else if (strstr(pos, "broadcast")) {
+                            mms->seekable = 0;
+                        }
+                    } else {
+                        dprintf(NULL, "Can't find features!\n");
+                    }
+                }
+            }
+        }
+    }
+}
+
+static uint16_t http_header_data_parser(MMSHContext *mms)
+{
+    uint16_t chunk_type;
+    int chunk_len;
+    int data_len = mms->content_length;
+    char *pos = mms->http_header_data;
+
+    while(data_len) {
+        chunk_type = AV_RL16(pos);
+        chunk_len = AV_RL16(pos + 2);
+        if(chunk_type == CHUNK_TYPE_ASF_HEADER) {
+            mms->asf_header_pos = pos + 4; // start from $H, ox4824
+            mms->asf_header_len = chunk_len;
+        }
+        data_len -= chunk_len + 4;
+        pos += chunk_len + 4;
+        if (data_len <= 0) {
+            dprintf(NULL, "http header data len is %d for type %x\n", chunk_len, chunk_type);
+            return -1;
+        }
+        if (chunk_type == CHUNK_TYPE_ASF_HEADER) {
+            //TODO parse asf header
+        }
+    }
+    return 0;
+}
+
+static int get_http_header_data(MMSHContext *mms, const int flag)
+{
+    int res, len;
+    if(mms->content_length && flag == 1) {
+        mms->http_header_data = av_mallocz(mms->content_length);
+        if (!mms->http_header_data)
+            return AVERROR(ENOMEM);
+        // read the http header data, may contain $H, $M, $P packet.
+        // In this situation, it should only has $H packet, ie asf header data.
+        res = url_read_complete(mms->mms_hd, mms->http_header_data, mms->content_length);
+        if (res != mms->content_length) {
+            dprintf(NULL, "recv header data len %d != %d", res, mms->content_length);
+            return -1;
+        }
+    } else  if(flag == 2){
+        // skip asf header
+        uint16_t type;
+        char *tmp = av_mallocz(mms->asf_header_len);
+        if (!tmp)
+            return AVERROR(ENOMEM);
+        res = url_read_complete(mms->mms_hd, tmp, mms->asf_header_len);
+        if (res != mms->asf_header_len) {
+           dprintf(NULL, "read skipped asf header failed!\n");
+           av_free(tmp);
+           return -1;
+        }
+        type = AV_RL16(tmp);
+        if (type != CHUNK_TYPE_ASF_HEADER) {
+            dprintf(NULL, "cann't skip asf header because we didn't recv it!\n");
+            av_free(tmp);
+            return -1;
+        }
+        av_free(tmp);
+    } else {
+        dprintf(NULL, "http response has no data!\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int get_http_answer(MMSHContext *mms, const int flag)
+{
+    int result;
+    result = get_and_parse_http_header(mms);
+    if (result) {
+        dprintf(NULL, "http header parser failed!\n");
+        return result;
+    }
+
+    result = get_http_header_data(mms, flag);
+    if (result) {
+        dprintf(NULL, "get http header data fialed!\n");
+        return result;
+    }
+    if (flag == 1) {
+        result = http_header_data_parser(mms);
+        if (result) {
+            dprintf(NULL, "http header data parser failed!\n");
+            return result;
+        }
+    }
+    return 0;
+}
+
+static int mmsh_open(URLContext *h, const char *uri, int flags)
+{
+    MMSHContext *mms;
+    int port, err;
+    char tcpname[256];
+
+    h->is_streamed = 1;
+    mms = h->priv_data = av_mallocz(sizeof(MMSHContext));
+    if (!h->priv_data)
+        return AVERROR(ENOMEM);
+
+    ff_url_split(NULL, 0, NULL, 0,
+            mms->host, sizeof(mms->host), &port, mms->path,
+            sizeof(mms->path), uri);
+    if(port<0)
+        port = 80; // defaut mmsh protocol port
+
+    ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, mms->host, port, NULL);
+    err = url_open(&mms->mms_hd, tcpname, URL_RDWR);
+    if (err)
+        goto fail;
+    // send describe request
+    snprintf (mms->out_buffer, sizeof(mms->out_buffer), mmsh_first_request, mms->path,
+            mms->host, port, this->http_request_number++);
+    err = send_pack(mms);
+    if (err)
+        goto fail;
+    err = get_http_answer(mms, 1); // TODO match with the first request
+    if(err)
+        goto fail;
+
+    // send paly request
+    if (mms->seekable) {
+        snprintf(mms->out_buffer, sizeof(mms->out_buffer), mmsh_seekable_request, mms->path,
+            mms->host, port, 0, 0, 0, this->http_request_number++, 0, mms->stream_num, mms->stream_selection);
+    } else {
+        snprintf(mms->out_buffer, sizeof(mms->out_buffer),, mmsh_live_request, mms->path,
+            mms->host, port, this->http_request_number++, mms->stream_num, mms->stream_selection);
+    }
+    err = send_pack(mms);
+    if (err)
+        goto fail;
+    err = get_http_answer(mms, 2);// TODO mathc with the second request
+    if(err)
+        goto fail;
+    dprintf(NULL, "Leaving mmsh open success.\n");
+fail:
+    mmsh_close(h);
+    dprintf(NULL, "Leaving mmsh open (failure: %d)\n", err);
+    return err;
+}
+
+static int read_data(MMSHContext *mms, char *buf, int size)
+{
+}
+
+static int read_data_packet(MMSHContext *mms)
+{
+}
+
+static int mmsh_read(URLContext *h, uint8_t *buf, int size)
+{
+    int size_to_copy;
+    MMSHContext *mms = h->priv_data;
+
+    if (mms->asf_header_remaining_len != 0) {
+        // copy asf header into buffer
+        char *pos;
+        size_to_copy = FFMIN(size, mms->asf_header_remaining_len);
+        pos = mms->asf_header_pos + (mms->asf_header_len - mms->asf_header_remaining_len);
+        memcpy(buf, pos, size_to_copy);
+        mms->asf_header_remaining_len -= size_to_copy;
+        if (mms->asf_header_remaining_len == 0) {
+            av_freep(&mms->http_header_data); // which contains asf header
+        }
+    } else if (mms->asf_data_remaining_len){
+        read_data(mms, buf, size);
+    } else {
+        // read data packet from network
+        read_data_packet(mms);
+    }
+}
+
+URLProtocol mmsh_protocol = {
+    "mmsh, mms",
+    mmsh_open,
+    mmsh_read,
+    NULL, // write
+    NULL, // seek
+    mmsh_close,
+};
+


More information about the FFmpeg-soc mailing list