rtmp: support connection parameters

Allow using connection parameters in order to append arbitrary
AMF data like "B:1 S:authMe O:1 NN:code:1.23 NS:flag:ok O:0" to the
Connect message. You can pass these parameters through the -rtmp_conn
option.

Signed-off-by: Martin Storsjö <martin@martin.st>
This commit is contained in:
Samuel Pitoiset 2012-06-08 13:16:34 +02:00 committed by Martin Storsjö
parent b4c92e94bb
commit 8ee3e1874e
2 changed files with 89 additions and 0 deletions

View File

@ -198,6 +198,18 @@ Additionally, the following parameters can be set via command line options
Name of application to connect on the RTMP server. This option Name of application to connect on the RTMP server. This option
overrides the parameter specified in the URI. overrides the parameter specified in the URI.
@item rtmp_conn
Extra arbitrary AMF connection parameters, parsed from a string,
e.g. like @code{B:1 S:authMe O:1 NN:code:1.23 NS:flag:ok O:0}.
Each value is prefixed by a single character denoting the type,
B for Boolean, N for number, S for string, O for object, or Z for null,
followed by a colon. For Booleans the data must be either 0 or 1 for
FALSE or TRUE, respectively. Likewise for Objects the data must be 0 or
1 to end or begin an object, respectively. Data items in subobjects may
be named, by prefixing the type with 'N' and specifying the name before
the value (i.e. @code{NB:myFlag:1}). This option may be used multiple
times to construct arbitrary AMF sequences.
@item rtmp_flashver @item rtmp_flashver
Version of the Flash plugin used to run the SWF player. The default Version of the Flash plugin used to run the SWF player. The default
is LNX 9,0,124,2. is LNX 9,0,124,2.

View File

@ -70,6 +70,7 @@ typedef struct RTMPContext {
char *playpath; ///< stream identifier to play (with possible "mp4:" prefix) char *playpath; ///< stream identifier to play (with possible "mp4:" prefix)
int live; ///< 0: recorded, -1: live, -2: both int live; ///< 0: recorded, -1: live, -2: both
char *app; ///< name of application char *app; ///< name of application
char *conn; ///< append arbitrary AMF data to the Connect message
ClientState state; ///< current state ClientState state; ///< current state
int main_channel_id; ///< an additional channel ID which is used for some invocations int main_channel_id; ///< an additional channel ID which is used for some invocations
uint8_t* flv_data; ///< buffer with data for demuxer uint8_t* flv_data; ///< buffer with data for demuxer
@ -112,6 +113,65 @@ static const uint8_t rtmp_server_key[] = {
0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
}; };
static int rtmp_write_amf_data(URLContext *s, char *param, uint8_t **p)
{
char *field, *value, *saveptr;
char type;
/* The type must be B for Boolean, N for number, S for string, O for
* object, or Z for null. For Booleans the data must be either 0 or 1 for
* FALSE or TRUE, respectively. Likewise for Objects the data must be
* 0 or 1 to end or begin an object, respectively. Data items in subobjects
* may be named, by prefixing the type with 'N' and specifying the name
* before the value (ie. NB:myFlag:1). This option may be used multiple times
* to construct arbitrary AMF sequences. */
if (param[0] && param[1] == ':') {
type = param[0];
value = param + 2;
} else if (param[0] == 'N' && param[1] && param[2] == ':') {
type = param[1];
field = strtok_r(param + 3, ":", &saveptr);
value = strtok_r(NULL, ":", &saveptr);
if (!field || !value)
goto fail;
ff_amf_write_field_name(p, field);
} else {
goto fail;
}
switch (type) {
case 'B':
ff_amf_write_bool(p, value[0] != '0');
break;
case 'S':
ff_amf_write_string(p, value);
break;
case 'N':
ff_amf_write_number(p, strtod(value, NULL));
break;
case 'Z':
ff_amf_write_null(p);
break;
case 'O':
if (value[0] != '0')
ff_amf_write_object_start(p);
else
ff_amf_write_object_end(p);
break;
default:
goto fail;
break;
}
return 0;
fail:
av_log(s, AV_LOG_ERROR, "Invalid AMF parameter: %s\n", param);
return AVERROR(EINVAL);
}
/** /**
* Generate 'connect' call and send it to the server. * Generate 'connect' call and send it to the server.
*/ */
@ -165,6 +225,22 @@ static int gen_connect(URLContext *s, RTMPContext *rt)
} }
ff_amf_write_object_end(&p); ff_amf_write_object_end(&p);
if (rt->conn) {
char *param, *saveptr;
// Write arbitrary AMF data to the Connect message.
param = strtok_r(rt->conn, " ", &saveptr);
while (param != NULL) {
if ((ret = rtmp_write_amf_data(s, param, &p)) < 0) {
// Invalid AMF parameter.
ff_rtmp_packet_destroy(&pkt);
return ret;
}
param = strtok_r(NULL, " ", &saveptr);
}
}
pkt.data_size = p - pkt.data; pkt.data_size = p - pkt.data;
ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
@ -1248,6 +1324,7 @@ static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
static const AVOption rtmp_options[] = { static const AVOption rtmp_options[] = {
{"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
{"rtmp_conn", "Append arbitrary AMF data to the Connect message", OFFSET(conn), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
{"rtmp_flashver", "Version of the Flash plugin used to run the SWF player.", OFFSET(flashver), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, {"rtmp_flashver", "Version of the Flash plugin used to run the SWF player.", OFFSET(flashver), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
{"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {-2}, INT_MIN, INT_MAX, DEC, "rtmp_live"}, {"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {-2}, INT_MIN, INT_MAX, DEC, "rtmp_live"},
{"any", "both", 0, AV_OPT_TYPE_CONST, {-2}, 0, 0, DEC, "rtmp_live"}, {"any", "both", 0, AV_OPT_TYPE_CONST, {-2}, 0, 0, DEC, "rtmp_live"},