rtmp: Add RTMP source plugin

https://bugzilla.gnome.org/show_bug.cgi?id=566604
This commit is contained in:
Bastien Nocera 2010-06-02 00:45:06 +01:00 committed by Sebastian Dröge
parent 581b63c678
commit b87668e143
18 changed files with 9454 additions and 0 deletions

View file

@ -1707,6 +1707,7 @@ gst/pnm/Makefile
gst/qtmux/Makefile
gst/rawparse/Makefile
gst/real/Makefile
gst/rtmp/Makefile
gst/rtpmux/Makefile
gst/scaletempo/Makefile
gst/sdp/Makefile

25
gst/rtmp/Makefile.am Normal file
View file

@ -0,0 +1,25 @@
plugin_LTLIBRARIES = libgstrtmp.la
libgstrtmp_la_SOURCES = amf.c \
gstrtmpsrc.c \
hashswf.c \
log.c \
parseurl.c \
rtmp.c
noinst_HEADERS = amf.h \
bytes.h \
dhgroups.h \
dh.h \
gstrtmpsrc.h \
handshake.h \
http.h \
log.h \
rtmp.h \
rtmp_sys.h
libgstrtmp_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) -DCRYPTO
libgstrtmp_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) -lssl $(LIBM)
libgstrtmp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstrtmp_la_LIBTOOLFLAGS = --tag=disable-static

1127
gst/rtmp/amf.c Normal file

File diff suppressed because it is too large Load diff

168
gst/rtmp/amf.h Normal file
View file

@ -0,0 +1,168 @@
#ifndef __AMF_H__
#define __AMF_H__
/*
* Copyright (C) 2005-2008 Team XBMC
* http://www.xbmc.org
* Copyright (C) 2008-2009 Andrej Stepanchuk
* Copyright (C) 2009-2010 Howard Chu
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
#include <stdint.h>
#ifdef _XBOX
#ifndef __cplusplus
#define bool _Bool
typedef unsigned char _Bool;
#define false 0
#define true 1
#endif
#else
#include <stdbool.h>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
typedef enum
{ AMF_NUMBER = 0, AMF_BOOLEAN, AMF_STRING, AMF_OBJECT,
AMF_MOVIECLIP, /* reserved, not used */
AMF_NULL, AMF_UNDEFINED, AMF_REFERENCE, AMF_ECMA_ARRAY, AMF_OBJECT_END,
AMF_STRICT_ARRAY, AMF_DATE, AMF_LONG_STRING, AMF_UNSUPPORTED,
AMF_RECORDSET, /* reserved, not used */
AMF_XML_DOC, AMF_TYPED_OBJECT,
AMF_AVMPLUS, /* switch to AMF3 */
AMF_INVALID = 0xff
} AMFDataType;
typedef enum
{ AMF3_UNDEFINED = 0, AMF3_NULL, AMF3_FALSE, AMF3_TRUE,
AMF3_INTEGER, AMF3_DOUBLE, AMF3_STRING, AMF3_XML_DOC, AMF3_DATE,
AMF3_ARRAY, AMF3_OBJECT, AMF3_XML, AMF3_BYTE_ARRAY
} AMF3DataType;
typedef struct AVal
{
char *av_val;
int av_len;
} AVal;
#define AVC(str) {(char *) str,sizeof(str)-1}
#define AVMATCH(a1,a2) ((a1)->av_len == (a2)->av_len && !memcmp((a1)->av_val,(a2)->av_val,(a1)->av_len))
struct AMFObjectProperty;
typedef struct AMFObject
{
int o_num;
struct AMFObjectProperty *o_props;
} AMFObject;
typedef struct AMFObjectProperty
{
AVal p_name;
AMFDataType p_type;
union
{
double p_number;
AVal p_aval;
AMFObject p_object;
} p_vu;
int16_t p_UTCoffset;
} AMFObjectProperty;
char *AMF_EncodeString(char *output, char *outend, const AVal * str);
char *AMF_EncodeNumber(char *output, char *outend, double dVal);
char *AMF_EncodeInt16(char *output, char *outend, short nVal);
char *AMF_EncodeInt24(char *output, char *outend, int nVal);
char *AMF_EncodeInt32(char *output, char *outend, int nVal);
char *AMF_EncodeBoolean(char *output, char *outend, bool bVal);
/* Shortcuts for AMFProp_Encode */
char *AMF_EncodeNamedString(char *output, char *outend, const AVal * name, const AVal * value);
char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal * name, double dVal);
char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal * name, bool bVal);
unsigned short AMF_DecodeInt16(const char *data);
unsigned int AMF_DecodeInt24(const char *data);
unsigned int AMF_DecodeInt32(const char *data);
void AMF_DecodeString(const char *data, AVal * str);
void AMF_DecodeLongString(const char *data, AVal * str);
bool AMF_DecodeBoolean(const char *data);
double AMF_DecodeNumber(const char *data);
char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd);
int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize,
bool bDecodeName);
int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize,
int nArrayLen, bool bDecodeName);
int AMF3_Decode(AMFObject * obj, const char *pBuffer, int nSize,
bool bDecodeName);
void AMF_Dump(AMFObject * obj);
void AMF_Reset(AMFObject * obj);
void AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop);
int AMF_CountProp(AMFObject * obj);
AMFObjectProperty *AMF_GetProp(AMFObject * obj, const AVal * name,
int nIndex);
AMFDataType AMFProp_GetType(AMFObjectProperty * prop);
void AMFProp_SetNumber(AMFObjectProperty * prop, double dval);
void AMFProp_SetBoolean(AMFObjectProperty * prop, bool bflag);
void AMFProp_SetString(AMFObjectProperty * prop, AVal * str);
void AMFProp_SetObject(AMFObjectProperty * prop, AMFObject * obj);
void AMFProp_GetName(AMFObjectProperty * prop, AVal * name);
void AMFProp_SetName(AMFObjectProperty * prop, AVal * name);
double AMFProp_GetNumber(AMFObjectProperty * prop);
bool AMFProp_GetBoolean(AMFObjectProperty * prop);
void AMFProp_GetString(AMFObjectProperty * prop, AVal * str);
void AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj);
bool AMFProp_IsValid(AMFObjectProperty * prop);
char *AMFProp_Encode(AMFObjectProperty * prop, char *pBuffer, char *pBufEnd);
int AMF3Prop_Decode(AMFObjectProperty * prop, const char *pBuffer,
int nSize, bool bDecodeName);
int AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer,
int nSize, bool bDecodeName);
void AMFProp_Dump(AMFObjectProperty * prop);
void AMFProp_Reset(AMFObjectProperty * prop);
typedef struct AMF3ClassDef
{
AVal cd_name;
char cd_externalizable;
char cd_dynamic;
int cd_num;
AVal *cd_props;
} AMF3ClassDef;
void AMF3CD_AddProp(AMF3ClassDef * cd, AVal * prop);
AVal *AMF3CD_GetProp(AMF3ClassDef * cd, int idx);
#ifdef __cplusplus
}
#endif
#endif /* __AMF_H__ */

90
gst/rtmp/bytes.h Normal file
View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 2005-2008 Team XBMC
* http://www.xbmc.org
* Copyright (C) 2008-2009 Andrej Stepanchuk
* Copyright (C) 2009-2010 Howard Chu
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
#ifndef __BYTES_H__
#define __BYTES_H__
#include <stdint.h>
#ifdef _WIN32
/* Windows is little endian only */
#define __LITTLE_ENDIAN 1234
#define __BIG_ENDIAN 4321
#define __BYTE_ORDER __LITTLE_ENDIAN
#define __FLOAT_WORD_ORDER __BYTE_ORDER
typedef unsigned char uint8_t;
#else /* !_WIN32 */
#include <sys/param.h>
#if defined(BYTE_ORDER) && !defined(__BYTE_ORDER)
#define __BYTE_ORDER BYTE_ORDER
#endif
#if defined(BIG_ENDIAN) && !defined(__BIG_ENDIAN)
#define __BIG_ENDIAN BIG_ENDIAN
#endif
#if defined(LITTLE_ENDIAN) && !defined(__LITTLE_ENDIAN)
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#endif
#endif /* !_WIN32 */
/* define default endianness */
#ifndef __LITTLE_ENDIAN
#define __LITTLE_ENDIAN 1234
#endif
#ifndef __BIG_ENDIAN
#define __BIG_ENDIAN 4321
#endif
#ifndef __BYTE_ORDER
#warning "Byte order not defined on your system, assuming little endian!"
#define __BYTE_ORDER __LITTLE_ENDIAN
#endif
/* ok, we assume to have the same float word order and byte order if float word order is not defined */
#ifndef __FLOAT_WORD_ORDER
#warning "Float word order not defined, assuming the same as byte order!"
#define __FLOAT_WORD_ORDER __BYTE_ORDER
#endif
#if !defined(__BYTE_ORDER) || !defined(__FLOAT_WORD_ORDER)
#error "Undefined byte or float word order!"
#endif
#if __FLOAT_WORD_ORDER != __BIG_ENDIAN && __FLOAT_WORD_ORDER != __LITTLE_ENDIAN
#error "Unknown/unsupported float word order!"
#endif
#if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN
#error "Unknown/unsupported byte order!"
#endif
#endif

333
gst/rtmp/dh.h Normal file
View file

