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 {
75  LibRTMPContext *ctx = s->priv_data;
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 {
97  LibRTMPContext *ctx = s->priv_data;
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 {
261  LibRTMPContext *ctx = s->priv_data;
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 {
272  LibRTMPContext *ctx = s->priv_data;
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 {
283  LibRTMPContext *ctx = s->priv_data;
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 {
294  LibRTMPContext *ctx = s->priv_data;
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 {
312  LibRTMPContext *ctx = s->priv_data;
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 
350 RTMP_CLASS(rtmp)
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 };
av_vlog
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:424
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:186
level
uint8_t level
Definition: svq3.c:204
r
const char * r
Definition: vf_curves.c:116
AVERROR
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
opt.h
LibRTMPContext::subscribe
char * subscribe
Definition: librtmp.c:44
URL_PROTOCOL_FLAG_NETWORK
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:34
LibRTMPContext::buffer_size
int buffer_size
Definition: librtmp.c:54
ENC
#define ENC
Definition: librtmp.c:320
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:57
LibRTMPContext::live
int live
Definition: librtmp.c:52
rtmp_close
static int rtmp_close(URLContext *s)
Definition: librtmp.c:73
ff_librtmps_protocol
const URLProtocol ff_librtmps_protocol
Definition: librtmp.c:411
tmp
static uint8_t tmp[11]
Definition: aes_ctr.c:26
AVOption
AVOption.
Definition: opt.h:247
AVSEEK_FLAG_BYTE
#define AVSEEK_FLAG_BYTE
seeking based on position in bytes
Definition: avformat.h:2168
AV_LOG_VERBOSE
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:196
mathematics.h
AVERROR_UNKNOWN
#define AVERROR_UNKNOWN
Unknown error, typically from an external library.
Definition: error.h:73
ff_librtmpt_protocol
const URLProtocol ff_librtmpt_protocol
Definition: librtmp.c:366
URLProtocol
Definition: url.h:54
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:31
rtmp_read_seek
static int64_t rtmp_read_seek(URLContext *s, int stream_index, int64_t timestamp, int flags)
Definition: librtmp.c:291
rtmp_open
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
LibRTMPContext::conn
char * conn
Definition: librtmp.c:43
DEC
#define DEC
Definition: librtmp.c:319
rtmp_read_pause
static int rtmp_read_pause(URLContext *s, int pause)
Definition: librtmp.c:281
fail
#define fail()
Definition: checkasm.h:136
LibRTMPContext::temp_filename
char * temp_filename
Definition: librtmp.c:53
AV_ROUND_UP
@ AV_ROUND_UP
Round toward +infinity.
Definition: mathematics.h:83
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
s
#define s(width, name)
Definition: cbs_vp9.c:257
ff_librtmp_protocol
const URLProtocol ff_librtmp_protocol
Definition: librtmp.c:351
AVIO_FLAG_WRITE
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:656
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:201
ctx
AVFormatContext * ctx
Definition: movenc.c:48
av_log_get_level
int av_log_get_level(void)
Get the current log level.
Definition: log.c:435
AVSEEK_FLAG_BACKWARD
#define AVSEEK_FLAG_BACKWARD
Definition: avformat.h:2167
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:66
NULL
#define NULL
Definition: coverity.c:32
LibRTMPContext
Definition: librtmp.c:39
LibRTMPContext::pageurl
char * pageurl
Definition: librtmp.c:50
LibRTMPContext::swfverify
char * swfverify
Definition: librtmp.c:49
av_rescale_rnd
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:57
rtmp_log
static void rtmp_log(int level, const char *fmt, va_list args)
Definition: librtmp.c:57
LibRTMPContext::playpath
char * playpath
Definition: librtmp.c:45
size
int size
Definition: twinvq_data.h:10344
URLProtocol::name
const char * name
Definition: url.h:55
LibRTMPContext::client_buffer_time
char * client_buffer_time
Definition: librtmp.c:51
options
static const AVOption options[]
Definition: librtmp.c:321
AVERROR_EXTERNAL
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:59
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:191
URLContext
Definition: url.h:38
LibRTMPContext::app
char * app
Definition: librtmp.c:42
AV_TIME_BASE
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
AV_ROUND_DOWN
@ AV_ROUND_DOWN
Round toward -infinity.
Definition: mathematics.h:82
url.h
rtmp_read
static int rtmp_read(URLContext *s, uint8_t *buf, int size)
Definition: librtmp.c:270
len
int len
Definition: vorbis_enc_data.h:426
ret
ret
Definition: filter_design.txt:187
AV_LOG_FATAL
#define AV_LOG_FATAL
Something went wrong and recovery is not possible.
Definition: log.h:174
av_strlcat
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
avformat.h
OFFSET
#define OFFSET(x)
Definition: librtmp.c:318
network.h
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Definition: opt.h:224
ff_librtmpe_protocol
const URLProtocol ff_librtmpe_protocol
Definition: librtmp.c:381
LibRTMPContext::flashver
char * flashver
Definition: librtmp.c:47
LibRTMPContext::swfurl
char * swfurl
Definition: librtmp.c:48
rtmp_write
static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
Definition: librtmp.c:259
ff_librtmpte_protocol
const URLProtocol ff_librtmpte_protocol
Definition: librtmp.c:396
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
rtmp_get_file_handle
static int rtmp_get_file_handle(URLContext *s)
Definition: librtmp.c:310
LibRTMPContext::tcurl
char * tcurl
Definition: librtmp.c:46
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:561
av_strlcpy
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
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
LibRTMPContext::rtmp
RTMP rtmp
Definition: librtmp.c:41
avstring.h
AV_OPT_TYPE_STRING
@ AV_OPT_TYPE_STRING
Definition: opt.h:228
RTMP_CLASS
#define RTMP_CLASS(flavor)
Definition: librtmp.c:342
AV_OPT_TYPE_CONST
@ AV_OPT_TYPE_CONST
Definition: opt.h:233