diff options
author | David Schleef <ds@schleef.org> | 2014-08-27 02:05:14 -0700 |
---|---|---|
committer | David Schleef <ds@schleef.org> | 2014-08-27 02:05:14 -0700 |
commit | cbf9572bc7d54b43b77f14b28de59f7bd80dd7d5 (patch) | |
tree | 5534ee20770e07b0b48085a6fa12c49424ea9c5b | |
parent | 8b025005d2bff72434fd6fe0125109a6f23d2390 (diff) |
hacking. amf parsing works
-rw-r--r-- | rtmp/amf.c | 114 | ||||
-rw-r--r-- | rtmp/amf.h | 4 | ||||
-rw-r--r-- | rtmp/rtmpconnection.c | 17 | ||||
-rw-r--r-- | tools/proxy-server.c | 24 |
4 files changed, 139 insertions, 20 deletions
@@ -35,7 +35,7 @@ struct _AmfObjectField typedef struct _AmfParser AmfParser; struct _AmfParser { - const char *data; + const guint8 *data; gsize size; int offset; gboolean error; @@ -54,7 +54,8 @@ gst_amf_node_new (GstAmfType type) node = g_malloc0 (sizeof (GstAmfNode)); node->type = type; - if (node->type == GST_AMF_TYPE_OBJECT) { + if (node->type == GST_AMF_TYPE_OBJECT || + node->type == GST_AMF_TYPE_ECMA_ARRAY) { node->array_val = g_ptr_array_new (); } @@ -92,7 +93,6 @@ _parse_u16 (AmfParser * parser) return x; } -#if 0 static int _parse_u24 (AmfParser * parser) { @@ -115,6 +115,16 @@ _parse_u32 (AmfParser * parser) parser->offset += 4; return x; } + +#if 0 +static guint8 * +_parse_array (AmfParser * parser, int size) +{ + guint8 *data; + data = g_memdup (parser->data + parser->offset, size); + parser->offset += size; + return data; +} #endif static int @@ -137,11 +147,12 @@ _parse_utf8_string (AmfParser * parser) char *s; size = _parse_u16 (parser); - if (parser->offset + size >= parser->size) { + if (parser->offset + size > parser->size) { + GST_ERROR ("string too long"); parser->error = TRUE; return NULL; } - s = g_strndup (parser->data + parser->offset, size); + s = g_strndup ((gchar *) (parser->data + parser->offset), size); parser->offset += size; return s; @@ -163,6 +174,33 @@ _parse_object (AmfParser * parser, GstAmfNode * node) } } +static void +_parse_ecma_array (AmfParser * parser, GstAmfNode * node) +{ + int n_elements; + int i; + + n_elements = _parse_u32 (parser); + + /* FIXME This is weird. The one time I've seen this, the encoded value + * was 0, but the number of elements was 1. */ + if (n_elements != 0) { + GST_ERROR ("unimplemented, n_elements != 0"); + } + + if (n_elements == 0) + n_elements++; + + for (i = 0; i < n_elements; i++) { + char *s; + GstAmfNode *child_node; + s = _parse_utf8_string (parser); + child_node = _parse_value (parser); + gst_amf_object_append_take (node, s, child_node); + } + _parse_u24 (parser); +} + static GstAmfNode * _parse_value (AmfParser * parser) { @@ -188,14 +226,17 @@ _parse_value (AmfParser * parser) _parse_object (parser, node); break; case GST_AMF_TYPE_MOVIECLIP: - GST_ERROR ("unimplemented"); + GST_ERROR ("unimplemented AMF type: movie clip"); break; case GST_AMF_TYPE_NULL: break; + case GST_AMF_TYPE_ECMA_ARRAY: + _parse_ecma_array (parser, node); + break; case GST_AMF_TYPE_OBJECT_END: break; default: - GST_ERROR ("unimplemented"); + GST_ERROR ("unimplemented AMF type %d", type); break; } @@ -203,7 +244,7 @@ _parse_value (AmfParser * parser) } GstAmfNode * -gst_amf_node_new_parse (const char *data, int size, int *n_bytes) +gst_amf_node_new_parse (const guint8 * data, gsize size, gsize * n_bytes) { AmfParser _p = { 0 }, *parser = &_p; GstAmfNode *node; @@ -250,7 +291,8 @@ gst_amf_object_append_take (GstAmfNode * node, char *s, GstAmfNode * child_node) { AmfObjectField *field; - g_return_if_fail (node->type == GST_AMF_TYPE_OBJECT); + g_return_if_fail (node->type == GST_AMF_TYPE_OBJECT || + node->type == GST_AMF_TYPE_ECMA_ARRAY); field = g_malloc0 (sizeof (AmfObjectField)); field->name = s; @@ -265,3 +307,57 @@ amf_object_field_free (AmfObjectField * field) gst_amf_node_free (field->value); g_free (field); } + +void +gst_amf_node_set_ecma_array (GstAmfNode * node, guint8 * data, int size) +{ + node->string_val = (char *) data; + node->int_val = size; +} + +static void +_gst_amf_node_dump (GstAmfNode * node, int indent) +{ + int i; + + switch (node->type) { + case GST_AMF_TYPE_NUMBER: + g_print ("%g", node->double_val); + break; + case GST_AMF_TYPE_BOOLEAN: + g_print ("%s", node->int_val ? "True" : "False"); + break; + case GST_AMF_TYPE_STRING: + g_print ("\"%s\"", node->string_val); + break; + case GST_AMF_TYPE_OBJECT: + case GST_AMF_TYPE_ECMA_ARRAY: + g_print ("{\n"); + for (i = 0; i < node->array_val->len; i++) { + AmfObjectField *field = g_ptr_array_index (node->array_val, i); + g_print ("%*.*s \"%s\": ", indent, indent, "", field->name); + _gst_amf_node_dump (field->value, indent + 2); + g_print (",\n"); + } + g_print ("%*.*s}", indent, indent, ""); + break; + case GST_AMF_TYPE_MOVIECLIP: + g_print ("MOVIE_CLIP"); + break; + case GST_AMF_TYPE_NULL: + g_print ("Null"); + break; + case GST_AMF_TYPE_OBJECT_END: + break; + default: + GST_ERROR ("unimplemented AMF type: %d", node->type); + break; + } +} + +void +gst_amf_node_dump (GstAmfNode * node) +{ + _gst_amf_node_dump (node, 0); + g_print ("\n"); +} @@ -57,13 +57,15 @@ typedef struct _GstAmfNode GstAmfNode; GstAmfNode * gst_amf_node_new (GstAmfType type); void gst_amf_node_free (GstAmfNode *node); +void gst_amf_node_dump (GstAmfNode *node); -GstAmfNode * gst_amf_node_new_parse (const char *data, int size, int *n_bytes); +GstAmfNode * gst_amf_node_new_parse (const guint8 *data, gsize size, gsize *n_bytes); void gst_amf_node_set_boolean (GstAmfNode *node, gboolean val); void gst_amf_node_set_double (GstAmfNode *node, double val); void gst_amf_node_set_string (GstAmfNode *node, const char *s); void gst_amf_node_set_string_take (GstAmfNode *node, char *s); +void gst_amf_node_set_ecma_array (GstAmfNode *node, guint8 *data, int size); void gst_amf_object_append_take (GstAmfNode *node, char *s, GstAmfNode *child_node); diff --git a/rtmp/rtmpconnection.c b/rtmp/rtmpconnection.c index 2e9e919..f217f22 100644 --- a/rtmp/rtmpconnection.c +++ b/rtmp/rtmpconnection.c @@ -335,29 +335,26 @@ G_GNUC_UNUSED static void parse_message (guint8 * data, int size) { int offset; - int bytes_read; + gsize bytes_read; GstAmfNode *node; offset = 4; - node = gst_amf_node_new_parse ((const char *) (data + offset), - size - offset, &bytes_read); + node = gst_amf_node_new_parse (data + offset, size - offset, &bytes_read); offset += bytes_read; - g_print ("bytes_read: %d\n", bytes_read); + g_print ("bytes_read: %" G_GSIZE_FORMAT "\n", bytes_read); if (node) gst_amf_node_free (node); - node = gst_amf_node_new_parse ((const char *) (data + offset), - size - offset, &bytes_read); + node = gst_amf_node_new_parse (data + offset, size - offset, &bytes_read); offset += bytes_read; - g_print ("bytes_read: %d\n", bytes_read); + g_print ("bytes_read: %" G_GSIZE_FORMAT "\n", bytes_read); if (node) gst_amf_node_free (node); - node = gst_amf_node_new_parse ((const char *) (data + offset), - size - offset, &bytes_read); + node = gst_amf_node_new_parse (data + offset, size - offset, &bytes_read); offset += bytes_read; - g_print ("bytes_read: %d\n", bytes_read); + g_print ("bytes_read: %" G_GSIZE_FORMAT "\n", bytes_read); if (node) gst_amf_node_free (node); diff --git a/tools/proxy-server.c b/tools/proxy-server.c index db177e2..6a77377 100644 --- a/tools/proxy-server.c +++ b/tools/proxy-server.c @@ -24,6 +24,7 @@ #include <gst/gst.h> #include <gio/gio.h> #include <stdlib.h> +#include "amf.h" #include "rtmpserver.h" #include "rtmpclient.h" #include "rtmputils.h" @@ -189,6 +190,25 @@ periodic (gpointer user_data) } static void +dump_command (GstRtmpChunk * chunk) +{ + GstAmfNode *amf; + gsize size; + const guint8 *data; + gsize n_parsed; + int offset; + + offset = 0; + data = g_bytes_get_data (chunk->payload, &size); + while (offset < size) { + amf = gst_amf_node_new_parse (data + offset, size - offset, &n_parsed); + gst_amf_node_dump (amf); + gst_amf_node_free (amf); + offset += n_parsed; + } +} + +static void dump_chunk (GstRtmpChunk * chunk, gboolean dir) { if (!dump) @@ -199,5 +219,9 @@ dump_chunk (GstRtmpChunk * chunk, gboolean dir) chunk->stream_id, chunk->timestamp, chunk->message_length, chunk->message_type_id, chunk->info); + if (chunk->stream_id == 3 && + chunk->message_type_id == 20) { + dump_command (chunk); + } gst_rtmp_dump_data (gst_rtmp_chunk_get_payload (chunk)); } |