@ -0,0 +1,333 @@
/* RTMPDump - Diffie-Hellmann Key Exchange
* Copyright (C) 2009 Andrej Stepanchuk
* Copyright (C) 2009-2010 Howard Chu
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#ifdef USE_POLARSSL
#include <polarssl/dhm.h>
typedef mpi * MP_t;
#define MP_new(m) m = malloc(sizeof(mpi)); mpi_init(m, NULL)
#define MP_set_w(mpi, w) mpi_lset(mpi, w)
#define MP_cmp(u, v) mpi_cmp_mpi(u, v)
#define MP_set(u, v) mpi_copy(u, v)
#define MP_sub_w(mpi, w) mpi_sub_int(mpi, mpi, w)
#define MP_cmp_1(mpi) mpi_cmp_int(mpi, 1)
#define MP_modexp(r, y, q, p) mpi_exp_mod(r, y, q, p, NULL)
#define MP_free(mpi) mpi_free(mpi, NULL); free(mpi)
#define MP_gethex(u, hex, res) MP_new(u); res = mpi_read_string(u, 16, hex) == 0
#define MP_bytes(u) mpi_size(u)
#define MP_setbin(u,buf,len) mpi_write_binary(u,buf,len)
#define MP_getbin(u,buf,len) MP_new(u); mpi_read_binary(u,buf,len)
typedef struct MDH {
MP_t p;
MP_t g;
MP_t pub_key;
MP_t priv_key;
long length;
dhm_context ctx;
} MDH;
#define MDH_new() calloc(1,sizeof(MDH))
#define MDH_free(vp) {MDH *dh = vp; dhm_free(&dh->ctx); MP_free(dh->p); MP_free(dh->g); MP_free(dh->pub_key); MP_free(dh->priv_key); free(dh);}
static int MDH_generate_key(MDH *dh)
{
unsigned char out[2];
MP_set(&dh->ctx.P, dh->p);
MP_set(&dh->ctx.G, dh->g);
dh->ctx.len = 128;
dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs);
MP_new(dh->pub_key);
MP_new(dh->priv_key);
MP_set(dh->pub_key, &dh->ctx.GX);
MP_set(dh->priv_key, &dh->ctx.X);
return 1;
}
static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh)
{
int n = len;
MP_set(&dh->ctx.GY, pub);
dhm_calc_secret(&dh->ctx, secret, &n);
return 0;
}
#elif defined(USE_GNUTLS)
#include <gcrypt.h>
typedef gcry_mpi_t MP_t;
#define MP_new(m) m = gcry_mpi_new(1)
#define MP_set_w(mpi, w) gcry_mpi_set_ui(mpi, w)
#define MP_cmp(u, v) gcry_mpi_cmp(u, v)
#define MP_set(u, v) gcry_mpi_set(u, v)
#define MP_sub_w(mpi, w) gcry_mpi_sub_ui(mpi, mpi, w)
#define MP_cmp_1(mpi) gcry_mpi_cmp_ui(mpi, 1)
#define MP_modexp(r, y, q, p) gcry_mpi_powm(r, y, q, p)
#define MP_free(mpi) gcry_mpi_release(mpi)
#define MP_gethex(u, hex, res) res = (gcry_mpi_scan(&u, GCRYMPI_FMT_HEX, hex, 0, 0) == 0)
#define MP_bytes(u) (gcry_mpi_get_nbits(u) + 7) / 8
#define MP_setbin(u,buf,len) gcry_mpi_print(GCRYMPI_FMT_USG,buf,len,NULL,u)
#define MP_getbin(u,buf,len) gcry_mpi_scan(&u,GCRYMPI_FMT_USG,buf,len,NULL)
typedef struct MDH {
MP_t p;
MP_t g;
MP_t pub_key;
MP_t priv_key;
long length;
} MDH;
#define MDH_new() calloc(1,sizeof(MDH))
#define MDH_free(dh) do {MP_free(((MDH*)(dh))->p); MP_free(((MDH*)(dh))->g); MP_free(((MDH*)(dh))->pub_key); MP_free(((MDH*)(dh))->priv_key); free(dh);} while(0)
extern MP_t gnutls_calc_dh_secret(MP_t *priv, MP_t g, MP_t p);
extern MP_t gnutls_calc_dh_key(MP_t y, MP_t x, MP_t p);
#define MDH_generate_key(dh) (dh->pub_key = gnutls_calc_dh_secret(&dh->priv_key, dh->g, dh->p))
static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh)
{
MP_t sec = gnutls_calc_dh_key(pub, dh->priv_key, dh->p);
if (sec)
{
MP_setbin(sec, secret, len);
MP_free(sec);
return 0;
}
else
return -1;
}
#else /* USE_OPENSSL */
#include <openssl/bn.h>
#include <openssl/dh.h>
typedef BIGNUM * MP_t;
#define MP_new(m) m = BN_new()
#define MP_set_w(mpi, w) BN_set_word(mpi, w)
#define MP_cmp(u, v) BN_cmp(u, v)
#define MP_set(u, v) BN_copy(u, v)
#define MP_sub_w(mpi, w) BN_sub_word(mpi, w)
#define MP_cmp_1(mpi) BN_cmp(mpi, BN_value_one())
#define MP_modexp(r, y, q, p) do {BN_CTX *ctx = BN_CTX_new(); BN_mod_exp(r, y, q, p, ctx); BN_CTX_free(ctx);} while(0)
#define MP_free(mpi) BN_free(mpi)
#define MP_gethex(u, hex, res) res = BN_hex2bn(&u, hex)
#define MP_bytes(u) BN_num_bytes(u)
#define MP_setbin(u,buf,len) BN_bn2bin(u,buf)
#define MP_getbin(u,buf,len) u = BN_bin2bn(buf,len,0)
#define MDH DH
#define MDH_new() DH_new()
#define MDH_free(dh) DH_free(dh)
#define MDH_generate_key(dh) DH_generate_key(dh)
#define MDH_compute_key(secret, seclen, pub, dh) DH_compute_key(secret, pub, dh)
#endif
#include "log.h"
#include "dhgroups.h"
/* RFC 2631, Section 2.1.5, http://www.ietf.org/rfc/rfc2631.txt */
static bool
isValidPublicKey(MP_t y, MP_t p, MP_t q)
{
int ret = true;
MP_t bn;
assert(y);
MP_new(bn);
assert(bn);
/* y must lie in [2,p-1] */
MP_set_w(bn, 1);
if (MP_cmp(y, bn) < 0)
{
RTMP_Log(RTMP_LOGERROR, "DH public key must be at least 2");
ret = false;
goto failed;
}
/* bn = p-2 */
MP_set(bn, p);
MP_sub_w(bn, 1);
if (MP_cmp(y, bn) > 0)
{
RTMP_Log(RTMP_LOGERROR, "DH public key must be at most p-2");
ret = false;
goto failed;
}
/* Verify with Sophie-Germain prime
*
* This is a nice test to make sure the public key position is calculated
* correctly. This test will fail in about 50% of the cases if applied to
* random data.
*/
if (q)
{
/* y must fulfill y^q mod p = 1 */
MP_modexp(bn, y, q, p);
if (MP_cmp_1(bn) != 0)
{
RTMP_Log(RTMP_LOGWARNING, "DH public key does not fulfill y^q mod p = 1");
}
}
failed:
MP_free(bn);
return ret;
}
static MDH *
DHInit(int nKeyBits)
{
size_t res;
MDH *dh = MDH_new();
if (!dh)
goto failed;
MP_new(dh->g);
if (!dh->g)
goto failed;
MP_gethex(dh->p, P1024, res); /* prime P1024, see dhgroups.h */
if (!res)
{
goto failed;
}
MP_set_w(dh->g, 2); /* base 2 */
dh->length = nKeyBits;
return dh;
failed:
if (dh)
MDH_free(dh);
return 0;
}
static int
DHGenerateKey(MDH *dh)
{
size_t res = 0;
if (!dh)
return 0;
while (!res)
{
MP_t q1 = NULL;
if (!MDH_generate_key(dh))
return 0;
MP_gethex(q1, Q1024, res);
assert(res);
res = isValidPublicKey(dh->pub_key, dh->p, q1);
if (!res)
{
MP_free(dh->pub_key);
MP_free(dh->priv_key);
dh->pub_key = dh->priv_key = 0;
}
MP_free(q1);
}
return 1;
}
/* fill pubkey with the public key in BIG ENDIAN order
* 00 00 00 00 00 x1 x2 x3 .....
*/
static int
DHGetPublicKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen)
{
int len;
if (!dh || !dh->pub_key)
return 0;
len = MP_bytes(dh->pub_key);
if (len <= 0 || len > (int) nPubkeyLen)
return 0;
memset(pubkey, 0, nPubkeyLen);
MP_setbin(dh->pub_key, pubkey + (nPubkeyLen - len), len);
return 1;
}
#if 0 /* unused */
static int
DHGetPrivateKey(MDH *dh, uint8_t *privkey, size_t nPrivkeyLen)
{
if (!dh || !dh->priv_key)
return 0;
int len = MP_bytes(dh->priv_key);
if (len <= 0 || len > (int) nPrivkeyLen)
return 0;
memset(privkey, 0, nPrivkeyLen);
MP_setbin(dh->priv_key, privkey + (nPrivkeyLen - len), len);
return 1;
}
#endif
/* computes the shared secret key from the private MDH value and the
* other party's public key (pubkey)
*/
static int
DHComputeSharedSecretKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen,
uint8_t *secret)
{
MP_t q1 = NULL, pubkeyBn = NULL;
size_t len;
int res;
if (!dh || !secret || nPubkeyLen >= INT_MAX)
return -1;
MP_getbin(pubkeyBn, pubkey, nPubkeyLen);
if (!pubkeyBn)
return -1;
MP_gethex(q1, Q1024, len);
assert(len);
if (isValidPublicKey(pubkeyBn, dh->p, q1))
res = MDH_compute_key(secret, nPubkeyLen, pubkeyBn, dh);
else
res = -1;
MP_free(q1);
MP_free(pubkeyBn);
return res;
}

198
gst/rtmp/dhgroups.h Normal file
View file

@ -0,0 +1,198 @@
/* librtmp - Diffie-Hellmann Key Exchange
* Copyright (C) 2009 Andrej Stepanchuk
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
/* from RFC 3526, see http://www.ietf.org/rfc/rfc3526.txt */
/* 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } */
#define P768 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"
/* 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } */
#define P1024 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
"FFFFFFFFFFFFFFFF"
/* Group morder largest prime factor: */
#define Q1024 \
"7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \
"948127044533E63A0105DF531D89CD9128A5043CC71A026E" \
"F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \
"F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \
"F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \
"FFFFFFFFFFFFFFFF"
/* 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } */
#define P1536 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
/* 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } */
#define P2048 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
"15728E5A8AACAA68FFFFFFFFFFFFFFFF"
/* 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } */
#define P3072 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
"43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"
/* 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } */
#define P4096 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \
"FFFFFFFFFFFFFFFF"
/* 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } */
#define P6144 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \
"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \
"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \
"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \
"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \
"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \
"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \
"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \
"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \
"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \
"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \
"12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF"
/* 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } */
#define P8192 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \
"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \
"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \
"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \
"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \
"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \
"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \
"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \
"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \
"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \
"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \
"12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \
"38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \
"741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \
"3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \
"22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \
"4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \
"062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \
"4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \
"B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \
"4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \
"9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \
"60C980DD98EDD3DFFFFFFFFFFFFFFFFF"

652
gst/rtmp/gstrtmpsrc.c Normal file
View file

