FFmpeg
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 #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;
52  int live;
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 {
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 {
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  else
197  break;
198  }
199  }
200  if (ctx->playpath) {
201  av_strlcat(filename, " playpath=", len);
202  av_strlcat(filename, ctx->playpath, len);
203  }
204  if (ctx->live)
205  av_strlcat(filename, " live=1", len);
206  if (ctx->subscribe) {
207  av_strlcat(filename, " subscribe=", len);
208  av_strlcat(filename, ctx->subscribe, len);
209  }
210  if (ctx->client_buffer_time) {
211  av_strlcat(filename, " buffer=", len);
212  av_strlcat(filename, ctx->client_buffer_time, len);
213  }
214  if (ctx->swfurl || ctx->swfverify) {
215  av_strlcat(filename, " swfUrl=", len);
216 
217  if (ctx->swfverify) {
218  av_strlcat(filename, ctx->swfverify, len);
219  av_strlcat(filename, " swfVfy=1", len);
220  } else {
221  av_strlcat(filename, ctx->swfurl, len);
222  }
223  }
224 
225  RTMP_Init(r);
226  if (!RTMP_SetupURL(r, filename)) {
227  rc = AVERROR_UNKNOWN;
228  goto fail;
229  }
230 
231  if (flags & AVIO_FLAG_WRITE)
232  RTMP_EnableWrite(r);
233 
234  if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) {
235  rc = AVERROR_UNKNOWN;
236  goto fail;
237  }
238 
239 #if CONFIG_NETWORK
240  if (ctx->buffer_size >= 0 && (flags & AVIO_FLAG_WRITE)) {
241  int tmp = ctx->buffer_size;
242  if (setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp))) {
243  rc = AVERROR_EXTERNAL;
244  goto fail;
245  }
246  }
247 #endif
248 
249  s->is_streamed = 1;
250  return 0;
251 fail:
252  av_freep(&ctx->temp_filename);
253  if (rc)
254  RTMP_Close(r);
255 
256  return rc;
257 }
258 
259 static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
260 {
262  RTMP *r = &ctx->rtmp;
263 
264  int ret = RTMP_Write(r, buf, size);
265  if (!ret)
266  return AVERROR_EOF;
267  return ret;
268 }
269 
270 static int rtmp_read(URLContext *s, uint8_t *buf, int size)
271 {
273  RTMP *r = &ctx->rtmp;
274 
275  int ret = RTMP_Read(r, buf, size);
276  if (!ret)
277  return AVERROR_EOF;
278  return ret;
279 }
280 
281 static int rtmp_read_pause(URLContext *s, int pause)
282 {
284  RTMP *r = &ctx->rtmp;
285 
286  if (!RTMP_Pause(r, pause))
287  return AVERROR_UNKNOWN;
288  return 0;
289 }
290 
291 static int64_t rtmp_read_seek(URLContext *s, int stream_index,
292  int64_t timestamp, int flags)
293 {
295  RTMP *r = &ctx->rtmp;
296 
297  if (flags & AVSEEK_FLAG_BYTE)
298  return AVERROR(ENOSYS);
299 
300  /* seeks are in milliseconds */
301  if (stream_index < 0)
302  timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE,
304 
305  if (!RTMP_SendSeek(r, timestamp))
306  return AVERROR_UNKNOWN;
307  return timestamp;
308 }
309 
311 {
313  RTMP *r = &ctx->rtmp;
314 
315  return RTMP_Socket(r);
316 }
317 
318 #define OFFSET(x) offsetof(LibRTMPContext, x)
319 #define DEC AV_OPT_FLAG_DECODING_PARAM
320 #define ENC AV_OPT_FLAG_ENCODING_PARAM
321 static const AVOption options[] = {
322  {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
323  {"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},
324  {"rtmp_conn", "Append arbitrary AMF data to the Connect message", OFFSET(conn), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
325  {"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},
326  {"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"},
327  {"any", "both", 0, AV_OPT_TYPE_CONST, {.i64 = -2}, 0, 0, DEC, "rtmp_live"},
328  {"live", "live stream", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, DEC, "rtmp_live"},
329  {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, DEC, "rtmp_live"},
330  {"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},
331  {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
332  {"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
333  {"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},
334  {"rtmp_swfverify", "URL to player swf file, compute hash/size automatically. (unimplemented)", OFFSET(swfverify), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
335  {"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},
336 #if CONFIG_NETWORK
337  {"rtmp_buffer_size", "set buffer size in bytes", OFFSET(buffer_size), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, DEC|ENC },
338 #endif
339  { NULL },
340 };
341 
342 #define RTMP_CLASS(flavor)\
343 static const AVClass lib ## flavor ## _class = {\
344  .class_name = "lib" #flavor " protocol",\
345  .item_name = av_default_item_name,\
346  .option = options,\
347  .version = LIBAVUTIL_VERSION_INT,\
348 };
349 
352  .name = "rtmp",
353  .url_open = rtmp_open,
354  .url_read = rtmp_read,
355  .url_write = rtmp_write,
356  .url_close = rtmp_close,
357  .url_read_pause = rtmp_read_pause,
358  .url_read_seek = rtmp_read_seek,
359  .url_get_file_handle = rtmp_get_file_handle,
360  .priv_data_size = sizeof(LibRTMPContext),
361  .priv_data_class = &librtmp_class,
363 };
364 
365 RTMP_CLASS(rtmpt)
367  .name = "rtmpt",
368  .url_open = rtmp_open,
369  .url_read = rtmp_read,
370  .url_write = rtmp_write,
371  .url_close = rtmp_close,
372  .url_read_pause = rtmp_read_pause,
373  .url_read_seek = rtmp_read_seek,
374  .url_get_file_handle = rtmp_get_file_handle,
375  .priv_data_size = sizeof(LibRTMPContext),
376  .priv_data_class = &librtmpt_class,
378 };
379 
380 RTMP_CLASS(rtmpe)
382  .name = "rtmpe",
383  .url_open = rtmp_open,
384  .url_read = rtmp_read,
385  .url_write = rtmp_write,
386  .url_close = rtmp_close,
387  .url_read_pause = rtmp_read_pause,
388  .url_read_seek = rtmp_read_seek,
389  .url_get_file_handle = rtmp_get_file_handle,
390  .priv_data_size = sizeof(LibRTMPContext),
391  .priv_data_class = &librtmpe_class,
393 };
394 
395 RTMP_CLASS(rtmpte)
397  .name = "rtmpte",
398  .url_open = rtmp_open,
399  .url_read = rtmp_read,
400  .url_write = rtmp_write,
401  .url_close = rtmp_close,
402  .url_read_pause = rtmp_read_pause,
403  .url_read_seek = rtmp_read_seek,
404  .url_get_file_handle = rtmp_get_file_handle,
405  .priv_data_size = sizeof(LibRTMPContext),
406  .priv_data_class = &librtmpte_class,
408 };
409 
410 RTMP_CLASS(rtmps)
412  .name = "rtmps",
413  .url_open = rtmp_open,
414  .url_read = rtmp_read,
415  .url_write = rtmp_write,
416  .url_close = rtmp_close,
417  .url_read_pause = rtmp_read_pause,
418  .url_read_seek = rtmp_read_seek,
419  .url_get_file_handle = rtmp_get_file_handle,
420  .priv_data_size = sizeof(LibRTMPContext),
421  .priv_data_class = &librtmps_class,
423 };
#define AVSEEK_FLAG_BACKWARD
Definition: avformat.h:2511
#define NULL
Definition: coverity.c:32
char * app
Definition: librtmp.c:42
#define ENC
Definition: librtmp.c:320
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:34
static int64_t rtmp_read_seek(URLContext *s, int stream_index, int64_t timestamp, int flags)
Definition: librtmp.c:291
AVOption.
Definition: opt.h:246
const URLProtocol ff_librtmps_protocol
Definition: librtmp.c:411
const char * fmt
Definition: avisynth_c.h:861
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
int is_streamed
true if streamed (no seek possible), default = false
Definition: url.h:45
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:675
char * temp_filename
Definition: librtmp.c:53
char * flashver
Definition: librtmp.c:47
static int rtmp_read_pause(URLContext *s, int pause)
Definition: librtmp.c:281
char * tcurl
Definition: librtmp.c:46
char * swfverify
Definition: librtmp.c:49
uint8_t
Round toward +infinity.
Definition: mathematics.h:83
#define av_malloc(s)
AVOptions.
int buffer_size
Definition: librtmp.c:54
static int rtmp_close(URLContext *s)
Definition: librtmp.c:73
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:95
#define DEC
Definition: librtmp.c:319
#define AVERROR_EOF
End of file.
Definition: error.h:55
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:192
ptrdiff_t size
Definition: opengl_enc.c:100
#define av_log(a,...)
const URLProtocol ff_librtmpt_protocol
Definition: librtmp.c:366
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
static void rtmp_log(int level, const char *fmt, va_list args)
Definition: librtmp.c:57
const char * r
Definition: vf_curves.c:114
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:197
int av_log_get_level(void)
Get the current log level.
Definition: log.c:380
const URLProtocol ff_librtmp_protocol
Definition: librtmp.c:351
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:83
#define fail()
Definition: checkasm.h:121
char * pageurl
Definition: librtmp.c:50
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
char * conn
Definition: librtmp.c:43
AVFormatContext * ctx
Definition: movenc.c:48
#define s(width, name)
Definition: cbs_vp9.c:257
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:58
char * client_buffer_time
Definition: librtmp.c:51
#define AV_LOG_INFO
Standard information.
Definition: log.h:187
static const AVOption options[]
Definition: librtmp.c:321
void * buf
Definition: avisynth_c.h:766
Definition: url.h:38
Describe the class of an AVClass context structure.
Definition: log.h:67
void * priv_data
Definition: url.h:41
#define OFFSET(x)
Definition: librtmp.c:318
#define AVSEEK_FLAG_BYTE
seeking based on position in bytes
Definition: avformat.h:2512
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:373
char * playpath
Definition: librtmp.c:45
static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
Definition: librtmp.c:259
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:93
const char * name
Definition: url.h:55
Round toward -infinity.
Definition: mathematics.h:82
const URLProtocol ff_librtmpte_protocol
Definition: librtmp.c:396
#define flags(name, subs,...)
Definition: cbs_av1.c:561
uint8_t level
Definition: svq3.c:207
Main libavformat public API header.
char * filename
specified URL
Definition: url.h:42
#define AVERROR_UNKNOWN
Unknown error, typically from an external library.
Definition: error.h:71
char * swfurl
Definition: librtmp.c:48
static int rtmp_get_file_handle(URLContext *s)
Definition: librtmp.c:310
int len
static int rtmp_read(URLContext *s, uint8_t *buf, int size)
Definition: librtmp.c:270
const URLProtocol ff_librtmpe_protocol
Definition: librtmp.c:381
char * subscribe
Definition: librtmp.c:44
#define av_freep(p)
unbuffered private I/O API
#define AV_LOG_FATAL
Something went wrong and recovery is not possible.
Definition: log.h:170
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:57
#define RTMP_CLASS(flavor)
Definition: librtmp.c:342
static uint8_t tmp[11]
Definition: aes_ctr.c:26