FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
librtmp.c
Go to the documentation of this file.
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 #include "url.h"
32 
33 #include <librtmp/rtmp.h>
34 #include <librtmp/log.h>
35 
36 typedef struct LibRTMPContext {
37  const AVClass *class;
38  RTMP rtmp;
39  char *app;
40  char *playpath;
42 
43 static void rtmp_log(int level, const char *fmt, va_list args)
44 {
45  switch (level) {
46  default:
47  case RTMP_LOGCRIT: level = AV_LOG_FATAL; break;
48  case RTMP_LOGERROR: level = AV_LOG_ERROR; break;
49  case RTMP_LOGWARNING: level = AV_LOG_WARNING; break;
50  case RTMP_LOGINFO: level = AV_LOG_INFO; break;
51  case RTMP_LOGDEBUG: level = AV_LOG_VERBOSE; break;
52  case RTMP_LOGDEBUG2: level = AV_LOG_DEBUG; break;
53  }
54 
55  av_vlog(NULL, level, fmt, args);
56  av_log(NULL, level, "\n");
57 }
58 
59 static int rtmp_close(URLContext *s)
60 {
61  LibRTMPContext *ctx = s->priv_data;
62  RTMP *r = &ctx->rtmp;
63 
64  RTMP_Close(r);
65  return 0;
66 }
67 
68 /**
69  * Open RTMP connection and verify that the stream can be played.
70  *
71  * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]...
72  * where 'app' is first one or two directories in the path
73  * (e.g. /ondemand/, /flash/live/, etc.)
74  * and 'playpath' is a file name (the rest of the path,
75  * may be prefixed with "mp4:")
76  *
77  * Additional RTMP library options may be appended as
78  * space-separated key-value pairs.
79  */
80 static int rtmp_open(URLContext *s, const char *uri, int flags)
81 {
82  LibRTMPContext *ctx = s->priv_data;
83  RTMP *r = &ctx->rtmp;
84  int rc = 0, level;
85  char *filename = s->filename;
86 
87  switch (av_log_get_level()) {
88  default:
89  case AV_LOG_FATAL: level = RTMP_LOGCRIT; break;
90  case AV_LOG_ERROR: level = RTMP_LOGERROR; break;
91  case AV_LOG_WARNING: level = RTMP_LOGWARNING; break;
92  case AV_LOG_INFO: level = RTMP_LOGINFO; break;
93  case AV_LOG_VERBOSE: level = RTMP_LOGDEBUG; break;
94  case AV_LOG_DEBUG: level = RTMP_LOGDEBUG2; break;
95  }
96  RTMP_LogSetLevel(level);
97  RTMP_LogSetCallback(rtmp_log);
98 
99  if (ctx->app || ctx->playpath) {
100  int len = strlen(s->filename) + 1;
101  if (ctx->app) len += strlen(ctx->app) + sizeof(" app=");
102  if (ctx->playpath) len += strlen(ctx->playpath) + sizeof(" playpath=");
103 
104  if (!(filename = av_malloc(len)))
105  return AVERROR(ENOMEM);
106 
107  av_strlcpy(filename, s->filename, len);
108  if (ctx->app) {
109  av_strlcat(filename, " app=", len);
110  av_strlcat(filename, ctx->app, len);
111  }
112  if (ctx->playpath) {
113  av_strlcat(filename, " playpath=", len);
114  av_strlcat(filename, ctx->playpath, len);
115  }
116  }
117 
118  RTMP_Init(r);
119  if (!RTMP_SetupURL(r, filename)) {
120  rc = AVERROR_UNKNOWN;
121  goto fail;
122  }
123 
124  if (flags & AVIO_FLAG_WRITE)
125  RTMP_EnableWrite(r);
126 
127  if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) {
128  rc = AVERROR_UNKNOWN;
129  goto fail;
130  }
131 
132  s->is_streamed = 1;
133  rc = 0;
134 fail:
135  if (filename != s->filename)
136  av_freep(&filename);
137  return rc;
138 }
139 
140 static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
141 {
142  LibRTMPContext *ctx = s->priv_data;
143  RTMP *r = &ctx->rtmp;
144 
145  return RTMP_Write(r, buf, size);
146 }
147 
148 static int rtmp_read(URLContext *s, uint8_t *buf, int size)
149 {
150  LibRTMPContext *ctx = s->priv_data;
151  RTMP *r = &ctx->rtmp;
152 
153  return RTMP_Read(r, buf, size);
154 }
155 
156 static int rtmp_read_pause(URLContext *s, int pause)
157 {
158  LibRTMPContext *ctx = s->priv_data;
159  RTMP *r = &ctx->rtmp;
160 
161  if (!RTMP_Pause(r, pause))
162  return AVERROR_UNKNOWN;
163  return 0;
164 }
165 
166 static int64_t rtmp_read_seek(URLContext *s, int stream_index,
167  int64_t timestamp, int flags)
168 {
169  LibRTMPContext *ctx = s->priv_data;
170  RTMP *r = &ctx->rtmp;
171 
172  if (flags & AVSEEK_FLAG_BYTE)
173  return AVERROR(ENOSYS);
174 
175  /* seeks are in milliseconds */
176  if (stream_index < 0)
177  timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE,
179 
180  if (!RTMP_SendSeek(r, timestamp))
181  return AVERROR_UNKNOWN;
182  return timestamp;
183 }
184 
186 {
187  LibRTMPContext *ctx = s->priv_data;
188  RTMP *r = &ctx->rtmp;
189 
190  return RTMP_Socket(r);
191 }
192 
193 #define OFFSET(x) offsetof(LibRTMPContext, x)
194 #define DEC AV_OPT_FLAG_DECODING_PARAM
195 #define ENC AV_OPT_FLAG_ENCODING_PARAM
196 static const AVOption options[] = {
197  {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
198  {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
199  { NULL },
200 };
201 
202 #define RTMP_CLASS(flavor)\
203 static const AVClass lib ## flavor ## _class = {\
204  .class_name = "lib" #flavor " protocol",\
205  .item_name = av_default_item_name,\
206  .option = options,\
207  .version = LIBAVUTIL_VERSION_INT,\
208 };
209 
210 RTMP_CLASS(rtmp)
212  .name = "rtmp",
213  .url_open = rtmp_open,
214  .url_read = rtmp_read,
215  .url_write = rtmp_write,
216  .url_close = rtmp_close,
217  .url_read_pause = rtmp_read_pause,
218  .url_read_seek = rtmp_read_seek,
219  .url_get_file_handle = rtmp_get_file_handle,
220  .priv_data_size = sizeof(LibRTMPContext),
221  .priv_data_class = &librtmp_class,
223 };
224 
225 RTMP_CLASS(rtmpt)
227  .name = "rtmpt",
228  .url_open = rtmp_open,
229  .url_read = rtmp_read,
230  .url_write = rtmp_write,
231  .url_close = rtmp_close,
232  .url_read_pause = rtmp_read_pause,
233  .url_read_seek = rtmp_read_seek,
234  .url_get_file_handle = rtmp_get_file_handle,
235  .priv_data_size = sizeof(LibRTMPContext),
236  .priv_data_class = &librtmpt_class,
238 };
239 
240 RTMP_CLASS(rtmpe)
242  .name = "rtmpe",
243  .url_open = rtmp_open,
244  .url_read = rtmp_read,
245  .url_write = rtmp_write,
246  .url_close = rtmp_close,
247  .url_read_pause = rtmp_read_pause,
248  .url_read_seek = rtmp_read_seek,
249  .url_get_file_handle = rtmp_get_file_handle,
250  .priv_data_size = sizeof(LibRTMPContext),
251  .priv_data_class = &librtmpe_class,
253 };
254 
255 RTMP_CLASS(rtmpte)
257  .name = "rtmpte",
258  .url_open = rtmp_open,
259  .url_read = rtmp_read,
260  .url_write = rtmp_write,
261  .url_close = rtmp_close,
262  .url_read_pause = rtmp_read_pause,
263  .url_read_seek = rtmp_read_seek,
264  .url_get_file_handle = rtmp_get_file_handle,
265  .priv_data_size = sizeof(LibRTMPContext),
266  .priv_data_class = &librtmpte_class,
268 };
269 
270 RTMP_CLASS(rtmps)
272  .name = "rtmps",
273  .url_open = rtmp_open,
274  .url_read = rtmp_read,
275  .url_write = rtmp_write,
276  .url_close = rtmp_close,
277  .url_read_pause = rtmp_read_pause,
278  .url_read_seek = rtmp_read_seek,
279  .url_get_file_handle = rtmp_get_file_handle,
280  .priv_data_size = sizeof(LibRTMPContext),
281  .priv_data_class = &librtmps_class,
283 };