@ -0,0 +1,652 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2001 Bastien Nocera <hadess@hadess.net>
* 2002 Kristian Rietveld <kris@gtk.org>
* 2002,2003 Colin Walters <walters@gnu.org>
*
* rtmpsrc.c:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-rtmpsrc
*
* This plugin reads data from a local or remote location specified
* by an URI. This location can be specified using any protocol supported by
* the RTMP library. Common protocols are 'file', 'http', 'ftp', or 'smb'.
*
* In case the #GstRTMPSrc:iradio-mode property is set and the
* location is a http resource, rtmpsrc will send special icecast http
* headers to the server to request additional icecast metainformation. If
* the server is not an icecast server, it will display the same behaviour
* as if the #GstRTMPSrc:iradio-mode property was not set. However,
* if the server is in fact an icecast server, rtmpsrc will output
* data with a media type of application/x-icy, in which case you will
* need to use the #GstICYDemux element as follow-up element to extract
* the icecast meta data and to determine the underlying media type.
*
* <refsect2>
* <title>Example launch lines</title>
* |[
* gst-launch -v rtmpsrc location=file:///home/joe/foo.xyz ! fakesink
* ]| The above pipeline will simply read a local file and do nothing with the
* data read. Instead of rtmpsrc, we could just as well have used the
* filesrc element here.
* |[
* gst-launch -v rtmpsrc location=smb://othercomputer/foo.xyz ! filesink location=/home/joe/foo.xyz
* ]| The above pipeline will copy a file from a remote host to the local file
* system using the Samba protocol.
* |[
* gst-launch -v rtmpsrc location=http://music.foobar.com/demo.mp3 ! mad ! audioconvert ! audioresample ! alsasink
* ]| The above pipeline will read and decode and play an mp3 file from a
* web server using the http protocol.
* </refsect2>
*/
#define DEFAULT_RTMP_PORT 1935
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib/gi18n-lib.h>
#include "gstrtmpsrc.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <gst/gst.h>
#include <gst/tag/tag.h>
GST_DEBUG_CATEGORY_STATIC (rtmpsrc_debug);
#define GST_CAT_DEFAULT rtmpsrc_debug
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
enum
{
ARG_0,
ARG_LOCATION,
};
static void gst_rtmp_src_base_init (gpointer g_class);
static void gst_rtmp_src_class_init (GstRTMPSrcClass * klass);
static void gst_rtmp_src_init (GstRTMPSrc * rtmpsrc);
static void gst_rtmp_src_finalize (GObject * object);
static void gst_rtmp_src_uri_handler_init (gpointer g_iface,
gpointer iface_data);
static void gst_rtmp_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_rtmp_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_rtmp_src_stop (GstBaseSrc * src);
static gboolean gst_rtmp_src_start (GstBaseSrc * src);
static gboolean gst_rtmp_src_is_seekable (GstBaseSrc * src);
#if 0
static gboolean gst_rtmp_src_check_get_range (GstBaseSrc * src);
static gboolean gst_rtmp_src_get_size (GstBaseSrc * src, guint64 * size);
#endif
static GstFlowReturn gst_rtmp_src_create (GstBaseSrc * basesrc,
guint64 offset, guint size, GstBuffer ** buffer);
#if 0
static gboolean gst_rtmp_src_query (GstBaseSrc * src, GstQuery * query);
#endif
static GstElementClass *parent_class = NULL;
static gboolean
plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "rtmpsrc", GST_RANK_NONE,
GST_TYPE_RTMP_SRC);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"rtmpsrc",
"flvstreamer sources",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
GType
gst_rtmp_src_get_type (void)
{
static GType rtmpsrc_type = 0;
if (!rtmpsrc_type) {
static const GTypeInfo rtmpsrc_info = {
sizeof (GstRTMPSrcClass),
gst_rtmp_src_base_init,
NULL,
(GClassInitFunc) gst_rtmp_src_class_init,
NULL,
NULL,
sizeof (GstRTMPSrc),
0,
(GInstanceInitFunc) gst_rtmp_src_init,
};
static const GInterfaceInfo urihandler_info = {
gst_rtmp_src_uri_handler_init,
NULL,
NULL
};
rtmpsrc_type =
g_type_register_static (GST_TYPE_BASE_SRC,
"GstRTMPSrc", &rtmpsrc_info, (GTypeFlags) 0);
g_type_add_interface_static (rtmpsrc_type, GST_TYPE_URI_HANDLER,
&urihandler_info);
}
return rtmpsrc_type;
}
static void
gst_rtmp_src_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&srctemplate));
gst_element_class_set_details_simple (element_class,
"RTMP Source",
"Source/File",
"Read RTMP streams",
"Bastien Nocera <hadess@hadess.net>\n"
"GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
GST_DEBUG_CATEGORY_INIT (rtmpsrc_debug, "rtmpsrc", 0, "RTMP Source");
}
static void
gst_rtmp_src_class_init (GstRTMPSrcClass * klass)
{
GObjectClass *gobject_class;
GstBaseSrcClass *gstbasesrc_class;
gobject_class = G_OBJECT_CLASS (klass);
gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
parent_class = (GstElementClass *) g_type_class_peek_parent (klass);
gobject_class->finalize = gst_rtmp_src_finalize;
gobject_class->set_property = gst_rtmp_src_set_property;
gobject_class->get_property = gst_rtmp_src_get_property;
/* properties */
gst_element_class_install_std_props (GST_ELEMENT_CLASS (klass),
"location", ARG_LOCATION, G_PARAM_READWRITE, NULL);
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_rtmp_src_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_rtmp_src_stop);
#if 0
gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_rtmp_src_get_size);
#endif
gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_rtmp_src_is_seekable);
#if 0
gstbasesrc_class->check_get_range =
GST_DEBUG_FUNCPTR (gst_rtmp_src_check_get_range);
#endif
gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_rtmp_src_create);
#if 0
gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_rtmp_src_query);
#endif
}
static void
gst_rtmp_src_init (GstRTMPSrc * rtmpsrc)
{
rtmpsrc->curoffset = 0;
rtmpsrc->seekable = FALSE;
}
static void
gst_rtmp_src_finalize (GObject * object)
{
GstRTMPSrc *rtmpsrc = GST_RTMP_SRC (object);
g_free (rtmpsrc->uri);
rtmpsrc->uri = NULL;
if (rtmpsrc->rtmp) {
RTMP_Close (rtmpsrc->rtmp);
RTMP_Free (rtmpsrc->rtmp);
rtmpsrc->rtmp = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/*
* URI interface support.
*/
static GstURIType
gst_rtmp_src_uri_get_type (void)
{
return GST_URI_SRC;
}
static gchar **
gst_rtmp_src_uri_get_protocols (void)
{
static gchar *protocols[] = { (char *) "rtmp", NULL };
return protocols;
}
static const gchar *
gst_rtmp_src_uri_get_uri (GstURIHandler * handler)
{
GstRTMPSrc *src = GST_RTMP_SRC (handler);
return src->uri;
}
static gboolean
gst_rtmp_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
{
GstRTMPSrc *src = GST_RTMP_SRC (handler);
if (GST_STATE (src) == GST_STATE_PLAYING ||
GST_STATE (src) == GST_STATE_PAUSED)
return FALSE;
g_object_set (G_OBJECT (src), "location", uri, NULL);
g_message ("just set uri to %s", uri);
return TRUE;
}
static void
gst_rtmp_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
iface->get_type = gst_rtmp_src_uri_get_type;
iface->get_protocols = gst_rtmp_src_uri_get_protocols;
iface->get_uri = gst_rtmp_src_uri_get_uri;
iface->set_uri = gst_rtmp_src_uri_set_uri;
}
static void
gst_rtmp_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstRTMPSrc *src;
src = GST_RTMP_SRC (object);
switch (prop_id) {
case ARG_LOCATION:{
char *new_location;
/* the element must be stopped or paused in order to do this */
if (GST_STATE (src) == GST_STATE_PLAYING ||
GST_STATE (src) == GST_STATE_PAUSED)
break;
g_free (src->uri);
src->uri = NULL;
if (src->rtmp) {
RTMP_Close (src->rtmp);
RTMP_Free (src->rtmp);
src->rtmp = NULL;
}
new_location = g_value_dup_string (value);
src->rtmp = RTMP_Alloc ();
RTMP_Init (src->rtmp);
if (!RTMP_SetupURL (src->rtmp, new_location)) {
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, NULL,
("Failed to setup URL '%s'", src->uri));
g_free (new_location);
RTMP_Free (src->rtmp);
src->rtmp = NULL;
} else {
src->uri = g_value_dup_string (value);
g_message ("parsed uri '%s' properly", src->uri);
}
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_rtmp_src_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstRTMPSrc *src;
src = GST_RTMP_SRC (object);
switch (prop_id) {
case ARG_LOCATION:
g_value_set_string (value, src->uri);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/*
* Read a new buffer from src->reqoffset, takes care of events
* and seeking and such.
*/
static GstFlowReturn
gst_rtmp_src_create (GstBaseSrc * basesrc, guint64 offset, guint size,
GstBuffer ** buffer)
{
GstRTMPSrc *src;
GstBuffer *buf;
guint8 *data;
guint todo;
int read;
src = GST_RTMP_SRC (basesrc);
g_return_val_if_fail (src->rtmp != NULL, GST_FLOW_ERROR);
GST_DEBUG ("now at %" G_GINT64_FORMAT ", reading from %" G_GUINT64_FORMAT
", size %u", src->curoffset, offset, size);
/* open if required */
if (G_UNLIKELY (!RTMP_IsConnected (src->rtmp))) {
if (!RTMP_Connect (src->rtmp, NULL)) {
GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
("Could not connect to RTMP stream \"%s\" for reading: %s (%d)",
src->uri, "FIXME", 0));
return GST_FLOW_ERROR;
}
}
/* seek if required */
if (G_UNLIKELY (src->curoffset != offset)) {
GST_DEBUG ("need to seek");
if (src->seekable) {
#if 0
GST_DEBUG ("seeking to %" G_GUINT64_FORMAT, offset);
res = rtmp_seek (src->handle, RTMP_SEEK_START, offset);
if (res != RTMP_OK)
goto seek_failed;
src->curoffset = offset;
#endif
} else {
goto cannot_seek;
}
}
buf = gst_buffer_try_new_and_alloc (size);
if (G_UNLIKELY (buf == NULL && size == 0)) {
GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", size);
return GST_FLOW_ERROR;
}
data = GST_BUFFER_DATA (buf);
/* FIXME add FLV header first time around? */
read = 0;
todo = size;
while (todo > 0) {
read = RTMP_Read (src->rtmp, (char *) &data, todo);
if (G_UNLIKELY (read == -1))
goto eos;
if (G_UNLIKELY (read == -2))
goto read_failed;
/* FIXME handle -3 ? */
if (read < todo) {
data = &data[read];
todo -= read;
} else {
todo = 0;
}
GST_LOG (" got size %" G_GUINT64_FORMAT, read);
}
GST_BUFFER_OFFSET (buf) = src->curoffset;
src->curoffset += size;
/* we're done, return the buffer */
*buffer = buf;
#if 0
RTMPFileSize readbytes;
guint todo;
return GST_FLOW_OK;
#endif
return GST_FLOW_OK;
//seek_failed:
{
GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL),
("Failed to seek to requested position %" G_GINT64_FORMAT ": %s",
offset, "FIXME"));
return GST_FLOW_ERROR;
}
cannot_seek:
{
GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL),
("Requested seek from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT
" on non-seekable stream", src->curoffset, offset));
return GST_FLOW_ERROR;
}
read_failed:
{
gst_buffer_unref (buf);
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
("Failed to read data: %s", "FIXME"));
return GST_FLOW_ERROR;
}
eos:
{
gst_buffer_unref (buf);
GST_DEBUG_OBJECT (src, "Reading data gave EOS");
return GST_FLOW_UNEXPECTED;
}
}
#if 0
static gboolean
gst_rtmp_src_query (GstBaseSrc * basesrc, GstQuery * query)
{
gboolean ret = FALSE;
GstRTMPSrc *src = GST_RTMP_SRC (basesrc);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_URI:
gst_query_set_uri (query, src->uri);
ret = TRUE;
break;
default:
ret = FALSE;
break;
}
if (!ret)
ret = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
return ret;
}
#endif
static gboolean
gst_rtmp_src_is_seekable (GstBaseSrc * basesrc)
{
GstRTMPSrc *src;
src = GST_RTMP_SRC (basesrc);
return src->seekable;
}
#if 0
static gboolean
gst_rtmp_src_check_get_range (GstBaseSrc * basesrc)
{
GstRTMPSrc *src;
const gchar *protocol;
src = GST_RTMP_SRC (basesrc);
if (src->uri == NULL) {
GST_WARNING_OBJECT (src, "no URI set yet");
return FALSE;
}
if (rtmp_uri_is_local (src->uri)) {
GST_LOG_OBJECT (src, "local URI (%s), assuming random access is possible",
GST_STR_NULL (src->uri_name));
return TRUE;
}
/* blacklist certain protocols we know won't work getrange-based */
protocol = rtmp_uri_get_scheme (src->uri);
if (protocol == NULL)
goto undecided;
if (strcmp (protocol, "http") == 0 || strcmp (protocol, "https") == 0) {
GST_LOG_OBJECT (src, "blacklisted protocol '%s', no random access possible"
" (URI=%s)", protocol, GST_STR_NULL (src->uri_name));
return FALSE;
}
/* fall through to undecided */
undecided:
{
/* don't know what to do, let the basesrc class decide for us */
GST_LOG_OBJECT (src, "undecided about URI '%s', let base class handle it",
GST_STR_NULL (src->uri_name));
if (GST_BASE_SRC_CLASS (parent_class)->check_get_range)
return GST_BASE_SRC_CLASS (parent_class)->check_get_range (basesrc);
return FALSE;
}
}
#endif
#if 0
static gboolean
gst_rtmp_src_get_size (GstBaseSrc * basesrc, guint64 * size)
{
GstRTMPSrc *src;
RTMPFileInfo *info;
RTMPFileInfoOptions options;
RTMPResult res;
src = GST_RTMP_SRC (basesrc);
*size = -1;
info = rtmp_file_info_new ();
options = RTMP_FILE_INFO_DEFAULT | RTMP_FILE_INFO_FOLLOW_LINKS;
res = rtmp_get_file_info_from_handle (src->handle, info, options);
if (res == RTMP_OK) {
if ((info->valid_fields & RTMP_FILE_INFO_FIELDS_SIZE) != 0) {
*size = info->size;
GST_DEBUG_OBJECT (src, "from handle: %" G_GUINT64_FORMAT " bytes", *size);
} else if (src->own_handle && rtmp_uri_is_local (src->uri)) {
GST_DEBUG_OBJECT (src,
"file size not known, file local, trying fallback");
res = rtmp_get_file_info_uri (src->uri, info, options);
if (res == RTMP_OK &&
(info->valid_fields & RTMP_FILE_INFO_FIELDS_SIZE) != 0) {
*size = info->size;
GST_DEBUG_OBJECT (src, "from uri: %" G_GUINT64_FORMAT " bytes", *size);
}
}
} else {
GST_WARNING_OBJECT (src, "getting info failed: %s",
rtmp_result_to_string (res));
}
rtmp_file_info_unref (info);
if (*size == (RTMPFileSize) - 1)
return FALSE;
GST_DEBUG_OBJECT (src, "return size %" G_GUINT64_FORMAT, *size);
return TRUE;
}
#endif
/* open the file, do stuff necessary to go to PAUSED state */
static gboolean
gst_rtmp_src_start (GstBaseSrc * basesrc)
{
GstRTMPSrc *src;
src = GST_RTMP_SRC (basesrc);
g_message ("start called!");
if (!src->uri) {
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given"));
return FALSE;
}
return TRUE;
}
static gboolean
gst_rtmp_src_stop (GstBaseSrc * basesrc)
{
GstRTMPSrc *src;
src = GST_RTMP_SRC (basesrc);
//FIXME you can't run RTMP_Close multiple times
// RTMP_Close (src->rtmp);
g_message ("stop called!");
src->curoffset = 0;
return TRUE;
}
/*
* vim: sw=2 ts=8 cindent noai bs=2
*/

