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  if (rc)
138  RTMP_Close(r);
139 
140  return rc;
141 }
142 
143 static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
144 {
145  LibRTMPContext *ctx = s->priv_data;
146  RTMP *r = &ctx->rtmp;
147 
148  return RTMP_Write(r, buf, size);
149 }
150 
151 static int rtmp_read(URLContext *s, uint8_t *buf, int size)
152 {
153  LibRTMPContext *ctx = s->priv_data;
154  RTMP *r = &ctx->rtmp;
155 
156  return RTMP_Read(r, buf, size);
157 }
158 
159 static int rtmp_read_pause(URLContext *s, int pause)
160 {
161  LibRTMPContext *ctx = s->priv_data;
162  RTMP *r = &ctx->rtmp;
163 
164  if (!RTMP_Pause(r, pause))
165  return AVERROR_UNKNOWN;
166  return 0;
167 }
168 
169 static int64_t rtmp_read_seek(URLContext *s, int stream_index,
170  int64_t timestamp, int flags)
171 {
172  LibRTMPContext *ctx = s->priv_data;
173  RTMP *r = &ctx->rtmp;
174 
175  if (flags & AVSEEK_FLAG_BYTE)
176  return AVERROR(ENOSYS);
177 
178  /* seeks are in milliseconds */
179  if (stream_index < 0)
180  timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE,
182 
183  if (!RTMP_SendSeek(r, timestamp))
184  return AVERROR_UNKNOWN;
185  return timestamp;
186 }
187 
189 {
190  LibRTMPContext *ctx = s->priv_data;
191  RTMP *r = &ctx->rtmp;
192 
193  return RTMP_Socket(r);
194 }
195 
196 #define OFFSET(x) offsetof(LibRTMPContext, x)
197 #define DEC AV_OPT_FLAG_DECODING_PARAM
198 #define ENC AV_OPT_FLAG_ENCODING_PARAM
199 static const AVOption options[] = {
200  {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
201  {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
202  { NULL },
203 };
204 
205 #define RTMP_CLASS(flavor)\
206 static const AVClass lib ## flavor ## _class = {\
207  .class_name = "lib" #flavor " protocol",\
208  .item_name = av_default_item_name,\
209  .option = options,\
210  .version = LIBAVUTIL_VERSION_INT,\
211 };
212 
213 RTMP_CLASS(rtmp)
215  .name = "rtmp",
216  .url_open = rtmp_open,
217  .url_read = rtmp_read,
218  .url_write = rtmp_write,
219  .url_close = rtmp_close,
220  .url_read_pause = rtmp_read_pause,
221  .url_read_seek = rtmp_read_seek,
222  .url_get_file_handle = rtmp_get_file_handle,
223  .priv_data_size = sizeof(LibRTMPContext),
224  .priv_data_class = &librtmp_class,
226 };
227 
228 RTMP_CLASS(rtmpt)
230  .name = "rtmpt",
231  .url_open = rtmp_open,
232  .url_read = rtmp_read,
233  .url_write = rtmp_write,
234  .url_close = rtmp_close,
235  .url_read_pause = rtmp_read_pause,
236  .url_read_seek = rtmp_read_seek,
237  .url_get_file_handle = rtmp_get_file_handle,
238  .priv_data_size = sizeof(LibRTMPContext),
239  .priv_data_class = &librtmpt_class,
241 };
242 
243 RTMP_CLASS(rtmpe)
245  .name = "rtmpe",
246  .url_open = rtmp_open,
247  .url_read = rtmp_read,
248  .url_write = rtmp_write,
249  .url_close = rtmp_close,
250  .url_read_pause = rtmp_read_pause,
251  .url_read_seek = rtmp_read_seek,
252  .url_get_file_handle = rtmp_get_file_handle,
253  .priv_data_size = sizeof(LibRTMPContext),
254  .priv_data_class = &librtmpe_class,
256 };
257 
258 RTMP_CLASS(rtmpte)
260  .name = "rtmpte",
261  .url_open = rtmp_open,
262  .url_read = rtmp_read,
263  .url_write = rtmp_write,
264  .url_close = rtmp_close,
265  .url_read_pause = rtmp_read_pause,
266  .url_read_seek = rtmp_read_seek,
267  .url_get_file_handle = rtmp_get_file_handle,
268  .priv_data_size = sizeof(LibRTMPContext),
269  .priv_data_class = &librtmpte_class,
271 };
272 
273 RTMP_CLASS(rtmps)
275  .name = "rtmps",
276  .url_open = rtmp_open,
277  .url_read = rtmp_read,
278  .url_write = rtmp_write,
279  .url_close = rtmp_close,
280  .url_read_pause = rtmp_read_pause,
281  .url_read_seek = rtmp_read_seek,
282  .url_get_file_handle = rtmp_get_file_handle,
283  .priv_data_size = sizeof(LibRTMPContext),
284  .priv_data_class = &librtmps_class,
286 };