1/*
2 * RTMP network protocol
3 * Copyright (c) 2010 Howard Chu
4 *
5 * This file is part of Libav.
6 *
7 * Libav 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 * Libav 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 Libav; 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/mathematics.h"
28#include "avformat.h"
29#include "url.h"
30
31#include <librtmp/rtmp.h>
32#include <librtmp/log.h>
33
34static void rtmp_log(int level, const char *fmt, va_list args)
35{
36    switch (level) {
37    default:
38    case RTMP_LOGCRIT:    level = AV_LOG_FATAL;   break;
39    case RTMP_LOGERROR:   level = AV_LOG_ERROR;   break;
40    case RTMP_LOGWARNING: level = AV_LOG_WARNING; break;
41    case RTMP_LOGINFO:    level = AV_LOG_INFO;    break;
42    case RTMP_LOGDEBUG:   level = AV_LOG_VERBOSE; break;
43    case RTMP_LOGDEBUG2:  level = AV_LOG_DEBUG;   break;
44    }
45
46    av_vlog(NULL, level, fmt, args);
47    av_log(NULL, level, "\n");
48}
49
50static int rtmp_close(URLContext *s)
51{
52    RTMP *r = s->priv_data;
53
54    RTMP_Close(r);
55    return 0;
56}
57
58/**
59 * Open RTMP connection and verify that the stream can be played.
60 *
61 * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]...
62 *             where 'app' is first one or two directories in the path
63 *             (e.g. /ondemand/, /flash/live/, etc.)
64 *             and 'playpath' is a file name (the rest of the path,
65 *             may be prefixed with "mp4:")
66 *
67 *             Additional RTMP library options may be appended as
68 *             space-separated key-value pairs.
69 */
70static int rtmp_open(URLContext *s, const char *uri, int flags)
71{
72    RTMP *r = s->priv_data;
73    int rc;
74
75    switch (av_log_get_level()) {
76    default:
77    case AV_LOG_FATAL:   rc = RTMP_LOGCRIT;    break;
78    case AV_LOG_ERROR:   rc = RTMP_LOGERROR;   break;
79    case AV_LOG_WARNING: rc = RTMP_LOGWARNING; break;
80    case AV_LOG_INFO:    rc = RTMP_LOGINFO;    break;
81    case AV_LOG_VERBOSE: rc = RTMP_LOGDEBUG;   break;
82    case AV_LOG_DEBUG:   rc = RTMP_LOGDEBUG2;  break;
83    }
84    RTMP_LogSetLevel(rc);
85    RTMP_LogSetCallback(rtmp_log);
86
87    RTMP_Init(r);
88    if (!RTMP_SetupURL(r, s->filename)) {
89        rc = -1;
90        goto fail;
91    }
92
93    if (flags & AVIO_FLAG_WRITE)
94        RTMP_EnableWrite(r);
95
96    if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) {
97        rc = -1;
98        goto fail;
99    }
100
101    s->is_streamed = 1;
102    return 0;
103fail:
104    return rc;
105}
106
107static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
108{
109    RTMP *r = s->priv_data;
110
111    return RTMP_Write(r, buf, size);
112}
113
114static int rtmp_read(URLContext *s, uint8_t *buf, int size)
115{
116    RTMP *r = s->priv_data;
117
118    return RTMP_Read(r, buf, size);
119}
120
121static int rtmp_read_pause(URLContext *s, int pause)
122{
123    RTMP *r = s->priv_data;
124
125    if (!RTMP_Pause(r, pause))
126        return -1;
127    return 0;
128}
129
130static int64_t rtmp_read_seek(URLContext *s, int stream_index,
131                              int64_t timestamp, int flags)
132{
133    RTMP *r = s->priv_data;
134
135    if (flags & AVSEEK_FLAG_BYTE)
136        return AVERROR(ENOSYS);
137
138    /* seeks are in milliseconds */
139    if (stream_index < 0)
140        timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE,
141            flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP);
142
143    if (!RTMP_SendSeek(r, timestamp))
144        return -1;
145    return timestamp;
146}
147
148static int rtmp_get_file_handle(URLContext *s)
149{
150    RTMP *r = s->priv_data;
151
152    return RTMP_Socket(r);
153}
154
155URLProtocol ff_rtmp_protocol = {
156    .name                = "rtmp",
157    .url_open            = rtmp_open,
158    .url_read            = rtmp_read,
159    .url_write           = rtmp_write,
160    .url_close           = rtmp_close,
161    .url_read_pause      = rtmp_read_pause,
162    .url_read_seek       = rtmp_read_seek,
163    .url_get_file_handle = rtmp_get_file_handle,
164    .priv_data_size      = sizeof(RTMP),
165    .flags               = URL_PROTOCOL_FLAG_NETWORK,
166};
167
168URLProtocol ff_rtmpt_protocol = {
169    .name                = "rtmpt",
170    .url_open            = rtmp_open,
171    .url_read            = rtmp_read,
172    .url_write           = rtmp_write,
173    .url_close           = rtmp_close,
174    .url_read_pause      = rtmp_read_pause,
175    .url_read_seek       = rtmp_read_seek,
176    .url_get_file_handle = rtmp_get_file_handle,
177    .priv_data_size      = sizeof(RTMP),
178    .flags               = URL_PROTOCOL_FLAG_NETWORK,
179};
180
181URLProtocol ff_rtmpe_protocol = {
182    .name                = "rtmpe",
183    .url_open            = rtmp_open,
184    .url_read            = rtmp_read,
185    .url_write           = rtmp_write,
186    .url_close           = rtmp_close,
187    .url_read_pause      = rtmp_read_pause,
188    .url_read_seek       = rtmp_read_seek,
189    .url_get_file_handle = rtmp_get_file_handle,
190    .priv_data_size      = sizeof(RTMP),
191    .flags               = URL_PROTOCOL_FLAG_NETWORK,
192};
193
194URLProtocol ff_rtmpte_protocol = {
195    .name                = "rtmpte",
196    .url_open            = rtmp_open,
197    .url_read            = rtmp_read,
198    .url_write           = rtmp_write,
199    .url_close           = rtmp_close,
200    .url_read_pause      = rtmp_read_pause,
201    .url_read_seek       = rtmp_read_seek,
202    .url_get_file_handle = rtmp_get_file_handle,
203    .priv_data_size      = sizeof(RTMP),
204    .flags               = URL_PROTOCOL_FLAG_NETWORK,
205};
206
207URLProtocol ff_rtmps_protocol = {
208    .name                = "rtmps",
209    .url_open            = rtmp_open,
210    .url_read            = rtmp_read,
211    .url_write           = rtmp_write,
212    .url_close           = rtmp_close,
213    .url_read_pause      = rtmp_read_pause,
214    .url_read_seek       = rtmp_read_seek,
215    .url_get_file_handle = rtmp_get_file_handle,
216    .priv_data_size      = sizeof(RTMP),
217    .flags               = URL_PROTOCOL_FLAG_NETWORK,
218};
219