76
gst/rtmp/gstrtmpsrc.h Normal file
View file

@ -0,0 +1,76 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2001 Bastien Nocera <hadess@hadess.net>
* 2002 Kristian Rietveld <kris@gtk.org>
* 2002,2003 Colin Walters <walters@gnu.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_RTMP_SRC_H__
#define __GST_RTMP_SRC_H__
#include <gst/base/gstbasesrc.h>
#include "rtmp.h"
#include "log.h"
#include "amf.h"
G_BEGIN_DECLS
#define GST_TYPE_RTMP_SRC \
(gst_rtmp_src_get_type())
#define GST_RTMP_SRC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTMP_SRC,GstRTMPSrc))
#define GST_RTMP_SRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTMP_SRC,GstRTMPSrcClass))
#define GST_IS_RTMP_SRC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTMP_SRC))
#define GST_IS_RTMP_SRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTMP_SRC))
typedef struct _GstRTMPSrc GstRTMPSrc;
typedef struct _GstRTMPSrcClass GstRTMPSrcClass;
/**
* GstRTMPSrc:
*
* Opaque data structure.
*/
struct _GstRTMPSrc
{
GstBaseSrc basesrc;
char *uri;
RTMP *rtmp;
gboolean seekable;
gint64 curoffset;
};
struct _GstRTMPSrcClass
{
GstBaseSrcClass basesrc_class;
};
GType gst_rtmp_src_get_type (void);
G_END_DECLS
#endif /* __GST_RTMP_SRC_H__ */

1089
gst/rtmp/handshake.h Normal file

File diff suppressed because it is too large Load diff

608
gst/rtmp/hashswf.c Normal file
View file

