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 22/** 23 * @file 24 * RTMP protocol based on http://rtmpdump.mplayerhq.hu/ librtmp 25 */ 26 27#include "libavutil/mathematics.h" 28#include "avformat.h" 29#include "url.h" 30 31#include <librtmp/rtmp.h> 32#include <librtmp/log.h> 33 34static void rtmp_log(int level, const char *fmt, va_list args) 35{ 36 switch (level) { 37 default: 38 case RTMP_LOGCRIT: level = AV_LOG_FATAL; break; 39 case RTMP_LOGERROR: level = AV_LOG_ERROR; break; 40 case RTMP_LOGWARNING: level = AV_LOG_WARNING; break; 41 case RTMP_LOGINFO: level = AV_LOG_INFO; break; 42 case RTMP_LOGDEBUG: level = AV_LOG_VERBOSE; break; 43 case RTMP_LOGDEBUG2: level = AV_LOG_DEBUG; break; 44 } 45 46 av_vlog(NULL, level, fmt, args); 47 av_log(NULL, level, "\n"); 48} 49 50static int rtmp_close(URLContext *s) 51{ 52 RTMP *r = s->priv_data; 53 54 RTMP_Close(r); 55 return 0; 56} 57 58/** 59 * Open RTMP connection and verify that the stream can be played. 60 * 61 * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]... 62 * where 'app' is first one or two directories in the path 63 * (e.g. /ondemand/, /flash/live/, etc.) 64 * and 'playpath' is a file name (the rest of the path, 65 * may be prefixed with "mp4:") 66 * 67 * Additional RTMP library options may be appended as 68 * space-separated key-value pairs. 69 */ 70static int rtmp_open(URLContext *s, const char *uri, int flags) 71{ 72 RTMP *r = s->priv_data; 73 int rc; 74 75 switch (av_log_get_level()) { 76 default: 77 case AV_LOG_FATAL: rc = RTMP_LOGCRIT; break; 78 case AV_LOG_ERROR: rc = RTMP_LOGERROR; break; 79 case AV_LOG_WARNING: rc = RTMP_LOGWARNING; break; 80 case AV_LOG_INFO: rc = RTMP_LOGINFO; break; 81 case AV_LOG_VERBOSE: rc = RTMP_LOGDEBUG; break; 82 case AV_LOG_DEBUG: rc = RTMP_LOGDEBUG2; break; 83 } 84 RTMP_LogSetLevel(rc); 85 RTMP_LogSetCallback(rtmp_log); 86 87 RTMP_Init(r); 88 if (!RTMP_SetupURL(r, s->filename)) { 89 rc = -1; 90 goto fail; 91 } 92 93 if (flags & AVIO_FLAG_WRITE) 94 RTMP_EnableWrite(r); 95 96 if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) { 97 rc = -1; 98 goto fail; 99 } 100 101 s->is_streamed = 1; 102 return 0; 103fail: 104 return rc; 105} 106 107static int rtmp_write(URLContext *s, const uint8_t *buf, int size) 108{ 109 RTMP *r = s->priv_data; 110 111 return RTMP_Write(r, buf, size); 112} 113 114static int rtmp_read(URLContext *s, uint8_t *buf, int size) 115{ 116 RTMP *r = s->priv_data; 117 118 return RTMP_Read(r, buf, size); 119} 120 121static int rtmp_read_pause(URLContext *s, int pause) 122{ 123 RTMP *r = s->priv_data; 124 125 if (!RTMP_Pause(r, pause)) 126 return -1; 127 return 0; 128} 129 130static int64_t rtmp_read_seek(URLContext *s, int stream_index, 131 int64_t timestamp, int flags) 132{ 133 RTMP *r = s->priv_data; 134 135 if (flags & AVSEEK_FLAG_BYTE) 136 return AVERROR(ENOSYS); 137 138 /* seeks are in milliseconds */ 139 if (stream_index < 0) 140 timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE, 141 flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP); 142 143 if (!RTMP_SendSeek(r, timestamp)) 144 return -1; 145 return timestamp; 146} 147 148static int rtmp_get_file_handle(URLContext *s) 149{ 150 RTMP *r = s->priv_data; 151 152 return RTMP_Socket(r); 153} 154 155URLProtocol ff_rtmp_protocol = { 156 .name = "rtmp", 157 .url_open = rtmp_open, 158 .url_read = rtmp_read, 159 .url_write = rtmp_write, 160 .url_close = rtmp_close, 161 .url_read_pause = rtmp_read_pause, 162 .url_read_seek = rtmp_read_seek, 163 .url_get_file_handle = rtmp_get_file_handle, 164 .priv_data_size = sizeof(RTMP), 165 .flags = URL_PROTOCOL_FLAG_NETWORK, 166}; 167 168URLProtocol ff_rtmpt_protocol = { 169 .name = "rtmpt", 170 .url_open = rtmp_open, 171 .url_read = rtmp_read, 172 .url_write = rtmp_write, 173 .url_close = rtmp_close, 174 .url_read_pause = rtmp_read_pause, 175 .url_read_seek = rtmp_read_seek, 176 .url_get_file_handle = rtmp_get_file_handle, 177 .priv_data_size = sizeof(RTMP), 178 .flags = URL_PROTOCOL_FLAG_NETWORK, 179}; 180 181URLProtocol ff_rtmpe_protocol = { 182 .name = "rtmpe", 183 .url_open = rtmp_open, 184 .url_read = rtmp_read, 185 .url_write = rtmp_write, 186 .url_close = rtmp_close, 187 .url_read_pause = rtmp_read_pause, 188 .url_read_seek = rtmp_read_seek, 189 .url_get_file_handle = rtmp_get_file_handle, 190 .priv_data_size = sizeof(RTMP), 191 .flags = URL_PROTOCOL_FLAG_NETWORK, 192}; 193 194URLProtocol ff_rtmpte_protocol = { 195 .name = "rtmpte", 196 .url_open = rtmp_open, 197 .url_read = rtmp_read, 198 .url_write = rtmp_write, 199 .url_close = rtmp_close, 200 .url_read_pause = rtmp_read_pause, 201 .url_read_seek = rtmp_read_seek, 202 .url_get_file_handle = rtmp_get_file_handle, 203 .priv_data_size = sizeof(RTMP), 204 .flags = URL_PROTOCOL_FLAG_NETWORK, 205}; 206 207URLProtocol ff_rtmps_protocol = { 208 .name = "rtmps", 209 .url_open = rtmp_open, 210 .url_read = rtmp_read, 211 .url_write = rtmp_write, 212 .url_close = rtmp_close, 213 .url_read_pause = rtmp_read_pause, 214 .url_read_seek = rtmp_read_seek, 215 .url_get_file_handle = rtmp_get_file_handle, 216 .priv_data_size = sizeof(RTMP), 217 .flags = URL_PROTOCOL_FLAG_NETWORK, 218}; 219