Libav
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 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 
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 *conn;
41  char *subscribe;
42  char *playpath;
43  char *tcurl;
44  char *flashver;
45  char *swfurl;
46  char *swfverify;
47  char *pageurl;
49  int live;
52 
53 static void rtmp_log(int level, const char *fmt, va_list args)
54 {
55  switch (level) {
56  default:
57  case RTMP_LOGCRIT: level = AV_LOG_FATAL; break;
58  case RTMP_LOGERROR: level = AV_LOG_ERROR; break;
59  case RTMP_LOGWARNING: level = AV_LOG_WARNING; break;
60  case RTMP_LOGINFO: level = AV_LOG_INFO; break;
61  case RTMP_LOGDEBUG: level = AV_LOG_VERBOSE; break;
62  case RTMP_LOGDEBUG2: level = AV_LOG_DEBUG; break;
63  }
64 
65  av_vlog(NULL, level, fmt, args);
66  av_log(NULL, level, "\n");
67 }
68 
69 static int rtmp_close(URLContext *s)
70 {
71  LibRTMPContext *ctx = s->priv_data;
72  RTMP *r = &ctx->rtmp;
73 
74  RTMP_Close(r);
75  av_freep(&ctx->temp_filename);
76  return 0;
77 }
78 
91 static int rtmp_open(URLContext *s, const char *uri, int flags)
92 {
93  LibRTMPContext *ctx = s->priv_data;
94  RTMP *r = &ctx->rtmp;
95  int rc = 0, level;
96  char *filename = s->filename;
97  int len = strlen(s->filename) + 1;
98 
99  switch (av_log_get_level()) {
100  default:
101  case AV_LOG_FATAL: level = RTMP_LOGCRIT; break;
102  case AV_LOG_ERROR: level = RTMP_LOGERROR; break;
103  case AV_LOG_WARNING: level = RTMP_LOGWARNING; break;
104  case AV_LOG_INFO: level = RTMP_LOGINFO; break;
105  case AV_LOG_VERBOSE: level = RTMP_LOGDEBUG; break;
106  case AV_LOG_DEBUG: level = RTMP_LOGDEBUG2; break;
107  }
108  RTMP_LogSetLevel(level);
109  RTMP_LogSetCallback(rtmp_log);
110 
111  if (ctx->app) len += strlen(ctx->app) + sizeof(" app=");
112  if (ctx->tcurl) len += strlen(ctx->tcurl) + sizeof(" tcUrl=");
113  if (ctx->pageurl) len += strlen(ctx->pageurl) + sizeof(" pageUrl=");
114  if (ctx->flashver) len += strlen(ctx->flashver) + sizeof(" flashver=");
115 
116  if (ctx->conn) {
117  char *sep, *p = ctx->conn;
118  int options = 0;
119 
120  while (p) {
121  options++;
122  p += strspn(p, " ");
123  if (!*p)
124  break;
125  sep = strchr(p, ' ');
126  if (sep)
127  p = sep + 1;
128  else
129  break;
130  }
131  len += options * sizeof(" conn=");
132  len += strlen(ctx->conn);
133  }
134 
135  if (ctx->playpath)
136  len += strlen(ctx->playpath) + sizeof(" playpath=");
137  if (ctx->live)
138  len += sizeof(" live=1");
139  if (ctx->subscribe)
140  len += strlen(ctx->subscribe) + sizeof(" subscribe=");
141 
142  if (ctx->client_buffer_time)
143  len += strlen(ctx->client_buffer_time) + sizeof(" buffer=");
144 
145  if (ctx->swfurl || ctx->swfverify) {
146  len += sizeof(" swfUrl=");
147 
148  if (ctx->swfverify)
149  len += strlen(ctx->swfverify) + sizeof(" swfVfy=1");
150  else
151  len += strlen(ctx->swfurl);
152  }
153 
154  if (!(ctx->temp_filename = filename = av_malloc(len)))
155  return AVERROR(ENOMEM);
156 
157  av_strlcpy(filename, s->filename, len);
158  if (ctx->app) {
159  av_strlcat(filename, " app=", len);
160  av_strlcat(filename, ctx->app, len);
161  }
162  if (ctx->tcurl) {
163  av_strlcat(filename, " tcUrl=", len);
164  av_strlcat(filename, ctx->tcurl, len);
165  }
166  if (ctx->pageurl) {
167  av_strlcat(filename, " pageUrl=", len);
168  av_strlcat(filename, ctx->pageurl, len);
169  }
170  if (ctx->swfurl) {
171  av_strlcat(filename, " swfUrl=", len);
172  av_strlcat(filename, ctx->swfurl, len);
173  }
174  if (ctx->flashver) {
175  av_strlcat(filename, " flashVer=", len);
176  av_strlcat(filename, ctx->flashver, len);
177  }
178  if (ctx->conn) {
179  char *sep, *p = ctx->conn;
180  while (p) {
181  av_strlcat(filename, " conn=", len);
182  p += strspn(p, " ");
183  if (!*p)
184  break;
185  sep = strchr(p, ' ');
186  if (sep)
187  *sep = '\0';
188  av_strlcat(filename, p, len);
189 
190  if (sep)
191  p = sep + 1;
192  else
193  break;
194  }
195  }
196  if (ctx->playpath) {
197  av_strlcat(filename, " playpath=", len);
198  av_strlcat(filename, ctx->playpath, len);
199  }
200  if (ctx->live)
201  av_strlcat(filename, " live=1", len);
202  if (ctx->subscribe) {
203  av_strlcat(filename, " subscribe=", len);
204  av_strlcat(filename, ctx->subscribe, len);
205  }
206  if (ctx->client_buffer_time) {
207  av_strlcat(filename, " buffer=", len);
208  av_strlcat(filename, ctx->client_buffer_time, len);
209  }
210  if (ctx->swfurl || ctx->swfverify) {
211  av_strlcat(filename, " swfUrl=", len);
212 
213  if (ctx->swfverify) {
214  av_strlcat(filename, ctx->swfverify, len);
215  av_strlcat(filename, " swfVfy=1", len);
216  } else {
217  av_strlcat(filename, ctx->swfurl, len);
218  }
219  }
220 
221  RTMP_Init(r);
222  if (!RTMP_SetupURL(r, filename)) {
223  rc = AVERROR_UNKNOWN;
224  goto fail;
225  }
226 
227  if (flags & AVIO_FLAG_WRITE)
228  RTMP_EnableWrite(r);
229 
230  if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) {
231  rc = AVERROR_UNKNOWN;
232  goto fail;
233  }
234 
235  s->is_streamed = 1;
236  return 0;
237 fail:
238  av_freep(&ctx->temp_filename);
239  return rc;
240 }
241 
242 static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
243 {
244  LibRTMPContext *ctx = s->priv_data;
245  RTMP *r = &ctx->rtmp;
246 
247  return RTMP_Write(r, buf, size);
248 }
249 
250 static int rtmp_read(URLContext *s, uint8_t *buf, int size)
251 {
252  LibRTMPContext *ctx = s->priv_data;
253  RTMP *r = &ctx->rtmp;
254 
255  return RTMP_Read(r, buf, size);
256 }
257 
258 static int rtmp_read_pause(URLContext *s, int pause)
259 {
260  LibRTMPContext *ctx = s->priv_data;
261  RTMP *r = &ctx->rtmp;
262 
263  if (!RTMP_Pause(r, pause))
264  return AVERROR_UNKNOWN;
265  return 0;
266 }
267 
268 static int64_t rtmp_read_seek(URLContext *s, int stream_index,
269  int64_t timestamp, int flags)
270 {
271  LibRTMPContext *ctx = s->priv_data;
272  RTMP *r = &ctx->rtmp;
273 
274  if (flags & AVSEEK_FLAG_BYTE)
275  return AVERROR(ENOSYS);
276 
277  /* seeks are in milliseconds */
278  if (stream_index < 0)
279  timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE,
281 
282  if (!RTMP_SendSeek(r, timestamp))
283  return AVERROR_UNKNOWN;
284  return timestamp;
285 }
286 
288 {
289  LibRTMPContext *ctx = s->priv_data;
290  RTMP *r = &ctx->rtmp;
291 
292  return RTMP_Socket(r);
293 }
294 
295 #define OFFSET(x) offsetof(LibRTMPContext, x)
296 #define DEC AV_OPT_FLAG_DECODING_PARAM
297 #define ENC AV_OPT_FLAG_ENCODING_PARAM
298 static const AVOption options[] = {
299  {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
300  {"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},
301  {"rtmp_conn", "Append arbitrary AMF data to the Connect message", OFFSET(conn), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
302  {"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},
303  {"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"},
304  {"any", "both", 0, AV_OPT_TYPE_CONST, {.i64 = -2}, 0, 0, DEC, "rtmp_live"},
305  {"live", "live stream", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, DEC, "rtmp_live"},
306  {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, DEC, "rtmp_live"},
307  {"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},
308  {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
309  {"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
310  {"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},
311  {"rtmp_swfverify", "URL to player swf file, compute hash/size automatically. (unimplemented)", OFFSET(swfverify), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
312  {"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},
313  { NULL },
314 };
315 
316 #define RTMP_CLASS(flavor)\
317 static const AVClass lib ## flavor ## _class = {\
318  .class_name = "lib" #flavor " protocol",\
319  .item_name = av_default_item_name,\
320  .option = options,\
321  .version = LIBAVUTIL_VERSION_INT,\
322 };
323 
324 RTMP_CLASS(rtmp)
326  .name = "rtmp",
327  .url_open = rtmp_open,
328  .url_read = rtmp_read,
329  .url_write = rtmp_write,
330  .url_close = rtmp_close,
331  .url_read_pause = rtmp_read_pause,
332  .url_read_seek = rtmp_read_seek,
333  .url_get_file_handle = rtmp_get_file_handle,
334  .priv_data_size = sizeof(LibRTMPContext),
335  .priv_data_class = &librtmp_class,
337 };
338 
339 RTMP_CLASS(rtmpt)
341  .name = "rtmpt",
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 = &librtmpt_class,
352 };
353 
354 RTMP_CLASS(rtmpe)
356  .name = "rtmpe",
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 = &librtmpe_class,
367 };
368 
369 RTMP_CLASS(rtmpte)
371  .name = "rtmpte",
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 = &librtmpte_class,
382 };
383 
384 RTMP_CLASS(rtmps)
386  .name = "rtmps",
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 = &librtmps_class,
397 };
#define AVSEEK_FLAG_BACKWARD
Definition: avformat.h:1609
void * av_malloc(size_t size)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...
Definition: mem.c:62
char * app
Definition: librtmp.c:39
#define ENC
Definition: librtmp.c:297
int size
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:35
static int64_t rtmp_read_seek(URLContext *s, int stream_index, int64_t timestamp, int flags)
Definition: librtmp.c:268
AVOption.
Definition: opt.h:234
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)
Rescale a 64-bit integer with specified rounding.
Definition: mathematics.c:61
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:129
int is_streamed
true if streamed (no seek possible), default = false
Definition: url.h:48
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:293
char * temp_filename
Definition: librtmp.c:50
char * flashver
Definition: librtmp.c:44
static int rtmp_read_pause(URLContext *s, int pause)
Definition: librtmp.c:258
void av_freep(void *arg)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc() and set the pointer ...
Definition: mem.c:198
char * tcurl
Definition: librtmp.c:43
char * swfverify
Definition: librtmp.c:46
uint8_t
Round toward +infinity.
Definition: mathematics.h:53
AVOptions.
static int rtmp_close(URLContext *s)
Definition: librtmp.c:69
static int rtmp_open(URLContext *s, const char *uri, int flags)
Open RTMP connection and verify that the stream can be played.
Definition: librtmp.c:91
#define DEC
Definition: librtmp.c:296
URLProtocol ff_librtmpt_protocol
Definition: librtmp.c:340
static int flags
Definition: log.c:44
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:139
#define r
Definition: input.c:51
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:123
static void rtmp_log(int level, const char *fmt, va_list args)
Definition: librtmp.c:53
#define AVERROR(e)
Definition: error.h:43
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:144
void av_log(void *avcl, int level, const char *fmt,...)
Definition: log.c:169
int av_log_get_level(void)
Get the current log level.
Definition: log.c:186
size_t av_strlcpy(char *dst, const char *src, size_t size)
Copy the string src to dst, but no more than size - 1 bytes, and null-terminate dst.
Definition: avstring.c:81
URLProtocol ff_librtmpte_protocol
Definition: librtmp.c:370
char * pageurl
Definition: librtmp.c:47
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:234
char * conn
Definition: librtmp.c:40
URLProtocol ff_librtmpe_protocol
Definition: librtmp.c:355
URLProtocol ff_librtmp_protocol
Definition: librtmp.c:325
char * client_buffer_time
Definition: librtmp.c:48
NULL
Definition: eval.c:55
#define AV_LOG_INFO
Standard information.
Definition: log.h:134
static const AVOption options[]
Definition: librtmp.c:298
Definition: url.h:41
Describe the class of an AVClass context structure.
Definition: log.h:33
void * priv_data
Definition: url.h:44
#define OFFSET(x)
Definition: librtmp.c:295
#define AVSEEK_FLAG_BYTE
seeking based on position in bytes
Definition: avformat.h:1610
void av_vlog(void *avcl, int level, const char *fmt, va_list vl)
Send the specified message to the log if the level is less than or equal to the current av_log_level...
Definition: log.c:181
char * playpath
Definition: librtmp.c:42
static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
Definition: librtmp.c:242
size_t av_strlcat(char *dst, const char *src, size_t size)
Append the string src to the string dst, but to a total length of no more than size - 1 bytes...
Definition: avstring.c:91
const char * name
Definition: url.h:54
Round toward -infinity.
Definition: mathematics.h:52
uint8_t level
Definition: svq3.c:147
Main libavformat public API header.
char * filename
specified URL
Definition: url.h:45
#define AVERROR_UNKNOWN
Unknown error, typically from an external library.
Definition: error.h:61
char * swfurl
Definition: librtmp.c:45
static int rtmp_get_file_handle(URLContext *s)
Definition: librtmp.c:287
URLProtocol ff_librtmps_protocol
Definition: librtmp.c:385
int len
static int rtmp_read(URLContext *s, uint8_t *buf, int size)
Definition: librtmp.c:250
char * subscribe
Definition: librtmp.c:41
unbuffered private I/O API
#define AV_LOG_FATAL
Something went wrong and recovery is not possible.
Definition: log.h:117
#define RTMP_CLASS(flavor)
Definition: librtmp.c:316