@ -0,0 +1,608 @@
/*
* Copyright (C) 2009-2010 Howard Chu
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "rtmp_sys.h"
#include "log.h"
#include "http.h"
#ifdef CRYPTO
#ifdef USE_POLARSSL
#include <polarssl/sha2.h>
#ifndef SHA256_DIGEST_LENGTH
#define SHA256_DIGEST_LENGTH 32
#endif
#define HMAC_CTX sha2_context
#define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0)
#define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len)
#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig)
#define HMAC_close(ctx)
#elif defined(USE_GNUTLS)
#include <gnutls/gnutls.h>
#include <gcrypt.h>
#ifndef SHA256_DIGEST_LENGTH
#define SHA256_DIGEST_LENGTH 32
#endif
#define HMAC_CTX gcry_md_hd_t
#define HMAC_setup(ctx, key, len) gcry_md_open(&ctx, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); gcry_md_setkey(ctx, key, len)
#define HMAC_crunch(ctx, buf, len) gcry_md_write(ctx, buf, len)
#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; memcpy(dig, gcry_md_read(ctx, 0), dlen)
#define HMAC_close(ctx) gcry_md_close(ctx)
#else /* USE_OPENSSL */
#include <openssl/ssl.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/rc4.h>
#define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0)
#define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len)
#define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen);
#define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx)
#endif
extern void RTMP_TLS_Init ();
extern TLS_CTX RTMP_TLS_ctx;
#endif /* CRYPTO */
#include <zlib.h>
#define AGENT "Mozilla/5.0"
HTTPResult
HTTP_get (struct HTTP_ctx *http, const char *url, HTTP_read_callback * cb)
{
char *host, *path;
char *p1, *p2;
char hbuf[256];
int port = 80;
#ifdef CRYPTO
int ssl = 0;
#endif
int hlen, flen = 0;
int rc, i;
int len_known;
HTTPResult ret = HTTPRES_OK;
struct sockaddr_in sa;
RTMPSockBuf sb = { 0 };
http->status = -1;
memset (&sa, 0, sizeof (struct sockaddr_in));
sa.sin_family = AF_INET;
/* we only handle http here */
if (strncasecmp (url, "http", 4))
return HTTPRES_BAD_REQUEST;
if (url[4] == 's') {
#ifdef CRYPTO
ssl = 1;
port = 443;
if (!RTMP_TLS_ctx)
RTMP_TLS_Init ();
#else
return HTTPRES_BAD_REQUEST;
#endif
}
p1 = strchr (url + 4, ':');
if (!p1 || strncmp (p1, "://", 3))
return HTTPRES_BAD_REQUEST;
host = p1 + 3;
path = strchr (host, '/');
hlen = path - host;
strncpy (hbuf, host, hlen);
hbuf[hlen] = '\0';
host = hbuf;
p1 = strrchr (host, ':');
if (p1) {
*p1++ = '\0';
port = atoi (p1);
}
sa.sin_addr.s_addr = inet_addr (host);
if (sa.sin_addr.s_addr == INADDR_NONE) {
struct hostent *hp = gethostbyname (host);
if (!hp || !hp->h_addr)
return HTTPRES_LOST_CONNECTION;
sa.sin_addr = *(struct in_addr *) hp->h_addr;
}
sa.sin_port = htons (port);
sb.sb_socket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sb.sb_socket == -1)
return HTTPRES_LOST_CONNECTION;
i = sprintf (sb.sb_buf,
"GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferrer: %.*s\r\n",
path, AGENT, host, (int) (path - url + 1), url);
if (http->date[0])
i += sprintf (sb.sb_buf + i, "If-Modified-Since: %s\r\n", http->date);
i += sprintf (sb.sb_buf + i, "\r\n");
if (connect
(sb.sb_socket, (struct sockaddr *) &sa, sizeof (struct sockaddr)) < 0) {
ret = HTTPRES_LOST_CONNECTION;
goto leave;
}
#ifdef CRYPTO
if (ssl) {
#ifdef NO_SSL
RTMP_Log (RTMP_LOGERROR, "%s, No SSL/TLS support", __FUNCTION__);
ret = HTTPRES_BAD_REQUEST;
goto leave;
#else
TLS_client (RTMP_TLS_ctx, sb.sb_ssl);
TLS_setfd (sb.sb_ssl, sb.sb_socket);
if ((i = TLS_connect (sb.sb_ssl)) < 0) {
RTMP_Log (RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
ret = HTTPRES_LOST_CONNECTION;
goto leave;
}
#endif
}
#endif
RTMPSockBuf_Send (&sb, sb.sb_buf, i);
/* set timeout */
#define HTTP_TIMEOUT 5
{
SET_RCVTIMEO (tv, HTTP_TIMEOUT);
if (setsockopt
(sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof (tv))) {
RTMP_Log (RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!",
__FUNCTION__, HTTP_TIMEOUT);
}
}
sb.sb_size = 0;
sb.sb_timedout = false;
if (RTMPSockBuf_Fill (&sb) < 1) {
ret = HTTPRES_LOST_CONNECTION;
goto leave;
}
if (strncmp (sb.sb_buf, "HTTP/1", 6)) {
ret = HTTPRES_BAD_REQUEST;
goto leave;
}
p1 = strchr (sb.sb_buf, ' ');
rc = atoi (p1 + 1);
http->status = rc;
if (rc >= 300) {
if (rc == 304) {
ret = HTTPRES_OK_NOT_MODIFIED;
goto leave;
} else if (rc == 404)
ret = HTTPRES_NOT_FOUND;
else if (rc >= 500)
ret = HTTPRES_SERVER_ERROR;
else if (rc >= 400)
ret = HTTPRES_BAD_REQUEST;
else
ret = HTTPRES_REDIRECTED;
}
p1 = memchr (sb.sb_buf, '\n', sb.sb_size);
if (!p1) {
ret = HTTPRES_BAD_REQUEST;
goto leave;
}
sb.sb_start = p1 + 1;
sb.sb_size -= sb.sb_start - sb.sb_buf;
while ((p2 = memchr (sb.sb_start, '\r', sb.sb_size))) {
if (*sb.sb_start == '\r') {
sb.sb_start += 2;
sb.sb_size -= 2;
break;
} else
if (!strncasecmp
(sb.sb_start, "Content-Length: ", sizeof ("Content-Length: ") - 1)) {
flen = atoi (sb.sb_start + sizeof ("Content-Length: ") - 1);
} else
if (!strncasecmp
(sb.sb_start, "Last-Modified: ", sizeof ("Last-Modified: ") - 1)) {
*p2 = '\0';
strcpy (http->date, sb.sb_start + sizeof ("Last-Modified: ") - 1);
}
p2 += 2;
sb.sb_size -= p2 - sb.sb_start;
sb.sb_start = p2;
if (sb.sb_size < 1) {
if (RTMPSockBuf_Fill (&sb) < 1) {
ret = HTTPRES_LOST_CONNECTION;
goto leave;
}
}
}
len_known = flen > 0;
while ((!len_known || flen > 0) &&
(sb.sb_size > 0 || RTMPSockBuf_Fill (&sb) > 0)) {
cb (sb.sb_start, 1, sb.sb_size, http->data);
if (len_known)
flen -= sb.sb_size;
http->size += sb.sb_size;
sb.sb_size = 0;
}
if (flen > 0)
ret = HTTPRES_LOST_CONNECTION;
leave:
RTMPSockBuf_Close (&sb);
return ret;
}
#ifdef CRYPTO
#define CHUNK 16384
struct info
{
z_stream *zs;
HMAC_CTX ctx;
int first;
int zlib;
int size;
};
static size_t
swfcrunch (void *ptr, size_t size, size_t nmemb, void *stream)
{
struct info *i = stream;
char *p = ptr;
size_t len = size * nmemb;
if (i->first) {
i->first = 0;
/* compressed? */
if (!strncmp (p, "CWS", 3)) {
*p = 'F';
i->zlib = 1;
}
HMAC_crunch (i->ctx, (unsigned char *) p, 8);
p += 8;
len -= 8;
i->size = 8;
}
if (i->zlib) {
unsigned char out[CHUNK];
i->zs->next_in = (unsigned char *) p;
i->zs->avail_in = len;
do {
i->zs->avail_out = CHUNK;
i->zs->next_out = out;
inflate (i->zs, Z_NO_FLUSH);
len = CHUNK - i->zs->avail_out;
i->size += len;
HMAC_crunch (i->ctx, out, len);
}
while (i->zs->avail_out == 0);
} else {
i->size += len;
HMAC_crunch (i->ctx, (unsigned char *) p, len);
}
return size * nmemb;
}
static int tzoff;
static int tzchecked;
#define JAN02_1980 318340800
static const char *monthtab[12] = { "Jan", "Feb", "Mar",
"Apr", "May", "Jun",
"Jul", "Aug", "Sep",
"Oct", "Nov", "Dec"
};
static const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
/* Parse an HTTP datestamp into Unix time */
static time_t
make_unix_time (char *s)
{
struct tm time;
int i, ysub = 1900, fmt = 0;
char *month;
char *n;
time_t res;
if (s[3] != ' ') {
fmt = 1;
if (s[3] != ',')
ysub = 0;
}
for (n = s; *n; ++n)
if (*n == '-' || *n == ':')
*n = ' ';
time.tm_mon = 0;
n = strchr (s, ' ');
if (fmt) {
/* Day, DD-MMM-YYYY HH:MM:SS GMT */
time.tm_mday = strtol (n + 1, &n, 0);
month = n + 1;
n = strchr (month, ' ');
time.tm_year = strtol (n + 1, &n, 0);
time.tm_hour = strtol (n + 1, &n, 0);
time.tm_min = strtol (n + 1, &n, 0);
time.tm_sec = strtol (n + 1, NULL, 0);
} else {
/* Unix ctime() format. Does not conform to HTTP spec. */
/* Day MMM DD HH:MM:SS YYYY */
month = n + 1;
n = strchr (month, ' ');
while (isspace (*n))
n++;
time.tm_mday = strtol (n, &n, 0);
time.tm_hour = strtol (n + 1, &n, 0);
time.tm_min = strtol (n + 1, &n, 0);
time.tm_sec = strtol (n + 1, &n, 0);
time.tm_year = strtol (n + 1, NULL, 0);
}
if (time.tm_year > 100)
time.tm_year -= ysub;
for (i = 0; i < 12; i++)
if (!strncasecmp (month, monthtab[i], 3)) {
time.tm_mon = i;
break;
}
time.tm_isdst = 0; /* daylight saving is never in effect in GMT */
/* this is normally the value of extern int timezone, but some
* braindead C libraries don't provide it.
*/
if (!tzchecked) {
struct tm *tc;
time_t then = JAN02_1980;
tc = localtime (&then);
tzoff = (12 - tc->tm_hour) * 3600 + tc->tm_min * 60 + tc->tm_sec;
tzchecked = 1;
}
res = mktime (&time);
/* Unfortunately, mktime() assumes the input is in local time,
* not GMT, so we have to correct it here.
*/
if (res != -1)
res += tzoff;
return res;
}
/* Convert a Unix time to a network time string
* Weekday, DD-MMM-YYYY HH:MM:SS GMT
*/
static void
strtime (time_t * t, char *s)
{
struct tm *tm;
tm = gmtime ((time_t *) t);
sprintf (s, "%s, %02d %s %d %02d:%02d:%02d GMT",
days[tm->tm_wday], tm->tm_mday, monthtab[tm->tm_mon],
tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
}
#define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
int
RTMP_HashSWF (const char *url, unsigned int *size, unsigned char *hash, int age)
{
FILE *f = NULL;
char *path, date[64], cctim[64];
long pos = 0;
time_t ctim = -1, cnow;
int i, got = 0, ret = 0;
unsigned int hlen;
struct info in = { 0 };
struct HTTP_ctx http = { 0 };
HTTPResult httpres;
z_stream zs = { 0 };
AVal home, hpre;
date[0] = '\0';
#ifdef _WIN32
#ifdef _XBOX
hpre.av_val = "Q:";
hpre.av_len = 2;
home.av_val = "\\UserData";
#else
hpre.av_val = getenv ("HOMEDRIVE");
hpre.av_len = strlen (hpre.av_val);
home.av_val = getenv ("HOMEPATH");
#endif
#define DIRSEP "\\"
#else /* !_WIN32 */
hpre.av_val = (char *) "";
hpre.av_len = 0;
home.av_val = getenv ("HOME");
#define DIRSEP "/"
#endif
if (!home.av_val)
home.av_val = (char *) ".";
home.av_len = strlen (home.av_val);
/* SWF hash info is cached in a fixed-format file.
* url: <url of SWF file>
* ctim: HTTP datestamp of when we last checked it.
* date: HTTP datestamp of the SWF's last modification.
* size: SWF size in hex
* hash: SWF hash in hex
*
* These fields must be present in this order. All fields
* besides URL are fixed size.
*/
path = malloc (hpre.av_len + home.av_len + sizeof (DIRSEP ".swfinfo"));
sprintf (path, "%s%s" DIRSEP ".swfinfo", hpre.av_val, home.av_val);
f = fopen (path, "r+");
while (f) {
char buf[4096], *file, *p;
file = strchr (url, '/');
if (!file)
break;
file += 2;
file = strchr (file, '/');
if (!file)
break;
file++;
hlen = file - url;
p = strrchr (file, '/');
if (p)
file = p;
else
file--;
while (fgets (buf, sizeof (buf), f)) {
char *r1;
got = 0;
if (strncmp (buf, "url: ", 5))
continue;
if (strncmp (buf + 5, url, hlen))
continue;
r1 = strrchr (buf, '/');
i = strlen (r1);
r1[--i] = '\0';
if (strncmp (r1, file, i))
continue;
pos = ftell (f);
while (got < 4 && fgets (buf, sizeof (buf), f)) {
if (!strncmp (buf, "size: ", 6)) {
*size = strtol (buf + 6, NULL, 16);
got++;
} else if (!strncmp (buf, "hash: ", 6)) {
unsigned char *ptr = hash, *in = (unsigned char *) buf + 6;
int l = strlen ((char *) in) - 1;
for (i = 0; i < l; i += 2)
*ptr++ = (HEX2BIN (in[i]) << 4) | HEX2BIN (in[i + 1]);
got++;
} else if (!strncmp (buf, "date: ", 6)) {
buf[strlen (buf) - 1] = '\0';
strncpy (date, buf + 6, sizeof (date));
got++;
} else if (!strncmp (buf, "ctim: ", 6)) {
buf[strlen (buf) - 1] = '\0';
ctim = make_unix_time (buf + 6);
got++;
} else if (!strncmp (buf, "url: ", 5))
break;
}
break;
}
break;
}
cnow = time (NULL);
/* If we got a cache time, see if it's young enough to use directly */
if (age && ctim > 0) {
ctim = cnow - ctim;
ctim /= 3600 * 24; /* seconds to days */
if (ctim < age) /* ok, it's new enough */
goto out;
}
in.first = 1;
HMAC_setup (in.ctx, "Genuine Adobe Flash Player 001", 30);
inflateInit (&zs);
in.zs = &zs;
http.date = date;
http.data = &in;
httpres = HTTP_get (&http, url, swfcrunch);
inflateEnd (&zs);
if (httpres != HTTPRES_OK && httpres != HTTPRES_OK_NOT_MODIFIED) {
ret = -1;
if (httpres == HTTPRES_LOST_CONNECTION)
RTMP_Log (RTMP_LOGERROR,
"%s: connection lost while downloading swfurl %s", __FUNCTION__, url);
else if (httpres == HTTPRES_NOT_FOUND)
RTMP_Log (RTMP_LOGERROR, "%s: swfurl %s not found", __FUNCTION__, url);
else
RTMP_Log (RTMP_LOGERROR, "%s: couldn't contact swfurl %s (HTTP error %d)",
__FUNCTION__, url, http.status);
} else {
if (got && pos)
fseek (f, pos, SEEK_SET);
else {
char *q;
if (!f)
f = fopen (path, "w");
if (!f) {
int err = errno;
RTMP_Log (RTMP_LOGERROR,
"%s: couldn't open %s for writing, errno %d (%s)",
__FUNCTION__, path, err, strerror (err));
ret = -1;
goto out;
}
fseek (f, 0, SEEK_END);
q = strchr (url, '?');
if (q)
i = q - url;
else
i = strlen (url);
fprintf (f, "url: %.*s\n", i, url);
}
strtime (&cnow, cctim);
fprintf (f, "ctim: %s\n", cctim);
if (!in.first) {
HMAC_finish (in.ctx, hash, hlen);
*size = in.size;
fprintf (f, "date: %s\n", date);
fprintf (f, "size: %08x\n", in.size);
fprintf (f, "hash: ");
for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
fprintf (f, "%02x", hash[i]);
fprintf (f, "\n");
}
}
HMAC_close (in.ctx);
out:
free (path);
if (f)
fclose (f);
return ret;
}
#else
int
RTMP_HashSWF (const char *url, unsigned int *size, unsigned char *hash, int age)
{
return -1;
}
#endif

46
gst/rtmp/http.h Normal file
View file

@ -0,0 +1,46 @@
#ifndef __RTMP_HTTP_H__
#define __RTMP_HTTP_H__
/*
* Copyright (C) 2010 Howard Chu
* Copyright (C) 2010 Antti Ajanki
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
typedef enum {
HTTPRES_OK, /* result OK */
HTTPRES_OK_NOT_MODIFIED, /* not modified since last request */
HTTPRES_NOT_FOUND, /* not found */
HTTPRES_BAD_REQUEST, /* client error */
HTTPRES_SERVER_ERROR, /* server reported an error */
HTTPRES_REDIRECTED, /* resource has been moved */
HTTPRES_LOST_CONNECTION /* connection lost while waiting for data */
} HTTPResult;
struct HTTP_ctx {
char *date;
int size;
int status;
void *data;
};
typedef size_t (HTTP_read_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
HTTPResult HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb);
#endif

