aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Pitoiset2012-08-15 16:11:50 +0200
committerMartin Storsjö2012-08-15 22:05:36 +0300
commit93f257db6b818896e58c708d3c2ec4b8cb0c7b00 (patch)
tree88a56d9f108b69ceea84de98ec4341a6bc107333
parent635ac8e1be91e941908f85642e4bbb609e48193f (diff)
rtmp: Automatically compute the hash for SWFVerification
Signed-off-by: Martin Storsjö <martin@martin.st>
-rw-r--r--doc/protocols.texi3
-rw-r--r--libavformat/rtmpproto.c134
-rw-r--r--libavformat/version.h2
3 files changed, 138 insertions, 1 deletions
diff --git a/doc/protocols.texi b/doc/protocols.texi
index bdb3e8cae1..ea5706fbea 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -251,6 +251,9 @@ Size of the decompressed SWF file, required for SWFVerification.
@item rtmp_swfurl
URL of the SWF player for the media. By default no value will be sent.
+@item rtmp_swfverify
+URL to player swf file, compute hash/size automatically.
+
@item rtmp_tcurl
URL of the target stream. Defaults to proto://host[:port]/app.
diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
index 9b85e523c5..db1150186f 100644
--- a/libavformat/rtmpproto.c
+++ b/libavformat/rtmpproto.c
@@ -41,6 +41,10 @@
#include "rtmppkt.h"
#include "url.h"
+#if CONFIG_ZLIB
+#include <zlib.h>
+#endif
+
//#define DEBUG
#define APP_MAX_LENGTH 128
@@ -95,6 +99,7 @@ typedef struct RTMPContext {
int swfhash_len; ///< length of the SHA256 hash
int swfsize; ///< size of the decompressed SWF file
char* swfurl; ///< url of the swf player
+ char* swfverify; ///< URL to player swf file, compute hash/size automatically
char swfverification[42]; ///< hash of the SWF verification
char* pageurl; ///< url of the web page
char* subscribe; ///< name of live stream to subscribe
@@ -825,6 +830,129 @@ static int rtmp_calc_swf_verification(URLContext *s, RTMPContext *rt,
return 0;
}
+#if CONFIG_ZLIB
+static int rtmp_uncompress_swfplayer(uint8_t *in_data, int64_t in_size,
+ uint8_t **out_data, int64_t *out_size)
+{
+ z_stream zs = { 0 };
+ void *ptr;
+ int size;
+ int ret = 0;
+
+ zs.avail_in = in_size;
+ zs.next_in = in_data;
+ ret = inflateInit(&zs);
+ if (ret != Z_OK)
+ return AVERROR_UNKNOWN;
+
+ do {
+ uint8_t tmp_buf[16384];
+
+ zs.avail_out = sizeof(tmp_buf);
+ zs.next_out = tmp_buf;
+
+ ret = inflate(&zs, Z_NO_FLUSH);
+ if (ret != Z_OK && ret != Z_STREAM_END) {
+ ret = AVERROR_UNKNOWN;
+ goto fail;
+ }
+
+ size = sizeof(tmp_buf) - zs.avail_out;
+ if (!(ptr = av_realloc(*out_data, *out_size + size))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ *out_data = ptr;
+
+ memcpy(*out_data + *out_size, tmp_buf, size);
+ *out_size += size;
+ } while (zs.avail_out == 0);
+
+fail:
+ inflateEnd(&zs);
+ return ret;
+}
+#endif
+
+static int rtmp_calc_swfhash(URLContext *s)
+{
+ RTMPContext *rt = s->priv_data;
+ uint8_t *in_data = NULL, *out_data = NULL, *swfdata;
+ int64_t in_size, out_size;
+ URLContext *stream;
+ char swfhash[32];
+ int swfsize;
+ int ret = 0;
+
+ /* Get the SWF player file. */
+ if ((ret = ffurl_open(&stream, rt->swfverify, AVIO_FLAG_READ,
+ &s->interrupt_callback, NULL)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Cannot open connection %s.\n", rt->swfverify);
+ goto fail;
+ }
+
+ if ((in_size = ffurl_seek(stream, 0, AVSEEK_SIZE)) < 0) {
+ ret = AVERROR(EIO);
+ goto fail;
+ }
+
+ if (!(in_data = av_malloc(in_size))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if ((ret = ffurl_read_complete(stream, in_data, in_size)) < 0)
+ goto fail;
+
+ if (in_size < 3) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ if (!memcmp(in_data, "CWS", 3)) {
+ /* Decompress the SWF player file using Zlib. */
+ if (!(out_data = av_malloc(8))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ *in_data = 'F'; // magic stuff
+ memcpy(out_data, in_data, 8);
+ out_size = 8;
+
+#if CONFIG_ZLIB
+ if ((ret = rtmp_uncompress_swfplayer(in_data + 8, in_size - 8,
+ &out_data, &out_size)) < 0)
+ goto fail;
+#else
+ av_log(s, AV_LOG_ERROR,
+ "Zlib is required for decompressing the SWF player file.\n");
+ ret = AVERROR(EINVAL);
+ goto fail;
+#endif
+ swfsize = out_size;
+ swfdata = out_data;
+ } else {
+ swfsize = in_size;
+ swfdata = in_data;
+ }
+
+ /* Compute the SHA256 hash of the SWF player file. */
+ if ((ret = ff_rtmp_calc_digest(swfdata, swfsize, 0,
+ "Genuine Adobe Flash Player 001", 30,
+ swfhash)) < 0)
+ goto fail;
+
+ /* Set SWFVerification parameters. */
+ av_opt_set_bin(rt, "rtmp_swfhash", swfhash, 32, 0);
+ rt->swfsize = swfsize;
+
+fail:
+ av_freep(&in_data);
+ av_freep(&out_data);
+ ffurl_close(stream);
+ return ret;
+}
+
/**
* Perform handshake with the server by means of exchanging pseudorandom data
* signed with HMAC-SHA2 digest.
@@ -1492,6 +1620,11 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
goto fail;
}
+ if (rt->swfverify) {
+ if ((ret = rtmp_calc_swfhash(s)) < 0)
+ goto fail;
+ }
+
rt->state = STATE_START;
if ((ret = rtmp_handshake(s, rt)) < 0)
goto fail;
@@ -1784,6 +1917,7 @@ static const AVOption rtmp_options[] = {
{"rtmp_swfhash", "SHA256 hash of the decompressed SWF file (32 bytes).", OFFSET(swfhash), AV_OPT_TYPE_BINARY, .flags = DEC},
{"rtmp_swfsize", "Size of the decompressed SWF file, required for SWFVerification.", OFFSET(swfsize), AV_OPT_TYPE_INT, {0}, 0, INT_MAX, DEC},
{"rtmp_swfurl", "URL of the SWF player. By default no value will be sent", OFFSET(swfurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
+ {"rtmp_swfverify", "URL to player swf file, compute hash/size automatically.", OFFSET(swfverify), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
{"rtmp_tcurl", "URL of the target stream. Defaults to proto://host[:port]/app.", OFFSET(tcurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
{ NULL },
};
diff --git a/libavformat/version.h b/libavformat/version.h
index 54185fa5b7..e54f22e7ab 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -31,7 +31,7 @@
#define LIBAVFORMAT_VERSION_MAJOR 54
#define LIBAVFORMAT_VERSION_MINOR 13
-#define LIBAVFORMAT_VERSION_MICRO 3
+#define LIBAVFORMAT_VERSION_MICRO 4
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \