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  }
193  }
194  if (ctx->playpath) {
195  av_strlcat(filename, " playpath=", len);
196  av_strlcat(filename, ctx->playpath, len);
197  }
198  if (ctx->live)
199  av_strlcat(filename, " live=1", len);
200  if (ctx->subscribe) {
201  av_strlcat(filename, " subscribe=", len);
202  av_strlcat(filename, ctx->subscribe, len);
203  }
204  if (ctx->client_buffer_time) {
205  av_strlcat(filename, " buffer=", len);
206  av_strlcat(filename, ctx->client_buffer_time, len);
207  }
208  if (ctx->swfurl || ctx->swfverify) {
209  av_strlcat(filename, " swfUrl=", len);
210 
211  if (ctx->swfverify) {
212  av_strlcat(filename, ctx->swfverify, len);
213  av_strlcat(filename, " swfVfy=1", len);
214  } else {
215  av_strlcat(filename, ctx->swfurl, len);
216  }
217  }
218 
219  RTMP_Init(r);
220  if (!RTMP_SetupURL(r, filename)) {
221  rc = AVERROR_UNKNOWN;
222  goto fail;
223  }
224 
225  if (flags & AVIO_FLAG_WRITE)
226  RTMP_EnableWrite(r);
227 
228  if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) {
229  rc = AVERROR_UNKNOWN;
230  goto fail;
231  }
232 
233  s->is_streamed = 1;
234  return 0;
235 fail:
236  av_freep(&ctx->temp_filename);
237  return rc;
238 }
239 
240 static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
241 {
242  LibRTMPContext *ctx = s->priv_data;
243  RTMP *r = &ctx->rtmp;
244 
245  return RTMP_Write(r, buf, size);
246 }
247 
248 static int rtmp_read(URLContext *s, uint8_t *buf, int size)
249 {
250  LibRTMPContext *ctx = s->priv_data;
251  RTMP *r = &ctx->rtmp;
252 
253  return RTMP_Read(r, buf, size);
254 }
255 
256 static int rtmp_read_pause(URLContext *s, int pause)
257 {
258  LibRTMPContext *ctx = s->priv_data;
259  RTMP *r = &ctx->rtmp;
260 
261  if (!RTMP_Pause(r, pause))
262  return AVERROR_UNKNOWN;
263  return 0;
264 }
265 
266 static int64_t rtmp_read_seek(URLContext *s, int stream_index,
267  int64_t timestamp, int flags)
268 {
269  LibRTMPContext *ctx = s->priv_data;
270  RTMP *r = &ctx->rtmp;
271 
272  if (flags & AVSEEK_FLAG_BYTE)
273  return AVERROR(ENOSYS);
274 
275  /* seeks are in milliseconds */
276  if (stream_index < 0)
277  timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE,
279 
280  if (!RTMP_SendSeek(r, timestamp))
281  return AVERROR_UNKNOWN;
282  return timestamp;
283 }
284 
286 {
287  LibRTMPContext *ctx = s->priv_data;
288  RTMP *r = &ctx->rtmp;
289 
290  return RTMP_Socket(r);
291 }
292 
293 #define OFFSET(x) offsetof(LibRTMPContext, x)
294 #define DEC AV_OPT_FLAG_DECODING_PARAM
295 #define ENC AV_OPT_FLAG_ENCODING_PARAM
296 static const AVOption options[] = {
297  {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
298  {"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},
299  {"rtmp_conn", "Append arbitrary AMF data to the Connect message", OFFSET(conn), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
300  {"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},
301  {"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"},
302  {"any", "both", 0, AV_OPT_TYPE_CONST, {.i64 = -2}, 0, 0, DEC, "rtmp_live"},
303  {"live", "live stream", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, DEC, "rtmp_live"},
304  {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, DEC, "rtmp_live"},
305  {"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},
306  {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
307  {"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
308  {"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},
309  {"rtmp_swfverify", "URL to player swf file, compute hash/size automatically. (unimplemented)", OFFSET(swfverify), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
310  {"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},
311  { NULL },
312 };
313 
314 #define RTMP_CLASS(flavor)\
315 static const AVClass lib ## flavor ## _class = {\
316  .class_name = "lib" #flavor " protocol",\
317  .item_name = av_default_item_name,\
318  .option = options,\
319  .version = LIBAVUTIL_VERSION_INT,\
320 };
321 
322 RTMP_CLASS(rtmp)
324  .name = "rtmp",
325  .url_open = rtmp_open,
326  .url_read = rtmp_read,
327  .url_write = rtmp_write,
328  .url_close = rtmp_close,
329  .url_read_pause = rtmp_read_pause,
330  .url_read_seek = rtmp_read_seek,
331  .url_get_file_handle = rtmp_get_file_handle,
332  .priv_data_size = sizeof(LibRTMPContext),
333  .priv_data_class = &librtmp_class,
335 };
336 
337 RTMP_CLASS(rtmpt)
339  .name = "rtmpt",
340  .url_open = rtmp_open,
341  .url_read = rtmp_read,
342  .url_write = rtmp_write,
343  .url_close = rtmp_close,
344  .url_read_pause = rtmp_read_pause,
345  .url_read_seek = rtmp_read_seek,
346  .url_get_file_handle = rtmp_get_file_handle,
347  .priv_data_size = sizeof(LibRTMPContext),
348  .priv_data_class = &librtmpt_class,
350 };
351 
352 RTMP_CLASS(rtmpe)
354  .name = "rtmpe",
355  .url_open = rtmp_open,
356  .url_read = rtmp_read,
357  .url_write = rtmp_write,
358  .url_close = rtmp_close,
359  .url_read_pause = rtmp_read_pause,
360  .url_read_seek = rtmp_read_seek,
361  .url_get_file_handle = rtmp_get_file_handle,
362  .priv_data_size = sizeof(LibRTMPContext),
363  .priv_data_class = &librtmpe_class,
365 };
366 
367 RTMP_CLASS(rtmpte)
369  .name = "rtmpte",
370  .url_open = rtmp_open,
371  .url_read = rtmp_read,
372  .url_write = rtmp_write,
373  .url_close = rtmp_close,
374  .url_read_pause = rtmp_read_pause,
375  .url_read_seek = rtmp_read_seek,
376  .url_get_file_handle = rtmp_get_file_handle,
377  .priv_data_size = sizeof(LibRTMPContext),
378  .priv_data_class = &librtmpte_class,
380 };
381 
382 RTMP_CLASS(rtmps)
384  .name = "rtmps",
385  .url_open = rtmp_open,
386  .url_read = rtmp_read,
387  .url_write = rtmp_write,
388  .url_close = rtmp_close,
389  .url_read_pause = rtmp_read_pause,
390  .url_read_seek = rtmp_read_seek,
391  .url_get_file_handle = rtmp_get_file_handle,
392  .priv_data_size = sizeof(LibRTMPContext),
393  .priv_data_class = &librtmps_class,
395 };
#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:295
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:266
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:256
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:294
URLProtocol ff_librtmpt_protocol
Definition: librtmp.c:338
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:368
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:353
URLProtocol ff_librtmp_protocol
Definition: librtmp.c:323
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:296
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:293
#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:240
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:285
URLProtocol ff_librtmps_protocol
Definition: librtmp.c:383
int len
static int rtmp_read(URLContext *s, uint8_t *buf, int size)
Definition: librtmp.c:248
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:314