233
gst/rtmp/log.c Normal file
View file

@ -0,0 +1,233 @@
/*
* Copyright (C) 2008-2009 Andrej Stepanchuk
* Copyright (C) 2009-2010 Howard Chu
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include "rtmp_sys.h"
#include "log.h"
#define MAX_PRINT_LEN 2048
RTMP_LogLevel RTMP_debuglevel = RTMP_LOGERROR;
static int neednl;
static FILE *fmsg;
static RTMP_LogCallback rtmp_log_default, *cb = rtmp_log_default;
static const char *levels[] = {
"CRIT", "ERROR", "WARNING", "INFO",
"DEBUG", "DEBUG2"
};
static void
rtmp_log_default (int level, const char *format, va_list vl)
{
char str[MAX_PRINT_LEN] = "";
vsnprintf (str, MAX_PRINT_LEN - 1, format, vl);
/* Filter out 'no-name' */
if (RTMP_debuglevel < RTMP_LOGALL && strstr (str, "no-name") != NULL)
return;
if (!fmsg)
fmsg = stderr;
if (level <= RTMP_debuglevel) {
if (neednl) {
putc ('\n', fmsg);
neednl = 0;
}
fprintf (fmsg, "%s: %s\n", levels[level], str);
#ifdef _DEBUG
fflush (fmsg);
#endif
}
}
void
RTMP_LogSetOutput (FILE * file)
{
fmsg = file;
}
void
RTMP_LogSetLevel (RTMP_LogLevel level)
{
RTMP_debuglevel = level;
}
void
RTMP_LogSetCallback (RTMP_LogCallback * cbp)
{
cb = cbp;
}
RTMP_LogLevel
RTMP_LogGetLevel (void)
{
return RTMP_debuglevel;
}
void
RTMP_Log (int level, const char *format, ...)
{
va_list args;
va_start (args, format);
cb (level, format, args);
va_end (args);
}
static const char hexdig[] = "0123456789abcdef";
void
RTMP_LogHex (int level, const uint8_t * data, unsigned long len)
{
unsigned long i;
char line[50], *ptr;
if (level > RTMP_debuglevel)
return;
ptr = line;
for (i = 0; i < len; i++) {
*ptr++ = hexdig[0x0f & (data[i] >> 4)];
*ptr++ = hexdig[0x0f & data[i]];
if ((i & 0x0f) == 0x0f) {
*ptr = '\0';
ptr = line;
RTMP_Log (level, "%s", line);
} else {
*ptr++ = ' ';
}
}
if (i & 0x0f) {
*ptr = '\0';
RTMP_Log (level, "%s", line);
}
}
void
RTMP_LogHexString (int level, const uint8_t * data, unsigned long len)
{
#define BP_OFFSET 9
#define BP_GRAPH 60
#define BP_LEN 80
char line[BP_LEN];
unsigned long i;
if (!data || level > RTMP_debuglevel)
return;
/* in case len is zero */
line[0] = '\0';
for (i = 0; i < len; i++) {
int n = i % 16;
unsigned off;
if (!n) {
if (i)
RTMP_Log (level, "%s", line);
memset (line, ' ', sizeof (line) - 2);
line[sizeof (line) - 2] = '\0';
off = i % 0x0ffffU;
line[2] = hexdig[0x0f & (off >> 12)];
line[3] = hexdig[0x0f & (off >> 8)];
line[4] = hexdig[0x0f & (off >> 4)];
line[5] = hexdig[0x0f & off];
line[6] = ':';
}
off = BP_OFFSET + n * 3 + ((n >= 8) ? 1 : 0);
line[off] = hexdig[0x0f & (data[i] >> 4)];
line[off + 1] = hexdig[0x0f & data[i]];
off = BP_GRAPH + n + ((n >= 8) ? 1 : 0);
if (isprint (data[i])) {
line[BP_GRAPH + n] = data[i];
} else {
line[BP_GRAPH + n] = '.';
}
}
RTMP_Log (level, "%s", line);
}
/* These should only be used by apps, never by the library itself */
void
RTMP_LogPrintf (const char *format, ...)
{
char str[MAX_PRINT_LEN] = "";
int len;
va_list args;
va_start (args, format);
len = vsnprintf (str, MAX_PRINT_LEN - 1, format, args);
va_end (args);
if (RTMP_debuglevel == RTMP_LOGCRIT)
return;
if (!fmsg)
fmsg = stderr;
if (neednl) {
putc ('\n', fmsg);
neednl = 0;
}
if (len > MAX_PRINT_LEN - 1)
len = MAX_PRINT_LEN - 1;
fprintf (fmsg, "%s", str);
if (str[len - 1] == '\n')
fflush (fmsg);
}
void
RTMP_LogStatus (const char *format, ...)
{
char str[MAX_PRINT_LEN] = "";
va_list args;
va_start (args, format);
vsnprintf (str, MAX_PRINT_LEN - 1, format, args);
va_end (args);
if (RTMP_debuglevel == RTMP_LOGCRIT)
return;
if (!fmsg)
fmsg = stderr;
fprintf (fmsg, "%s", str);
fflush (fmsg);
neednl = 1;
}

62
gst/rtmp/log.h Normal file
View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2008-2009 Andrej Stepanchuk
* Copyright (C) 2009-2010 Howard Chu
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
#ifndef __RTMP_LOG_H__
#define __RTMP_LOG_H__
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Enable this to get full debugging output */
/* #define _DEBUG */
#ifdef _DEBUG
#undef NODEBUG
#endif
typedef enum
{ RTMP_LOGCRIT=0, RTMP_LOGERROR, RTMP_LOGWARNING, RTMP_LOGINFO,
RTMP_LOGDEBUG, RTMP_LOGDEBUG2, RTMP_LOGALL
} RTMP_LogLevel;
extern RTMP_LogLevel RTMP_debuglevel;
typedef void (RTMP_LogCallback)(int level, const char *fmt, va_list);
void RTMP_LogSetCallback(RTMP_LogCallback *cb);
void RTMP_LogSetOutput(FILE *file);
void RTMP_LogPrintf(const char *format, ...);
void RTMP_LogStatus(const char *format, ...);
void RTMP_Log(int level, const char *format, ...);
void RTMP_LogHex(int level, const uint8_t *data, unsigned long len);
void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len);
void RTMP_LogSetLevel(RTMP_LogLevel lvl);
RTMP_LogLevel RTMP_LogGetLevel(void);
#ifdef __cplusplus
}
#endif
#endif

281
gst/rtmp/parseurl.c Normal file
View file

