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 *playpath;
43 
44 static void rtmp_log(int level, const char *fmt, va_list args)
45 {
46  switch (level) {
47  default:
48  case RTMP_LOGCRIT: level = AV_LOG_FATAL; break;
49  case RTMP_LOGERROR: level = AV_LOG_ERROR; break;
50  case RTMP_LOGWARNING: level = AV_LOG_WARNING; break;
51  case RTMP_LOGINFO: level = AV_LOG_INFO; break;
52  case RTMP_LOGDEBUG: level = AV_LOG_VERBOSE; break;
53  case RTMP_LOGDEBUG2: level = AV_LOG_DEBUG; break;
54  }
55 
56  av_vlog(NULL, level, fmt, args);
57  av_log(NULL, level, "\n");
58 }
59 
60 static int rtmp_close(URLContext *s)
61 {
62  LibRTMPContext *ctx = s->priv_data;
63  RTMP *r = &ctx->rtmp;
64 
65  RTMP_Close(r);
66  av_freep(&ctx->temp_filename);
67  return 0;
68 }
69 
82 static int rtmp_open(URLContext *s, const char *uri, int flags)
83 {
84  LibRTMPContext *ctx = s->priv_data;
85  RTMP *r = &ctx->rtmp;
86  int rc = 0, level;
87  char *filename = s->filename;
88 
89  switch (av_log_get_level()) {
90  default:
91  case AV_LOG_FATAL: level = RTMP_LOGCRIT; break;
92  case AV_LOG_ERROR: level = RTMP_LOGERROR; break;
93  case AV_LOG_WARNING: level = RTMP_LOGWARNING; break;
94  case AV_LOG_INFO: level = RTMP_LOGINFO; break;
95  case AV_LOG_VERBOSE: level = RTMP_LOGDEBUG; break;
96  case AV_LOG_DEBUG: level = RTMP_LOGDEBUG2; break;
97  }
98  RTMP_LogSetLevel(level);
99  RTMP_LogSetCallback(rtmp_log);
100 
101  if (ctx->app || ctx->playpath) {
102  int len = strlen(s->filename) + 1;
103  if (ctx->app) len += strlen(ctx->app) + sizeof(" app=");
104  if (ctx->playpath) len += strlen(ctx->playpath) + sizeof(" playpath=");
105 
106  if (!(ctx->temp_filename = filename = av_malloc(len)))
107  return AVERROR(ENOMEM);
108 
109  av_strlcpy(filename, s->filename, len);
110  if (ctx->app) {
111  av_strlcat(filename, " app=", len);
112  av_strlcat(filename, ctx->app, len);
113  }
114  if (ctx->playpath) {
115  av_strlcat(filename, " playpath=", len);
116  av_strlcat(filename, ctx->playpath, len);
117  }
118  }
119 
120  RTMP_Init(r);
121  if (!RTMP_SetupURL(r, filename)) {
122  rc = AVERROR_UNKNOWN;
123  goto fail;
124  }
125 
126  if (flags & AVIO_FLAG_WRITE)
127  RTMP_EnableWrite(r);
128 
129  if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) {
130  rc = AVERROR_UNKNOWN;
131  goto fail;
132  }
133 
134  s->is_streamed = 1;
135  return 0;
136 fail:
137  av_freep(&ctx->temp_filename);
138  return rc;
139 }
140 
141 static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
142 {
143  LibRTMPContext *ctx = s->priv_data;
144  RTMP *r = &ctx->rtmp;
145 
146  return RTMP_Write(r, buf, size);
147 }
148 
149 static int rtmp_read(URLContext *s, uint8_t *buf, int size)
150 {
151  LibRTMPContext *ctx = s->priv_data;
152  RTMP *r = &ctx->rtmp;
153 
154  return RTMP_Read(r, buf, size);
155 }
156 
157 static int rtmp_read_pause(URLContext *s, int pause)
158 {
159  LibRTMPContext *ctx = s->priv_data;
160  RTMP *r = &ctx->rtmp;
161 
162  if (!RTMP_Pause(r, pause))
163  return AVERROR_UNKNOWN;
164  return 0;
165 }
166 
167 static int64_t rtmp_read_seek(URLContext *s, int stream_index,
168  int64_t timestamp, int flags)
169 {
170  LibRTMPContext *ctx = s->priv_data;
171  RTMP *r = &ctx->rtmp;
172 
173  if (flags & AVSEEK_FLAG_BYTE)
174  return AVERROR(ENOSYS);
175 
176  /* seeks are in milliseconds */
177  if (stream_index < 0)
178  timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE,
180 
181  if (!RTMP_SendSeek(r, timestamp))
182  return AVERROR_UNKNOWN;
183  return timestamp;
184 }
185 
187 {
188  LibRTMPContext *ctx = s->priv_data;
189  RTMP *r = &ctx->rtmp;
190 
191  return RTMP_Socket(r);
192 }
193 
194 #define OFFSET(x) offsetof(LibRTMPContext, x)
195 #define DEC AV_OPT_FLAG_DECODING_PARAM
196 #define ENC AV_OPT_FLAG_ENCODING_PARAM
197 static const AVOption options[] = {
198  {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
199  {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
200  { NULL },
201 };
202 
203 #define RTMP_CLASS(flavor)\
204 static const AVClass lib ## flavor ## _class = {\
205  .class_name = "lib" #flavor " protocol",\
206  .item_name = av_default_item_name,\
207  .option = options,\
208  .version = LIBAVUTIL_VERSION_INT,\
209 };
210 
211 RTMP_CLASS(rtmp)
213  .name = "rtmp",
214  .url_open = rtmp_open,
215  .url_read = rtmp_read,
216  .url_write = rtmp_write,
217  .url_close = rtmp_close,
218  .url_read_pause = rtmp_read_pause,
219  .url_read_seek = rtmp_read_seek,
220  .url_get_file_handle = rtmp_get_file_handle,
221  .priv_data_size = sizeof(LibRTMPContext),
222  .priv_data_class = &librtmp_class,
224 };
225 
226 RTMP_CLASS(rtmpt)
228  .name = "rtmpt",
229  .url_open = rtmp_open,
230  .url_read = rtmp_read,
231  .url_write = rtmp_write,
232  .url_close = rtmp_close,
233  .url_read_pause = rtmp_read_pause,
234  .url_read_seek = rtmp_read_seek,
235  .url_get_file_handle = rtmp_get_file_handle,
236  .priv_data_size = sizeof(LibRTMPContext),
237  .priv_data_class = &librtmpt_class,
239 };
240 
241 RTMP_CLASS(rtmpe)
243  .name = "rtmpe",
244  .url_open = rtmp_open,
245  .url_read = rtmp_read,
246  .url_write = rtmp_write,
247  .url_close = rtmp_close,
248  .url_read_pause = rtmp_read_pause,
249  .url_read_seek = rtmp_read_seek,
250  .url_get_file_handle = rtmp_get_file_handle,
251  .priv_data_size = sizeof(LibRTMPContext),
252  .priv_data_class = &librtmpe_class,
254 };
255 
256 RTMP_CLASS(rtmpte)
258  .name = "rtmpte",
259  .url_open = rtmp_open,
260  .url_read = rtmp_read,
261  .url_write = rtmp_write,
262  .url_close = rtmp_close,
263  .url_read_pause = rtmp_read_pause,
264  .url_read_seek = rtmp_read_seek,
265  .url_get_file_handle = rtmp_get_file_handle,
266  .priv_data_size = sizeof(LibRTMPContext),
267  .priv_data_class = &librtmpte_class,
269 };
270 
271 RTMP_CLASS(rtmps)
273  .name = "rtmps",
274  .url_open = rtmp_open,
275  .url_read = rtmp_read,
276  .url_write = rtmp_write,
277  .url_close = rtmp_close,
278  .url_read_pause = rtmp_read_pause,
279  .url_read_seek = rtmp_read_seek,
280  .url_get_file_handle = rtmp_get_file_handle,
281  .priv_data_size = sizeof(LibRTMPContext),
282  .priv_data_class = &librtmps_class,
284 };