summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Schleef <ds@schleef.org>2014-08-27 02:05:14 -0700
committerDavid Schleef <ds@schleef.org>2014-08-27 02:05:14 -0700
commitcbf9572bc7d54b43b77f14b28de59f7bd80dd7d5 (patch)
tree5534ee20770e07b0b48085a6fa12c49424ea9c5b
parent8b025005d2bff72434fd6fe0125109a6f23d2390 (diff)
hacking. amf parsing works
-rw-r--r--rtmp/amf.c114
-rw-r--r--rtmp/amf.h4
-rw-r--r--rtmp/rtmpconnection.c17
-rw-r--r--tools/proxy-server.c24
4 files changed, 139 insertions, 20 deletions
diff --git a/rtmp/amf.c b/rtmp/amf.c
index 153f663..c295d1a 100644
--- a/rtmp/amf.c
+++ b/rtmp/amf.c
@@ -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");
+}
diff --git a/rtmp/amf.h b/rtmp/amf.h
index 00b092a..c782e06 100644
--- a/rtmp/amf.h
+++ b/rtmp/amf.h
@@ -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));
}