@ -0,0 +1,281 @@
/*
* Copyright (C) 2009 Andrej Stepanchuk
* Copyright (C) 2009-2010 Howard Chu
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include "rtmp_sys.h"
#include "log.h"
bool
RTMP_ParseURL (const char *url, int *protocol, AVal * host, unsigned int *port,
AVal * playpath, AVal * app)
{
char *p, *end, *col, *ques, *slash;
RTMP_Log (RTMP_LOGDEBUG, "Parsing...");
*protocol = RTMP_PROTOCOL_RTMP;
*port = 0;
playpath->av_len = 0;
playpath->av_val = NULL;
app->av_len = 0;
app->av_val = NULL;
/* Old School Parsing */
/* look for usual :// pattern */
p = strstr (url, "://");
if (!p) {
RTMP_Log (RTMP_LOGERROR, "RTMP URL: No :// in url!");
return false;
}
{
int len = (int) (p - url);
if (len == 4 && strncasecmp (url, "rtmp", 4) == 0)
*protocol = RTMP_PROTOCOL_RTMP;
else if (len == 5 && strncasecmp (url, "rtmpt", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMPT;
else if (len == 5 && strncasecmp (url, "rtmps", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMPS;
else if (len == 5 && strncasecmp (url, "rtmpe", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMPE;
else if (len == 5 && strncasecmp (url, "rtmfp", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMFP;
else if (len == 6 && strncasecmp (url, "rtmpte", 6) == 0)
*protocol = RTMP_PROTOCOL_RTMPTE;
else if (len == 6 && strncasecmp (url, "rtmpts", 6) == 0)
*protocol = RTMP_PROTOCOL_RTMPTS;
else {
RTMP_Log (RTMP_LOGWARNING, "Unknown protocol!\n");
goto parsehost;
}
}
RTMP_Log (RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol);
parsehost:
/* let's get the hostname */
p += 3;
/* check for sudden death */
if (*p == 0) {
RTMP_Log (RTMP_LOGWARNING, "No hostname in URL!");
return false;
}
end = p + strlen (p);
col = strchr (p, ':');
ques = strchr (p, '?');
slash = strchr (p, '/');
{
int hostlen;
if (slash)
hostlen = slash - p;
else
hostlen = end - p;
if (col && col - p < hostlen)
hostlen = col - p;
if (hostlen < 256) {
host->av_val = p;
host->av_len = hostlen;
RTMP_Log (RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host->av_val);
} else {
RTMP_Log (RTMP_LOGWARNING, "Hostname exceeds 255 characters!");
}
p += hostlen;
}
/* get the port number if available */
if (*p == ':') {
unsigned int p2;
p++;
p2 = atoi (p);
if (p2 > 65535) {
RTMP_Log (RTMP_LOGWARNING, "Invalid port number!");
} else {
*port = p2;
}
}
if (!slash) {
RTMP_Log (RTMP_LOGWARNING, "No application or playpath in URL!");
return true;
}
p = slash + 1;
{
/* parse application
*
* rtmp://host[:port]/app[/appinstance][/...]
* application = app[/appinstance]
*/
char *slash2, *slash3 = NULL;
int applen, appnamelen;
slash2 = strchr (p, '/');
if (slash2)
slash3 = strchr (slash2 + 1, '/');
applen = end - p; /* ondemand, pass all parameters as app */
appnamelen = applen; /* ondemand length */
if (ques && strstr (p, "slist=")) { /* whatever it is, the '?' and slist= means we need to use everything as app and parse plapath from slist= */
appnamelen = ques - p;
} else if (strncmp (p, "ondemand/", 9) == 0) {
/* app = ondemand/foobar, only pass app=ondemand */
applen = 8;
appnamelen = 8;
} else { /* app!=ondemand, so app is app[/appinstance] */
if (slash3)
appnamelen = slash3 - p;
else if (slash2)
appnamelen = slash2 - p;
applen = appnamelen;
}
app->av_val = p;
app->av_len = applen;
RTMP_Log (RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p);
p += appnamelen;
}
if (*p == '/')
p++;
if (end - p) {
AVal av = { p, end - p };
RTMP_ParsePlaypath (&av, playpath);
}
return true;
}
/*
* Extracts playpath from RTMP URL. playpath is the file part of the
* URL, i.e. the part that comes after rtmp://host:port/app/
*
* Returns the stream name in a format understood by FMS. The name is
* the playpath part of the URL with formatting depending on the stream
* type:
*
* mp4 streams: prepend "mp4:", remove extension
* mp3 streams: prepend "mp3:", remove extension
* flv streams: remove extension
*/
void
RTMP_ParsePlaypath (AVal * in, AVal * out)
{
int addMP4 = 0;
int addMP3 = 0;
int subExt = 0;
const char *playpath = in->av_val;
const char *temp, *q, *ext = NULL;
const char *ppstart = playpath;
char *streamname, *destptr, *p;
int pplen = in->av_len;
out->av_val = NULL;
out->av_len = 0;
if ((*ppstart == '?') && (temp = strstr (ppstart, "slist=")) != 0) {
ppstart = temp + 6;
pplen = strlen (ppstart);
temp = strchr (ppstart, '&');
if (temp) {
pplen = temp - ppstart;
}
}
q = strchr (ppstart, '?');
if (pplen >= 4) {
if (q)
ext = q - 4;
else
ext = &ppstart[pplen - 4];
if ((strncmp (ext, ".f4v", 4) == 0) || (strncmp (ext, ".mp4", 4) == 0)) {
addMP4 = 1;
subExt = 1;
/* Only remove .flv from rtmp URL, not slist params */
} else if ((ppstart == playpath) && (strncmp (ext, ".flv", 4) == 0)) {
subExt = 1;
} else if (strncmp (ext, ".mp3", 4) == 0) {
addMP3 = 1;
subExt = 1;
}
}
streamname = (char *) malloc ((pplen + 4 + 1) * sizeof (char));
if (!streamname)
return;
destptr = streamname;
if (addMP4) {
if (strncmp (ppstart, "mp4:", 4)) {
strcpy (destptr, "mp4:");
destptr += 4;
} else {
subExt = 0;
}
} else if (addMP3) {
if (strncmp (ppstart, "mp3:", 4)) {
strcpy (destptr, "mp3:");
destptr += 4;
} else {
subExt = 0;
}
}
for (p = (char *) ppstart; pplen > 0;) {
/* skip extension */
if (subExt && p == ext) {
p += 4;
pplen -= 4;
}
if (*p == '%') {
unsigned int c;
sscanf (p + 1, "%02x", &c);
*destptr++ = c;
pplen -= 3;
p += 3;
} else {
*destptr++ = *p++;
pplen--;
}
}
*destptr = '\0';
out->av_val = streamname;
out->av_len = destptr - streamname;
}

4018
gst/rtmp/rtmp.c Normal file

File diff suppressed because it is too large Load diff

336
gst/rtmp/rtmp.h Normal file
View file

@ -0,0 +1,336 @@
#ifndef __RTMP_H__
#define __RTMP_H__
/*
* Copyright (C) 2005-2008 Team XBMC
* http://www.xbmc.org
* Copyright (C) 2008-2009 Andrej Stepanchuk
* Copyright (C) 2009-2010 Howard Chu
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
#if !defined(NO_CRYPTO) && !defined(CRYPTO)
#define CRYPTO
#endif
#include <errno.h>
#include <stdint.h>
#include <stddef.h>
#include "amf.h"
#ifdef __cplusplus
extern "C"
{
#endif
#define RTMP_LIB_VERSION 0x020205 /* 2.2e */
#define RTMP_FEATURE_HTTP 0x01
#define RTMP_FEATURE_ENC 0x02
#define RTMP_FEATURE_SSL 0x04
#define RTMP_FEATURE_MFP 0x08 /* not yet supported */
#define RTMP_FEATURE_WRITE 0x10 /* publish, not play */
#define RTMP_FEATURE_HTTP2 0x20 /* server-side rtmpt */
#define RTMP_PROTOCOL_UNDEFINED -1
#define RTMP_PROTOCOL_RTMP 0
#define RTMP_PROTOCOL_RTMPE RTMP_FEATURE_ENC
#define RTMP_PROTOCOL_RTMPT RTMP_FEATURE_HTTP
#define RTMP_PROTOCOL_RTMPS RTMP_FEATURE_SSL
#define RTMP_PROTOCOL_RTMPTE (RTMP_FEATURE_HTTP|RTMP_FEATURE_ENC)
#define RTMP_PROTOCOL_RTMPTS (RTMP_FEATURE_HTTP|RTMP_FEATURE_SSL)
#define RTMP_PROTOCOL_RTMFP RTMP_FEATURE_MFP
#define RTMP_DEFAULT_CHUNKSIZE 128
/* needs to fit largest number of bytes recv() may return */
#define RTMP_BUFFER_CACHE_SIZE (16*1024)
#define RTMP_CHANNELS 65600
extern const char RTMPProtocolStringsLower[][7];
extern const AVal RTMP_DefaultFlashVer;
extern bool RTMP_ctrlC;
uint32_t RTMP_GetTime(void);
#define RTMP_PACKET_TYPE_AUDIO 0x08
#define RTMP_PACKET_TYPE_VIDEO 0x09
#define RTMP_PACKET_TYPE_INFO 0x12
#define RTMP_MAX_HEADER_SIZE 18
#define RTMP_PACKET_SIZE_LARGE 0
#define RTMP_PACKET_SIZE_MEDIUM 1
#define RTMP_PACKET_SIZE_SMALL 2
#define RTMP_PACKET_SIZE_MINIMUM 3
typedef struct RTMPChunk
{
int c_headerSize;
int c_chunkSize;
char *c_chunk;
char c_header[RTMP_MAX_HEADER_SIZE];
} RTMPChunk;
typedef struct RTMPPacket
{
uint8_t m_headerType;
uint8_t m_packetType;
uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */
int m_nChannel;
uint32_t m_nTimeStamp; /* timestamp */
int32_t m_nInfoField2; /* last 4 bytes in a long header */
uint32_t m_nBodySize;
uint32_t m_nBytesRead;
RTMPChunk *m_chunk;
char *m_body;
} RTMPPacket;
typedef struct RTMPSockBuf
{
int sb_socket;
int sb_size; /* number of unprocessed bytes in buffer */
char *sb_start; /* pointer into sb_pBuffer of next byte to process */
char sb_buf[RTMP_BUFFER_CACHE_SIZE]; /* data read from socket */
bool sb_timedout;
void *sb_ssl;
} RTMPSockBuf;
void RTMPPacket_Reset(RTMPPacket *p);
void RTMPPacket_Dump(RTMPPacket *p);
bool RTMPPacket_Alloc(RTMPPacket *p, int nSize);
void RTMPPacket_Free(RTMPPacket *p);
#define RTMPPacket_IsReady(a) ((a)->m_nBytesRead == (a)->m_nBodySize)
typedef struct RTMP_LNK
{
AVal hostname;
AVal sockshost;
AVal playpath0; /* parsed from URL */
AVal playpath; /* passed in explicitly */
AVal tcUrl;
AVal swfUrl;
AVal pageUrl;
AVal app;
AVal auth;
AVal flashVer;
AVal subscribepath;
AVal token;
AMFObject extras;
int edepth;
int seekTime;
int stopTime;
#define RTMP_LF_AUTH 0x0001 /* using auth param */
#define RTMP_LF_LIVE 0x0002 /* stream is live */
#define RTMP_LF_SWFV 0x0004 /* do SWF verification */
#define RTMP_LF_PLST 0x0008 /* send playlist before play */
#define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */
int lFlags;
int swfAge;
int protocol;
int timeout; /* connection timeout in seconds */
unsigned short socksport;
unsigned short port;
#ifdef CRYPTO
#define RTMP_SWF_HASHLEN 32
void *dh; /* for encryption */
void *rc4keyIn;
void *rc4keyOut;
uint32_t SWFSize;
uint8_t SWFHash[RTMP_SWF_HASHLEN];
char SWFVerificationResponse[RTMP_SWF_HASHLEN+10];
#endif
} RTMP_LNK;
/* state for read() wrapper */
typedef struct RTMP_READ
{
char *buf;
char *bufpos;
unsigned int buflen;
uint32_t timestamp;
uint8_t dataType;
uint8_t flags;
#define RTMP_READ_HEADER 0x01
#define RTMP_READ_RESUME 0x02
#define RTMP_READ_NO_IGNORE 0x04
#define RTMP_READ_GOTKF 0x08
#define RTMP_READ_GOTFLVK 0x10
#define RTMP_READ_SEEKING 0x20
int8_t status;
#define RTMP_READ_COMPLETE -3
#define RTMP_READ_ERROR -2
#define RTMP_READ_EOF -1
#define RTMP_READ_IGNORE 0
/* if bResume == TRUE */
uint8_t initialFrameType;
uint32_t nResumeTS;
char *metaHeader;
char *initialFrame;
uint32_t nMetaHeaderSize;
uint32_t nInitialFrameSize;
uint32_t nIgnoredFrameCounter;
uint32_t nIgnoredFlvFrameCounter;
} RTMP_READ;
typedef struct RTMP_METHOD
{
AVal name;
int num;
} RTMP_METHOD;
typedef struct RTMP
{
int m_inChunkSize;
int m_outChunkSize;
int m_nBWCheckCounter;
int m_nBytesIn;
int m_nBytesInSent;
int m_nBufferMS;
int m_stream_id; /* returned in _result from createStream */
int m_mediaChannel;
uint32_t m_mediaStamp;
uint32_t m_pauseStamp;
int m_pausing;
int m_nServerBW;
int m_nClientBW;
uint8_t m_nClientBW2;
uint8_t m_bPlaying;
uint8_t m_bSendEncoding;
uint8_t m_bSendCounter;
int m_numInvokes;
int m_numCalls;
RTMP_METHOD *m_methodCalls; /* remote method calls queue */
RTMPPacket *m_vecChannelsIn[RTMP_CHANNELS];
RTMPPacket *m_vecChannelsOut[RTMP_CHANNELS];
int m_channelTimestamp[RTMP_CHANNELS]; /* abs timestamp of last packet */
double m_fAudioCodecs; /* audioCodecs for the connect packet */
double m_fVideoCodecs; /* videoCodecs for the connect packet */
double m_fEncoding; /* AMF0 or AMF3 */
double m_fDuration; /* duration of stream in seconds */
int m_msgCounter; /* RTMPT stuff */
int m_polling;
int m_resplen;
int m_unackd;
AVal m_clientID;
RTMP_READ m_read;
RTMPPacket m_write;
RTMPSockBuf m_sb;
RTMP_LNK Link;
} RTMP;
bool RTMP_ParseURL(const char *url, int *protocol, AVal *host,
unsigned int *port, AVal *playpath, AVal *app);
void RTMP_ParsePlaypath(AVal *in, AVal *out);
void RTMP_SetBufferMS(RTMP *r, int size);
void RTMP_UpdateBufferMS(RTMP *r);
bool RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg);
bool RTMP_SetupURL(RTMP *r, char *url);
void RTMP_SetupStream(RTMP *r, int protocol,
AVal *hostname,
unsigned int port,
AVal *sockshost,
AVal *playpath,
AVal *tcUrl,
AVal *swfUrl,
AVal *pageUrl,
AVal *app,
AVal *auth,
AVal *swfSHA256Hash,
uint32_t swfSize,
AVal *flashVer,
AVal *subscribepath,
int dStart,
int dStop, bool bLiveStream, long int timeout);
bool RTMP_Connect(RTMP *r, RTMPPacket *cp);
struct sockaddr;
bool RTMP_Connect0(RTMP *r, struct sockaddr *svc);
bool RTMP_Connect1(RTMP *r, RTMPPacket *cp);
bool RTMP_Serve(RTMP *r);
bool RTMP_ReadPacket(RTMP *r, RTMPPacket *packet);
bool RTMP_SendPacket(RTMP *r, RTMPPacket *packet, bool queue);
bool RTMP_SendChunk(RTMP *r, RTMPChunk *chunk);
bool RTMP_IsConnected(RTMP *r);
bool RTMP_IsTimedout(RTMP *r);
double RTMP_GetDuration(RTMP *r);
bool RTMP_ToggleStream(RTMP *r);
bool RTMP_ConnectStream(RTMP *r, int seekTime);
bool RTMP_ReconnectStream(RTMP *r, int seekTime);
void RTMP_DeleteStream(RTMP *r);
int RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet);
int RTMP_ClientPacket(RTMP *r, RTMPPacket *packet);
void RTMP_Init(RTMP *r);
void RTMP_Close(RTMP *r);
RTMP *RTMP_Alloc(void);
void RTMP_Free(RTMP *r);
void RTMP_EnableWrite(RTMP *r);
int RTMP_LibVersion(void);
void RTMP_UserInterrupt(void); /* user typed Ctrl-C */
bool RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject,
unsigned int nTime);
bool RTMP_SendPause(RTMP *r, bool DoPause, int dTime);
bool RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name,
AMFObjectProperty * p);
int RTMPSockBuf_Fill(RTMPSockBuf *sb);
int RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len);
int RTMPSockBuf_Close(RTMPSockBuf *sb);
bool RTMP_SendCreateStream(RTMP *r);
bool RTMP_SendSeek(RTMP *r, int dTime);
bool RTMP_SendServerBW(RTMP *r);
bool RTMP_SendClientBW(RTMP *r);
void RTMP_DropRequest(RTMP *r, int i, bool freeit);
int RTMP_Read(RTMP *r, char *buf, int size);
int RTMP_Write(RTMP *r, char *buf, int size);
/* hashswf.c */
int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
int age);
#ifdef __cplusplus
};
#endif
#endif

