Re: [MPlayer-dev-eng] rtmpe support
- Date: Fri, 12 Mar 2010 16:00:50 -0800
- From: Howard Chu <hyc@xxxxxxxxxxxxxxx>
- Subject: Re: [MPlayer-dev-eng] rtmpe support
Diego Biurrun wrote:
On Thu, Mar 11, 2010 at 11:27:57PM -0800, Howard Chu wrote:
I've written the attached module to let mplayer use rtmpdump's librtmp.
.. some comments ..
#include "stream.h"
#include "m_option.h"
#include "m_struct.h"
#include "libmpdemux/demuxer.h"
#undef closesocket /* silence warning */
What warning?
librtmp/rtmp.h has #define closesocket(s) close(s)
which the compiler complains about since you have #define closesocket close
in some previous header file. I've moved the #undef to rtmp.h.
Please use 4 spaces indentation for new files.
Done, used indent -kr.
#define ST_OFF(f) M_ST_OFF(struct rtmp_params,f)
static const m_option_t rtmp_params_fields[] = {
{ "swfurl", ST_OFF(swfUrl), CONF_TYPE_STRING, 0, 0, 0, NULL },
{ "pageurl", ST_OFF(pageUrl), CONF_TYPE_STRING, 0, 0, 0, NULL },
{ "tcurl", ST_OFF(tcUrl), CONF_TYPE_STRING, 0, 0, 0, NULL },
{ "app", ST_OFF(app), CONF_TYPE_STRING, 0, 0, 0, NULL },
{ "playpath", ST_OFF(playpath), CONF_TYPE_STRING, 0, 0, 0, NULL },
{ "token", ST_OFF(token), CONF_TYPE_STRING, 0, 0, 0, NULL },
{ "islive", ST_OFF(isLive), CONF_TYPE_FLAG, 0, 0, 1, NULL },
{ "swfverify", ST_OFF(swfVerify), CONF_TYPE_FLAG, 0, 0, 1, NULL },
extra good karma for aligning this
Done.
stream->priv = r;
stream->type = STREAMTYPE_STREAM;
stream->fill_buffer = rtmp_stream_read;
stream->control = rtmp_stream_ctrl;
stream->close = rtmp_stream_close;
align
Done.
After chatting on #mplayerdev with uau and Compn I've implemented the options
by piggybacking them on the main URL. That's all working well now, but seeking
is still broken.
But this works:
mplayer
"rtmpe://cdn-auth.akamai.crackle.com/ondemand/crackle/1/n/sk/6dpvb_306p.mp4
swfurl=http://crackle.com/flash/CracklePlayer.swf?id=2419511&site=219
pageurl=http://www.youtube.com/watch?v=yPeNnReBK0A&feature=spotlight swfverify=1"
-- Howard Chu
CTO, Symas Corp. http://www.symas.com
Director, Highland Sun http://highlandsun.com/hyc/
Chief Architect, OpenLDAP http://www.openldap.org/project/
/*
* stream layer for RTMP, based on rtmpdump librtmp.
*
* Copyright (C) 2010 Howard Chu
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "stream.h"
#include "m_option.h"
#include "m_struct.h"
#include "libmpdemux/demuxer.h"
#include "librtmp/rtmp.h"
static struct rtmp_params {
AVal swfUrl;
AVal pageUrl;
AVal tcUrl;
AVal app;
AVal playpath;
AVal token;
int isLive;
int swfVerify;
} rtmp_dflts = {
{
NULL, 0}, {
NULL, 0}, {
NULL, 0}, {
NULL, 0}, {
NULL, 0}, {
NULL, 0}, 0, 0};
#define ST_OFF(f) M_ST_OFF(struct rtmp_params,f)
static const m_option_t rtmp_params_fields[] = {
{"swfurl", ST_OFF(swfUrl), CONF_TYPE_STRING, 0, 0, 0, NULL},
{"pageurl", ST_OFF(pageUrl), CONF_TYPE_STRING, 0, 0, 0, NULL},
{"tcurl", ST_OFF(tcUrl), CONF_TYPE_STRING, 0, 0, 0, NULL},
{"app", ST_OFF(app), CONF_TYPE_STRING, 0, 0, 0, NULL},
{"playpath", ST_OFF(playpath), CONF_TYPE_STRING, 0, 0, 0, NULL},
{"token", ST_OFF(token), CONF_TYPE_STRING, 0, 0, 0, NULL},
{"islive", ST_OFF(isLive), CONF_TYPE_FLAG, 0, 0, 1, NULL},
{"swfverify", ST_OFF(swfVerify), CONF_TYPE_FLAG, 0, 0, 1, NULL},
{NULL, NULL, 0, 0, 0, 0, NULL}
};
static const struct m_struct_st stream_opts = {
"rtmp",
sizeof(struct rtmp_params),
&rtmp_dflts,
rtmp_params_fields
};
struct rtmp_state {
RTMP r;
struct rtmp_params *opts;
};
static int
rtmp_stream_read(int fd, char *buffer, int size,
streaming_ctrl_t *streaming_ctrl)
{
struct rtmp_state *rs = streaming_ctrl->data;
return RTMP_Read(&rs->r, buffer, size);
}
static int rtmp_stream_ctrl(stream_t *s, int cmd, void *arg)
{
struct rtmp_state *rs = s->priv;
switch (cmd) {
case STREAM_CTRL_GET_TIME_LENGTH:
*(double *) arg = RTMP_GetDuration(&rs->r);
return 1;
case STREAM_CTRL_GET_CURRENT_TIME:
*(double *) arg = rs->r.m_mediaStamp / 1000.0;
return 1;
case STREAM_CTRL_SEEK_TO_TIME:
RTMP_SendSeek(&rs->r, *(double *) arg * 1000.0);
return 1;
}
return STREAM_UNSUPPORTED;
}
static int rtmp_stream_seek(stream_t *s, off_t pos)
{
struct rtmp_state *rs = s->priv;
if (pos == 0) {
RTMP_SendSeek(&rs->r, 0.0);
return 1;
}
return 0;
}
static void rtmp_stream_close(stream_t *s)
{
struct rtmp_state *rs = s->priv;
s->streaming_ctrl->data = NULL;
streaming_ctrl_free(s->streaming_ctrl);
s->streaming_ctrl = NULL;
RTMP_Close(&rs->r);
free((char *) rs->r.Link.hostname);
m_struct_free(&stream_opts, rs->opts);
free(rs);
}
#define SWF_AGE 30 /* days */
#define RTMP_TIMEOUT 120 /* seconds */
#define BUFFERTIME 20000 /* msecs */
static int
rtmp_stream_open(stream_t *stream, int mode, void *opts, int *file_format)
{
struct rtmp_params *p = (struct rtmp_params *) opts;
struct rtmp_state *rs = NULL;
int proto, rc;
unsigned int port = 0, swfSize;
AVal playpath = { NULL, 0 }, app, tcUrl, swfHash;
char *host = NULL;
unsigned char hash[HASHLEN];
extern int network_bandwidth;
char *ptr, *p1, *p2;
mp_msg(MSGT_OPEN, MSGL_INFO, "STREAM_RTMP, URL: %s\n", stream->url);
if (mode != STREAM_READ) {
rc = STREAM_UNSUPPORTED;
goto fail;
}
ptr = strchr(stream->url, ' ');
if (ptr) {
m_struct_t *desc = (m_struct_t *) & stream_opts;
while (ptr) {
*ptr++ = '\0';
p1 = ptr;
p2 = strchr(p1, '=');
if (!p2)
break;
*p2++ = '\0';
ptr = strchr(p2, ' ');
if (ptr)
*ptr = '\0';
if (!m_struct_set(desc, opts, p1, p2)) {
rc = STREAM_UNSUPPORTED;
goto fail;
}
}
}
if (!RTMP_ParseURL(stream->url, &proto, &host, &port, &playpath, &app)) {
rc = STREAM_UNSUPPORTED;
goto fail;
}
rs = malloc(sizeof(struct rtmp_state));
if (!rs) {
rc = STREAM_ERROR;
goto fail;
}
stream->streaming_ctrl = streaming_ctrl_new();
if (!stream->streaming_ctrl) {
rc = STREAM_ERROR;
goto fail;
}
stream->streaming_ctrl->bandwidth = network_bandwidth;
RTMP_Init(&rs->r);
RTMP_SetBufferMS(&rs->r, BUFFERTIME);
if (p->swfUrl.av_val)
p->swfUrl.av_len = strlen(p->swfUrl.av_val);
if (p->pageUrl.av_val)
p->pageUrl.av_len = strlen(p->pageUrl.av_val);
if (p->tcUrl.av_val)
p->tcUrl.av_len = strlen(p->tcUrl.av_val);
if (p->app.av_val)
p->app.av_len = strlen(p->app.av_val);
if (p->playpath.av_val)
p->playpath.av_len = strlen(p->playpath.av_val);
if (p->token.av_val)
p->token.av_len = strlen(p->token.av_val);
if (playpath.av_len) {
if (p->playpath.av_len) {
free(playpath.av_val);
} else {
p->playpath = playpath;
}
playpath.av_val = NULL;
playpath.av_len = 0;
}
if (p->tcUrl.av_len) {
tcUrl = p->tcUrl;
} else {
tcUrl.av_val = stream->url;
tcUrl.av_len = app.av_len + (app.av_val - stream->url);
}
if (p->swfUrl.av_len && p->swfVerify &&
RTMP_HashSWF(p->swfUrl.av_val, &swfSize, hash, SWF_AGE) == 0) {
swfHash.av_val = (char *)hash;
swfHash.av_len = HASHLEN;
} else {
swfHash.av_val = NULL;
swfHash.av_len = 0;
swfSize = 0;
}
RTMP_SetupStream(&rs->r, proto, host, port, NULL, &p->playpath, &tcUrl,
&p->swfUrl, &p->pageUrl, &app, NULL, &swfHash,
swfSize, NULL, NULL, 0, 0, p->isLive, RTMP_TIMEOUT);
rs->r.Link.token = p->token;
if (!RTMP_Connect(&rs->r, NULL) || !RTMP_ConnectStream(&rs->r, 0, 0)) {
mp_msg(MSGT_NETWORK, MSGL_ERR, "RTMP_Connect failed\n");
rc = STREAM_ERROR;
goto fail;
}
rs->opts = opts;
stream->streaming_ctrl->data = rs;
stream->streaming_ctrl->streaming_read = rtmp_stream_read;
stream->streaming_ctrl->streaming_seek = NULL;
stream->streaming_ctrl->prebuffer_size = 128 * 1024;
stream->streaming_ctrl->buffering = 1;
stream->streaming_ctrl->status = streaming_playing_e;
stream->seek = rtmp_stream_seek;
stream->control = rtmp_stream_ctrl;
stream->close = rtmp_stream_close;
stream->fd = rs->r.m_sb.sb_socket;
stream->type = STREAMTYPE_STREAM;
stream->priv = rs;
*file_format = DEMUXER_TYPE_LAVF_PREFERRED;
fixup_network_stream_cache(stream);
return STREAM_OK;
fail:
if (host)
free(host);
if (playpath.av_val)
free(playpath.av_val);
if (rs)
free(rs);
m_struct_free(&stream_opts, opts);
return rc;
}
const stream_info_t stream_info_rtmp = {
"FLV over RTMP streaming",
"rtmp",
"Howard Chu",
"native rtmp support",
rtmp_stream_open,
{"rtmp", "rtmpt", "rtmpe", "rtmpte", "rtmps", NULL},
&stream_opts,
0 /* Urls are complicated */
};
_______________________________________________
MPlayer-dev-eng mailing list
MPlayer-dev-eng@xxxxxxxxxxxx
https://lists.mplayerhq.hu/mailman/listinfo/mplayer-dev-eng