/** * test the stream utility, access empty */ VOID TEST(KernelStreamTest, StreamEmpty) { SrsStream s; char data[1024]; EXPECT_TRUE(s.empty()); EXPECT_TRUE(ERROR_SUCCESS == s.initialize(data, 1024)); EXPECT_FALSE(s.empty()); s.read_bytes(data, 1024); EXPECT_TRUE(s.empty()); }
/** * test the stream utility, read string */ VOID TEST(KernelStreamTest, StreamReadString) { SrsStream s; char data[] = "Hello, world!"; EXPECT_TRUE(ERROR_SUCCESS == s.initialize(data, sizeof(data) - 1)); string str = s.read_string(2); EXPECT_STREQ("He", str.c_str()); str = s.read_string(2); EXPECT_STREQ("ll", str.c_str()); s.skip(3); str = s.read_string(6); EXPECT_STREQ("world!", str.c_str()); EXPECT_TRUE(s.empty()); }
/** * test the stream utility, read bytes */ VOID TEST(KernelStreamTest, StreamReadBytes) { SrsStream s; char data[] = "Hello, world!"; EXPECT_TRUE(ERROR_SUCCESS == s.initialize(data, sizeof(data) - 1)); char bytes[64]; s.read_bytes(bytes, 2); bytes[2] = 0; EXPECT_STREQ("He", bytes); s.read_bytes(bytes, 2); bytes[2] = 0; EXPECT_STREQ("ll", bytes); s.skip(3); s.read_bytes(bytes, 6); bytes[6] = 0; EXPECT_STREQ("world!", bytes); EXPECT_TRUE(s.empty()); }
// user scenario: coding and decoding with amf0 VOID TEST(AMF0Test, ScenarioMain) { // coded amf0 object int nb_bytes = 0; char* bytes = NULL; // coding data to binaries by amf0 // for example, send connect app response to client. if (true) { // props: object // fmsVer: string // capabilities: number // mode: number // info: object // level: string // code: string // descrption: string // objectEncoding: number // data: array // version: string // srs_sig: string SrsAmf0Object* props = SrsAmf0Any::object(); SrsAutoFree(SrsAmf0Object, props); props->set("fmsVer", SrsAmf0Any::str("FMS/3,5,3,888")); props->set("capabilities", SrsAmf0Any::number(253)); props->set("mode", SrsAmf0Any::number(123)); SrsAmf0Object* info = SrsAmf0Any::object(); SrsAutoFree(SrsAmf0Object, info); info->set("level", SrsAmf0Any::str("info")); info->set("code", SrsAmf0Any::str("NetStream.Connnect.Success")); info->set("descrption", SrsAmf0Any::str("connected")); info->set("objectEncoding", SrsAmf0Any::number(3)); SrsAmf0EcmaArray* data = SrsAmf0Any::ecma_array(); info->set("data", data); data->set("version", SrsAmf0Any::str("FMS/3,5,3,888")); data->set("srs_sig", SrsAmf0Any::str("srs")); // buf store the serialized props/info nb_bytes = props->total_size() + info->total_size(); ASSERT_GT(nb_bytes, 0); bytes = new char[nb_bytes]; // use SrsStream to write props/info to binary buf. SrsStream s; EXPECT_EQ(ERROR_SUCCESS, s.initialize(bytes, nb_bytes)); EXPECT_EQ(ERROR_SUCCESS, props->write(&s)); EXPECT_EQ(ERROR_SUCCESS, info->write(&s)); EXPECT_TRUE(s.empty()); // now, user can use the buf EXPECT_EQ(0x03, bytes[0]); EXPECT_EQ(0x09, bytes[nb_bytes - 1]); } SrsAutoFree(char, bytes); // decoding amf0 object from bytes // when user know the schema if (true) { ASSERT_TRUE(NULL != bytes); // use SrsStream to assist amf0 object to read from bytes. SrsStream s; EXPECT_EQ(ERROR_SUCCESS, s.initialize(bytes, nb_bytes)); // decoding // if user know the schema, for instance, it's an amf0 object, // user can use specified object to decoding. SrsAmf0Object* props = SrsAmf0Any::object(); SrsAutoFree(SrsAmf0Object, props); EXPECT_EQ(ERROR_SUCCESS, props->read(&s)); // user can use specified object to decoding. SrsAmf0Object* info = SrsAmf0Any::object(); SrsAutoFree(SrsAmf0Object, info); EXPECT_EQ(ERROR_SUCCESS, info->read(&s)); // use the decoded data. SrsAmf0Any* prop = NULL; // if user requires specified property, use ensure of amf0 object EXPECT_TRUE(NULL != (prop = props->ensure_property_string("fmsVer"))); // the property can assert to string. ASSERT_TRUE(prop->is_string()); // get the prop string value. EXPECT_STREQ("FMS/3,5,3,888", prop->to_str().c_str()); // get other type property value EXPECT_TRUE(NULL != (prop = info->get_property("data"))); // we cannot assert the property is ecma array if (prop->is_ecma_array()) { SrsAmf0EcmaArray* data = prop->to_ecma_array(); // it must be a ecma array. ASSERT_TRUE(NULL != data); // get property of array EXPECT_TRUE(NULL != (prop = data->ensure_property_string("srs_sig"))); ASSERT_TRUE(prop->is_string()); EXPECT_STREQ("srs", prop->to_str().c_str()); } // confidence about the schema EXPECT_TRUE(NULL != (prop = info->ensure_property_string("level"))); ASSERT_TRUE(prop->is_string()); EXPECT_STREQ("info", prop->to_str().c_str()); } // use any to decoding it, // if user donot know the schema if (true) { ASSERT_TRUE(NULL != bytes); // use SrsStream to assist amf0 object to read from bytes. SrsStream s; EXPECT_EQ(ERROR_SUCCESS, s.initialize(bytes, nb_bytes)); // decoding a amf0 any, for user donot know SrsAmf0Any* any = NULL; EXPECT_EQ(ERROR_SUCCESS, srs_amf0_read_any(&s, &any)); SrsAutoFree(SrsAmf0Any, any); // for amf0 object if (any->is_object()) { SrsAmf0Object* obj = any->to_object(); ASSERT_TRUE(NULL != obj); // use foreach to process properties for (int i = 0; i < obj->count(); ++i) { string name = obj->key_at(i); SrsAmf0Any* value = obj->value_at(i); // use the property name EXPECT_TRUE("" != name); // use the property value EXPECT_TRUE(NULL != value); } } } }
int SrsSource::on_aggregate(SrsMessage* msg) { int ret = ERROR_SUCCESS; SrsStream* stream = aggregate_stream; if ((ret = stream->initialize((char*)msg->payload, msg->size)) != ERROR_SUCCESS) { return ret; } while (!stream->empty()) { if (!stream->require(1)) { ret = ERROR_RTMP_AGGREGATE; srs_error("invalid aggregate message type. ret=%d", ret); return ret; } int8_t type = stream->read_1bytes(); if (!stream->require(3)) { ret = ERROR_RTMP_AGGREGATE; srs_error("invalid aggregate message size. ret=%d", ret); return ret; } int32_t data_size = stream->read_3bytes(); if (data_size < 0) { ret = ERROR_RTMP_AGGREGATE; srs_error("invalid aggregate message size(negative). ret=%d", ret); return ret; } if (!stream->require(3)) { ret = ERROR_RTMP_AGGREGATE; srs_error("invalid aggregate message time. ret=%d", ret); return ret; } int32_t timestamp = stream->read_3bytes(); if (!stream->require(1)) { ret = ERROR_RTMP_AGGREGATE; srs_error("invalid aggregate message time(high). ret=%d", ret); return ret; } int32_t time_h = stream->read_1bytes(); timestamp |= time_h<<24; timestamp &= 0x7FFFFFFF; if (!stream->require(3)) { ret = ERROR_RTMP_AGGREGATE; srs_error("invalid aggregate message stream_id. ret=%d", ret); return ret; } int32_t stream_id = stream->read_3bytes(); if (data_size > 0 && !stream->require(data_size)) { ret = ERROR_RTMP_AGGREGATE; srs_error("invalid aggregate message data. ret=%d", ret); return ret; } // to common message. SrsCommonMessage __o; SrsMessage& o = __o; o.header.message_type = type; o.header.payload_length = data_size; o.header.timestamp_delta = timestamp; o.header.timestamp = timestamp; o.header.stream_id = stream_id; o.header.perfer_cid = msg->header.perfer_cid; if (data_size > 0) { o.size = data_size; o.payload = new char[o.size]; stream->read_bytes(o.payload, o.size); } if (!stream->require(4)) { ret = ERROR_RTMP_AGGREGATE; srs_error("invalid aggregate message previous tag size. ret=%d", ret); return ret; } stream->read_4bytes(); // process parsed message if (o.header.is_audio()) { if ((ret = on_audio(&o)) != ERROR_SUCCESS) { return ret; } } else if (o.header.is_video()) { if ((ret = on_video(&o)) != ERROR_SUCCESS) { return ret; } } } return ret; }