111
gst/rtmp/rtmp_sys.h Normal file
View file

@ -0,0 +1,111 @@
#ifndef __RTMP_SYS_H__
#define __RTMP_SYS_H__
/*
* Copyright (C) 2010 Howard Chu
*
* This file is part of librtmp.
*
* librtmp 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,
* or (at your option) any later version.
*
* librtmp 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/lgpl.html
*/
#ifdef _WIN32
#ifdef _XBOX
#include <xtl.h>
#include <winsockx.h>
#define snprintf _snprintf
#define strcasecmp stricmp
#define strncasecmp strnicmp
#define vsnprintf _vsnprintf
#else /* !_XBOX */
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#define GetSockError() WSAGetLastError()
#define SetSockError(e) WSASetLastError(e)
#define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
#define EWOULDBLOCK WSAETIMEDOUT /* we don't use nonblocking, but we do use timeouts */
#define sleep(n) Sleep(n*1000)
#define msleep(n) Sleep(n)
#define SET_RCVTIMEO(tv,s) int tv = s*1000
#else /* !_WIN32 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/times.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#define GetSockError() errno
#define SetSockError(e) errno = e
#undef closesocket
#define closesocket(s) close(s)
#define msleep(n) usleep(n*1000)
#define SET_RCVTIMEO(tv,s) struct timeval tv = {s,0}
#endif
#include "rtmp.h"
#ifdef USE_POLARSSL
#include <polarssl/net.h>
#include <polarssl/ssl.h>
#include <polarssl/havege.h>
typedef struct tls_ctx {
havege_state hs;
ssl_session ssn;
} tls_ctx;
#define TLS_CTX tls_ctx *
#define TLS_client(ctx,s) s = malloc(sizeof(ssl_context)); ssl_init(s);\
ssl_set_endpoint(s, SSL_IS_CLIENT); ssl_set_authmode(s, SSL_VERIFY_NONE);\
ssl_set_rng(s, havege_rand, &ctx->hs); ssl_set_ciphers(s, ssl_default_ciphers);\
ssl_set_session(s, 1, 600, &ctx->ssn)
#define TLS_setfd(s,fd) ssl_set_bio(s, net_recv, &fd, net_send, &fd)
#define TLS_connect(s) ssl_handshake(s)
#define TLS_read(s,b,l) ssl_read(s,(unsigned char *)b,l)
#define TLS_write(s,b,l) ssl_write(s,(unsigned char *)b,l)
#define TLS_shutdown(s) ssl_close_notify(s)
#define TLS_close(s) ssl_free(s); free(s)
#elif defined(USE_GNUTLS)
#include <gnutls/gnutls.h>
typedef struct tls_ctx {
gnutls_certificate_credentials_t cred;
gnutls_priority_t prios;
} tls_ctx;
#define TLS_CTX tls_ctx *
#define TLS_client(ctx,s) gnutls_init((gnutls_session_t *)(&s), GNUTLS_CLIENT); gnutls_priority_set(s, ctx->prios); gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, ctx->cred)
#define TLS_setfd(s,fd) gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)(long)fd)
#define TLS_connect(s) gnutls_handshake(s)
#define TLS_read(s,b,l) gnutls_record_recv(s,b,l)
#define TLS_write(s,b,l) gnutls_record_send(s,b,l)
#define TLS_shutdown(s) gnutls_bye(s, GNUTLS_SHUT_RDWR)
#define TLS_close(s) gnutls_deinit(s)
#else /* USE_OPENSSL */
#define TLS_CTX SSL_CTX *
#define TLS_client(ctx,s) s = SSL_new(ctx)
#define TLS_setfd(s,fd) SSL_set_fd(s,fd)
#define TLS_connect(s) SSL_connect(s)
#define TLS_read(s,b,l) SSL_read(s,b,l)
#define TLS_write(s,b,l) SSL_write(s,b,l)
#define TLS_shutdown(s) SSL_shutdown(s)
#define TLS_close(s) SSL_free(s)
#endif
#endif