diff --git a/Changelog b/Changelog index 762407d4e5..14f5fb6601 100644 --- a/Changelog +++ b/Changelog @@ -66,6 +66,7 @@ version : - VorbisComment writing for FLAC, Ogg FLAC and Ogg Speex files - RTP depacketization of Theora - HTTP Digest authentication +- RTMP/RTMPT/RTMPS/RTMPE/RTMPTE protocol support via librtmp diff --git a/configure b/configure index e6706b9716..5604181b58 100755 --- a/configure +++ b/configure @@ -177,6 +177,7 @@ External library support: --enable-libnut enable NUT (de)muxing via libnut, native (de)muxer exists [no] --enable-libopenjpeg enable JPEG 2000 decoding via OpenJPEG [no] + --enable-librtmp enable RTMP[E] support via librtmp [no] --enable-libschroedinger enable Dirac support via libschroedinger [no] --enable-libspeex enable Speex decoding via libspeex [no] --enable-libtheora enable Theora encoding via libtheora [no] @@ -920,6 +921,7 @@ CONFIG_LIST=" libopencore_amrnb libopencore_amrwb libopenjpeg + librtmp libschroedinger libspeex libtheora @@ -2615,6 +2617,7 @@ enabled libnut && require libnut libnut.h nut_demuxer_init -lnut enabled libopencore_amrnb && require libopencore_amrnb opencore-amrnb/interf_dec.h Decoder_Interface_init -lopencore-amrnb -lm enabled libopencore_amrwb && require libopencore_amrwb opencore-amrwb/dec_if.h D_IF_init -lopencore-amrwb -lm enabled libopenjpeg && require libopenjpeg openjpeg.h opj_version -lopenjpeg +enabled librtmp && require librtmp librtmp/rtmp.h RTMP_Init -lrtmp enabled libschroedinger && add_cflags $(pkg-config --cflags schroedinger-1.0) && require libschroedinger schroedinger/schro.h schro_init $(pkg-config --libs schroedinger-1.0) enabled libspeex && require libspeex speex/speex.h speex_decoder_init -lspeex @@ -2893,6 +2896,7 @@ echo "libnut enabled ${libnut-no}" echo "libopencore-amrnb support ${libopencore_amrnb-no}" echo "libopencore-amrwb support ${libopencore_amrwb-no}" echo "libopenjpeg enabled ${libopenjpeg-no}" +echo "librtmp enabled ${librtmp-no}" echo "libschroedinger enabled ${libschroedinger-no}" echo "libspeex enabled ${libspeex-no}" echo "libtheora enabled ${libtheora-no}" diff --git a/libavformat/Makefile b/libavformat/Makefile index 79430982da..3afa02831a 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -274,7 +274,12 @@ OBJS-$(CONFIG_FILE_PROTOCOL) += file.o OBJS-$(CONFIG_GOPHER_PROTOCOL) += gopher.o OBJS-$(CONFIG_HTTP_PROTOCOL) += http.o httpauth.o OBJS-$(CONFIG_PIPE_PROTOCOL) += file.o -OBJS-$(CONFIG_RTMP_PROTOCOL) += rtmpproto.o rtmppkt.o + +# external or internal rtmp +RTMP-OBJS-$(CONFIG_LIBRTMP) = librtmp.o +RTMP-OBJS-$(!CONFIG_LIBRTMP) = rtmpproto.o rtmppkt.o +OBJS-$(CONFIG_RTMP_PROTOCOL) += $(RTMP-OBJS-yes) + OBJS-$(CONFIG_RTP_PROTOCOL) += rtpproto.o OBJS-$(CONFIG_TCP_PROTOCOL) += tcp.o OBJS-$(CONFIG_UDP_PROTOCOL) += udp.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 090d134d73..91dba0a3a6 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -218,6 +218,12 @@ void av_register_all(void) REGISTER_PROTOCOL (HTTP, http); REGISTER_PROTOCOL (PIPE, pipe); REGISTER_PROTOCOL (RTMP, rtmp); +#if CONFIG_LIBRTMP + REGISTER_PROTOCOL (RTMP, rtmpt); + REGISTER_PROTOCOL (RTMP, rtmpe); + REGISTER_PROTOCOL (RTMP, rtmpte); + REGISTER_PROTOCOL (RTMP, rtmps); +#endif REGISTER_PROTOCOL (RTP, rtp); REGISTER_PROTOCOL (TCP, tcp); REGISTER_PROTOCOL (UDP, udp); diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 4ec9452310..258501abcd 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -22,8 +22,8 @@ #define AVFORMAT_AVFORMAT_H #define LIBAVFORMAT_VERSION_MAJOR 52 -#define LIBAVFORMAT_VERSION_MINOR 57 -#define LIBAVFORMAT_VERSION_MICRO 1 +#define LIBAVFORMAT_VERSION_MINOR 58 +#define LIBAVFORMAT_VERSION_MICRO 0 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ diff --git a/libavformat/librtmp.c b/libavformat/librtmp.c new file mode 100644 index 0000000000..82f1bc8362 --- /dev/null +++ b/libavformat/librtmp.c @@ -0,0 +1,206 @@ +/* + * RTMP network protocol + * Copyright (c) 2010 Howard Chu + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file libavformat/librtmp.c + * RTMP protocol based on http://rtmpdump.mplayerhq.hu librtmp + */ + +#include "avformat.h" + +#include +#include + +static int rtmp_close(URLContext *s) +{ + RTMP *r = s->priv_data; + + RTMP_Close(r); + av_free(r); + return 0; +} + +/** + * Opens RTMP connection and verifies that the stream can be played. + * + * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]... + * where 'app' is first one or two directories in the path + * (e.g. /ondemand/, /flash/live/, etc.) + * and 'playpath' is a file name (the rest of the path, + * may be prefixed with "mp4:") + * + * Additional RTMP library options may be appended as + * space-separated key-value pairs. + */ +static int rtmp_open(URLContext *s, const char *uri, int flags) +{ + RTMP *r; + int rc; + + r = av_mallocz(sizeof(RTMP)); + if (!r) + return AVERROR(ENOMEM); + + switch(av_log_get_level()) { + default: + case AV_LOG_FATAL: rc = RTMP_LOGCRIT; break; + case AV_LOG_ERROR: rc = RTMP_LOGERROR; break; + case AV_LOG_WARNING: rc = RTMP_LOGWARNING; break; + case AV_LOG_INFO: rc = RTMP_LOGINFO; break; + case AV_LOG_VERBOSE: rc = RTMP_LOGDEBUG; break; + case AV_LOG_DEBUG: rc = RTMP_LOGDEBUG2; break; + } + RTMP_LogSetLevel(rc); + + RTMP_Init(r); + if (!RTMP_SetupURL(r, s->filename)) { + rc = -1; + goto fail; + } + + if (flags & URL_WRONLY) + r->Link.protocol |= RTMP_FEATURE_WRITE; + + if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) { + rc = -1; + goto fail; + } + + s->priv_data = r; + s->is_streamed = 1; + return 0; +fail: + av_free(r); + return rc; +} + +static int rtmp_write(URLContext *s, uint8_t *buf, int size) +{ + RTMP *r = s->priv_data; + + return RTMP_Write(r, buf, size); +} + +static int rtmp_read(URLContext *s, uint8_t *buf, int size) +{ + RTMP *r = s->priv_data; + + return RTMP_Read(r, buf, size); +} + +static int rtmp_read_pause(URLContext *s, int pause) +{ + RTMP *r = s->priv_data; + + if (pause) + r->m_pauseStamp = + r->m_channelTimestamp[r->m_mediaChannel]; + if (!RTMP_SendPause(r, pause, r->m_pauseStamp)) + return -1; + return 0; +} + +static int64_t rtmp_read_seek(URLContext *s, int stream_index, + int64_t timestamp, int flags) +{ + RTMP *r = s->priv_data; + + if (flags & AVSEEK_FLAG_BYTE) + return AVERROR_NOTSUPP; + + /* seeks are in milliseconds */ + timestamp = av_rescale(timestamp, AV_TIME_BASE, 1000); + if (!RTMP_SendSeek(r, timestamp)) + return -1; + return timestamp; +} + +static int rtmp_get_file_handle(URLContext *s) +{ + RTMP *r = s->priv_data; + + return r->m_sb.sb_socket; +} + +URLProtocol rtmp_protocol = { + "rtmp", + rtmp_open, + rtmp_read, + rtmp_write, + NULL, /* seek */ + rtmp_close, + NULL, /* next */ + rtmp_read_pause, + rtmp_read_seek, + rtmp_get_file_handle +}; + +URLProtocol rtmpt_protocol = { + "rtmpt", + rtmp_open, + rtmp_read, + rtmp_write, + NULL, /* seek */ + rtmp_close, + NULL, /* next */ + rtmp_read_pause, + rtmp_read_seek, + rtmp_get_file_handle +}; + +URLProtocol rtmpe_protocol = { + "rtmpe", + rtmp_open, + rtmp_read, + rtmp_write, + NULL, /* seek */ + rtmp_close, + NULL, /* next */ + rtmp_read_pause, + rtmp_read_seek, + rtmp_get_file_handle +}; + +URLProtocol rtmpte_protocol = { + "rtmpte", + rtmp_open, + rtmp_read, + rtmp_write, + NULL, /* seek */ + rtmp_close, + NULL, /* next */ + rtmp_read_pause, + rtmp_read_seek, + rtmp_get_file_handle +}; + +URLProtocol rtmps_protocol = { + "rtmps", + rtmp_open, + rtmp_read, + rtmp_write, + NULL, /* seek */ + rtmp_close, + NULL, /* next */ + rtmp_read_pause, + rtmp_read_seek, + rtmp_get_file_handle +};