Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * RTMP network protocol
  3.  * Copyright (c) 2010 Howard Chu
  4.  *
  5.  * This file is part of FFmpeg.
  6.  *
  7.  * FFmpeg is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * FFmpeg is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with FFmpeg; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20.  */
  21.  
  22. /**
  23.  * @file
  24.  * RTMP protocol based on http://rtmpdump.mplayerhq.hu/ librtmp
  25.  */
  26.  
  27. #include "libavutil/avstring.h"
  28. #include "libavutil/mathematics.h"
  29. #include "libavutil/opt.h"
  30. #include "avformat.h"
  31. #if CONFIG_NETWORK
  32. #include "network.h"
  33. #endif
  34. #include "url.h"
  35.  
  36. #include <librtmp/rtmp.h>
  37. #include <librtmp/log.h>
  38.  
  39. typedef struct LibRTMPContext {
  40.     const AVClass *class;
  41.     RTMP rtmp;
  42.     char *app;
  43.     char *conn;
  44.     char *subscribe;
  45.     char *playpath;
  46.     char *tcurl;
  47.     char *flashver;
  48.     char *swfurl;
  49.     char *swfverify;
  50.     char *pageurl;
  51.     char *client_buffer_time;
  52.     int live;
  53.     char *temp_filename;
  54.     int buffer_size;
  55. } LibRTMPContext;
  56.  
  57. static void rtmp_log(int level, const char *fmt, va_list args)
  58. {
  59.     switch (level) {
  60.     default:
  61.     case RTMP_LOGCRIT:    level = AV_LOG_FATAL;   break;
  62.     case RTMP_LOGERROR:   level = AV_LOG_ERROR;   break;
  63.     case RTMP_LOGWARNING: level = AV_LOG_WARNING; break;
  64.     case RTMP_LOGINFO:    level = AV_LOG_INFO;    break;
  65.     case RTMP_LOGDEBUG:   level = AV_LOG_VERBOSE; break;
  66.     case RTMP_LOGDEBUG2:  level = AV_LOG_DEBUG;   break;
  67.     }
  68.  
  69.     av_vlog(NULL, level, fmt, args);
  70.     av_log(NULL, level, "\n");
  71. }
  72.  
  73. static int rtmp_close(URLContext *s)
  74. {
  75.     LibRTMPContext *ctx = s->priv_data;
  76.     RTMP *r = &ctx->rtmp;
  77.  
  78.     RTMP_Close(r);
  79.     av_freep(&ctx->temp_filename);
  80.     return 0;
  81. }
  82.  
  83. /**
  84.  * Open RTMP connection and verify that the stream can be played.
  85.  *
  86.  * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]...
  87.  *             where 'app' is first one or two directories in the path
  88.  *             (e.g. /ondemand/, /flash/live/, etc.)
  89.  *             and 'playpath' is a file name (the rest of the path,
  90.  *             may be prefixed with "mp4:")
  91.  *
  92.  *             Additional RTMP library options may be appended as
  93.  *             space-separated key-value pairs.
  94.  */
  95. static int rtmp_open(URLContext *s, const char *uri, int flags)
  96. {
  97.     LibRTMPContext *ctx = s->priv_data;
  98.     RTMP *r = &ctx->rtmp;
  99.     int rc = 0, level;
  100.     char *filename = s->filename;
  101.     int len = strlen(s->filename) + 1;
  102.  
  103.     switch (av_log_get_level()) {
  104.     default:
  105.     case AV_LOG_FATAL:   level = RTMP_LOGCRIT;    break;
  106.     case AV_LOG_ERROR:   level = RTMP_LOGERROR;   break;
  107.     case AV_LOG_WARNING: level = RTMP_LOGWARNING; break;
  108.     case AV_LOG_INFO:    level = RTMP_LOGINFO;    break;
  109.     case AV_LOG_VERBOSE: level = RTMP_LOGDEBUG;   break;
  110.     case AV_LOG_DEBUG:   level = RTMP_LOGDEBUG2;  break;
  111.     }
  112.     RTMP_LogSetLevel(level);
  113.     RTMP_LogSetCallback(rtmp_log);
  114.  
  115.     if (ctx->app)      len += strlen(ctx->app)      + sizeof(" app=");
  116.     if (ctx->tcurl)    len += strlen(ctx->tcurl)    + sizeof(" tcUrl=");
  117.     if (ctx->pageurl)  len += strlen(ctx->pageurl)  + sizeof(" pageUrl=");
  118.     if (ctx->flashver) len += strlen(ctx->flashver) + sizeof(" flashver=");
  119.  
  120.     if (ctx->conn) {
  121.         char *sep, *p = ctx->conn;
  122.         int options = 0;
  123.  
  124.         while (p) {
  125.             options++;
  126.             p += strspn(p, " ");
  127.             if (!*p)
  128.                 break;
  129.             sep = strchr(p, ' ');
  130.             if (sep)
  131.                 p = sep + 1;
  132.             else
  133.                 break;
  134.         }
  135.         len += options * sizeof(" conn=");
  136.         len += strlen(ctx->conn);
  137.     }
  138.  
  139.     if (ctx->playpath)
  140.         len += strlen(ctx->playpath) + sizeof(" playpath=");
  141.     if (ctx->live)
  142.         len += sizeof(" live=1");
  143.     if (ctx->subscribe)
  144.         len += strlen(ctx->subscribe) + sizeof(" subscribe=");
  145.  
  146.     if (ctx->client_buffer_time)
  147.         len += strlen(ctx->client_buffer_time) + sizeof(" buffer=");
  148.  
  149.     if (ctx->swfurl || ctx->swfverify) {
  150.         len += sizeof(" swfUrl=");
  151.  
  152.         if (ctx->swfverify)
  153.             len += strlen(ctx->swfverify) + sizeof(" swfVfy=1");
  154.         else
  155.             len += strlen(ctx->swfurl);
  156.     }
  157.  
  158.     if (!(ctx->temp_filename = filename = av_malloc(len)))
  159.         return AVERROR(ENOMEM);
  160.  
  161.     av_strlcpy(filename, s->filename, len);
  162.     if (ctx->app) {
  163.         av_strlcat(filename, " app=", len);
  164.         av_strlcat(filename, ctx->app, len);
  165.     }
  166.     if (ctx->tcurl) {
  167.         av_strlcat(filename, " tcUrl=", len);
  168.         av_strlcat(filename, ctx->tcurl, len);
  169.     }
  170.     if (ctx->pageurl) {
  171.         av_strlcat(filename, " pageUrl=", len);
  172.         av_strlcat(filename, ctx->pageurl, len);
  173.     }
  174.     if (ctx->swfurl) {
  175.         av_strlcat(filename, " swfUrl=", len);
  176.         av_strlcat(filename, ctx->swfurl, len);
  177.     }
  178.     if (ctx->flashver) {
  179.         av_strlcat(filename, " flashVer=", len);
  180.         av_strlcat(filename, ctx->flashver, len);
  181.     }
  182.     if (ctx->conn) {
  183.         char *sep, *p = ctx->conn;
  184.         while (p) {
  185.             av_strlcat(filename, " conn=", len);
  186.             p += strspn(p, " ");
  187.             if (!*p)
  188.                 break;
  189.             sep = strchr(p, ' ');
  190.             if (sep)
  191.                 *sep = '\0';
  192.             av_strlcat(filename, p, len);
  193.  
  194.             if (sep)
  195.                 p = sep + 1;
  196.         }
  197.     }
  198.     if (ctx->playpath) {
  199.         av_strlcat(filename, " playpath=", len);
  200.         av_strlcat(filename, ctx->playpath, len);
  201.     }
  202.     if (ctx->live)
  203.         av_strlcat(filename, " live=1", len);
  204.     if (ctx->subscribe) {
  205.         av_strlcat(filename, " subscribe=", len);
  206.         av_strlcat(filename, ctx->subscribe, len);
  207.     }
  208.     if (ctx->client_buffer_time) {
  209.         av_strlcat(filename, " buffer=", len);
  210.         av_strlcat(filename, ctx->client_buffer_time, len);
  211.     }
  212.     if (ctx->swfurl || ctx->swfverify) {
  213.         av_strlcat(filename, " swfUrl=", len);
  214.  
  215.         if (ctx->swfverify) {
  216.             av_strlcat(filename, ctx->swfverify, len);
  217.             av_strlcat(filename, " swfVfy=1", len);
  218.         } else {
  219.             av_strlcat(filename, ctx->swfurl, len);
  220.         }
  221.     }
  222.  
  223.     RTMP_Init(r);
  224.     if (!RTMP_SetupURL(r, filename)) {
  225.         rc = AVERROR_UNKNOWN;
  226.         goto fail;
  227.     }
  228.  
  229.     if (flags & AVIO_FLAG_WRITE)
  230.         RTMP_EnableWrite(r);
  231.  
  232.     if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) {
  233.         rc = AVERROR_UNKNOWN;
  234.         goto fail;
  235.     }
  236.  
  237. #if CONFIG_NETWORK
  238.     if (ctx->buffer_size >= 0 && (flags & AVIO_FLAG_WRITE)) {
  239.         int tmp = ctx->buffer_size;
  240.         setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp));
  241.     }
  242. #endif
  243.  
  244.     s->is_streamed = 1;
  245.     return 0;
  246. fail:
  247.     av_freep(&ctx->temp_filename);
  248.     if (rc)
  249.         RTMP_Close(r);
  250.  
  251.     return rc;
  252. }
  253.  
  254. static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
  255. {
  256.     LibRTMPContext *ctx = s->priv_data;
  257.     RTMP *r = &ctx->rtmp;
  258.  
  259.     return RTMP_Write(r, buf, size);
  260. }
  261.  
  262. static int rtmp_read(URLContext *s, uint8_t *buf, int size)
  263. {
  264.     LibRTMPContext *ctx = s->priv_data;
  265.     RTMP *r = &ctx->rtmp;
  266.  
  267.     return RTMP_Read(r, buf, size);
  268. }
  269.  
  270. static int rtmp_read_pause(URLContext *s, int pause)
  271. {
  272.     LibRTMPContext *ctx = s->priv_data;
  273.     RTMP *r = &ctx->rtmp;
  274.  
  275.     if (!RTMP_Pause(r, pause))
  276.         return AVERROR_UNKNOWN;
  277.     return 0;
  278. }
  279.  
  280. static int64_t rtmp_read_seek(URLContext *s, int stream_index,
  281.                               int64_t timestamp, int flags)
  282. {
  283.     LibRTMPContext *ctx = s->priv_data;
  284.     RTMP *r = &ctx->rtmp;
  285.  
  286.     if (flags & AVSEEK_FLAG_BYTE)
  287.         return AVERROR(ENOSYS);
  288.  
  289.     /* seeks are in milliseconds */
  290.     if (stream_index < 0)
  291.         timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE,
  292.             flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP);
  293.  
  294.     if (!RTMP_SendSeek(r, timestamp))
  295.         return AVERROR_UNKNOWN;
  296.     return timestamp;
  297. }
  298.  
  299. static int rtmp_get_file_handle(URLContext *s)
  300. {
  301.     LibRTMPContext *ctx = s->priv_data;
  302.     RTMP *r = &ctx->rtmp;
  303.  
  304.     return RTMP_Socket(r);
  305. }
  306.  
  307. #define OFFSET(x) offsetof(LibRTMPContext, x)
  308. #define DEC AV_OPT_FLAG_DECODING_PARAM
  309. #define ENC AV_OPT_FLAG_ENCODING_PARAM
  310. static const AVOption options[] = {
  311.     {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
  312.     {"rtmp_buffer", "Set buffer time in milliseconds. The default is 3000.", OFFSET(client_buffer_time), AV_OPT_TYPE_STRING, {.str = "3000"}, 0, 0, DEC|ENC},
  313.     {"rtmp_conn", "Append arbitrary AMF data to the Connect message", OFFSET(conn), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
  314.     {"rtmp_flashver", "Version of the Flash plugin used to run the SWF player.", OFFSET(flashver), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
  315.     {"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtmp_live"},
  316.     {"any", "both", 0, AV_OPT_TYPE_CONST, {.i64 = -2}, 0, 0, DEC, "rtmp_live"},
  317.     {"live", "live stream", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, DEC, "rtmp_live"},
  318.     {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, DEC, "rtmp_live"},
  319.     {"rtmp_pageurl", "URL of the web page in which the media was embedded. By default no value will be sent.", OFFSET(pageurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
  320.     {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
  321.     {"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
  322.     {"rtmp_swfurl", "URL of the SWF player. By default no value will be sent", OFFSET(swfurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
  323.     {"rtmp_swfverify", "URL to player swf file, compute hash/size automatically. (unimplemented)", OFFSET(swfverify), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
  324.     {"rtmp_tcurl", "URL of the target stream. Defaults to proto://host[:port]/app.", OFFSET(tcurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
  325. #if CONFIG_NETWORK
  326.     {"rtmp_buffer_size", "set buffer size in bytes", OFFSET(buffer_size), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, DEC|ENC },
  327. #endif
  328.     { NULL },
  329. };
  330.  
  331. #define RTMP_CLASS(flavor)\
  332. static const AVClass lib ## flavor ## _class = {\
  333.     .class_name = "lib" #flavor " protocol",\
  334.     .item_name  = av_default_item_name,\
  335.     .option     = options,\
  336.     .version    = LIBAVUTIL_VERSION_INT,\
  337. };
  338.  
  339. RTMP_CLASS(rtmp)
  340. URLProtocol ff_librtmp_protocol = {
  341.     .name                = "rtmp",
  342.     .url_open            = rtmp_open,
  343.     .url_read            = rtmp_read,
  344.     .url_write           = rtmp_write,
  345.     .url_close           = rtmp_close,
  346.     .url_read_pause      = rtmp_read_pause,
  347.     .url_read_seek       = rtmp_read_seek,
  348.     .url_get_file_handle = rtmp_get_file_handle,
  349.     .priv_data_size      = sizeof(LibRTMPContext),
  350.     .priv_data_class     = &librtmp_class,
  351.     .flags               = URL_PROTOCOL_FLAG_NETWORK,
  352. };
  353.  
  354. RTMP_CLASS(rtmpt)
  355. URLProtocol ff_librtmpt_protocol = {
  356.     .name                = "rtmpt",
  357.     .url_open            = rtmp_open,
  358.     .url_read            = rtmp_read,
  359.     .url_write           = rtmp_write,
  360.     .url_close           = rtmp_close,
  361.     .url_read_pause      = rtmp_read_pause,
  362.     .url_read_seek       = rtmp_read_seek,
  363.     .url_get_file_handle = rtmp_get_file_handle,
  364.     .priv_data_size      = sizeof(LibRTMPContext),
  365.     .priv_data_class     = &librtmpt_class,
  366.     .flags               = URL_PROTOCOL_FLAG_NETWORK,
  367. };
  368.  
  369. RTMP_CLASS(rtmpe)
  370. URLProtocol ff_librtmpe_protocol = {
  371.     .name                = "rtmpe",
  372.     .url_open            = rtmp_open,
  373.     .url_read            = rtmp_read,
  374.     .url_write           = rtmp_write,
  375.     .url_close           = rtmp_close,
  376.     .url_read_pause      = rtmp_read_pause,
  377.     .url_read_seek       = rtmp_read_seek,
  378.     .url_get_file_handle = rtmp_get_file_handle,
  379.     .priv_data_size      = sizeof(LibRTMPContext),
  380.     .priv_data_class     = &librtmpe_class,
  381.     .flags               = URL_PROTOCOL_FLAG_NETWORK,
  382. };
  383.  
  384. RTMP_CLASS(rtmpte)
  385. URLProtocol ff_librtmpte_protocol = {
  386.     .name                = "rtmpte",
  387.     .url_open            = rtmp_open,
  388.     .url_read            = rtmp_read,
  389.     .url_write           = rtmp_write,
  390.     .url_close           = rtmp_close,
  391.     .url_read_pause      = rtmp_read_pause,
  392.     .url_read_seek       = rtmp_read_seek,
  393.     .url_get_file_handle = rtmp_get_file_handle,
  394.     .priv_data_size      = sizeof(LibRTMPContext),
  395.     .priv_data_class     = &librtmpte_class,
  396.     .flags               = URL_PROTOCOL_FLAG_NETWORK,
  397. };
  398.  
  399. RTMP_CLASS(rtmps)
  400. URLProtocol ff_librtmps_protocol = {
  401.     .name                = "rtmps",
  402.     .url_open            = rtmp_open,
  403.     .url_read            = rtmp_read,
  404.     .url_write           = rtmp_write,
  405.     .url_close           = rtmp_close,
  406.     .url_read_pause      = rtmp_read_pause,
  407.     .url_read_seek       = rtmp_read_seek,
  408.     .url_get_file_handle = rtmp_get_file_handle,
  409.     .priv_data_size      = sizeof(LibRTMPContext),
  410.     .priv_data_class     = &librtmps_class,
  411.     .flags               = URL_PROTOCOL_FLAG_NETWORK,
  412. };
  413.