winlin

supprt inject flv

/*
The MIT License (MIT)
Copyright (c) 2013-2014 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SRS_RESEARH_FLV_CODEC_HPP
#define SRS_RESEARH_FLV_CODEC_HPP
/*
#include "srs_flv_codec.h"
*/
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define ERROR_FLV_CODEC_EOF 100
int open_flv_file(char* in_flv_file)
{
return open(in_flv_file, O_RDONLY);
}
void close_flv_file(int fd)
{
if (fd > 0) {
close(fd);
}
}
int flv_open_ic(int flv_fd)
{
int ret = 0;
char h[13]; // 9+4
if (read(flv_fd, h, sizeof(h)) != sizeof(h)) {
ret = -1;
trace("read flv header failed. ret=%d", ret);
return ret;
}
if (h[0] != 'F' || h[1] != 'L' || h[2] != 'V') {
ret = -1;
trace("input is not a flv file. ret=%d", ret);
return ret;
}
return ret;
}
int flv_read_packet(int flv_fd, int* type, u_int32_t* timestamp, char** data, int* size)
{
int ret = 0;
char th[11]; // tag header
char ts[4]; // tag size
int32_t data_size = 0;
u_int32_t time = 0;
char* pp;
// read tag header
if ((ret = read(flv_fd, th, sizeof(th))) != sizeof(th)) {
if (ret == 0) {
return ERROR_FLV_CODEC_EOF;
}
ret = -1;
trace("read flv tag header failed. ret=%d", ret);
return ret;
}
// Reserved UB [2]
// Filter UB [1]
// TagType UB [5]
*type = (int)(th[0] & 0x1F);
// DataSize UI24
pp = (char*)&data_size;
pp[2] = th[1];
pp[1] = th[2];
pp[0] = th[3];
// Timestamp UI24
pp = (char*)&time;
pp[2] = th[4];
pp[1] = th[5];
pp[0] = th[6];
// TimestampExtended UI8
pp[3] = th[7];
*timestamp = time;
// check data size.
if (data_size <= 0) {
ret = -1;
trace("invalid data size. size=%d, ret=%d", data_size, ret);
return ret;
}
// read tag data.
*size = data_size;
*data = (char*)malloc(data_size);
if ((ret = read(flv_fd, *data, data_size)) != data_size) {
if (ret == 0) {
return ERROR_FLV_CODEC_EOF;
}
ret = -1;
trace("read flv tag data failed. size=%d, ret=%d", data_size, ret);
return ret;
}
// ignore 4bytes tag size.
if ((ret = read(flv_fd, ts, sizeof(ts))) != sizeof(ts)) {
if (ret == 0) {
return ERROR_FLV_CODEC_EOF;
}
ret = -1;
trace("read flv tag size failed. ret=%d", ret);
return ret;
}
return 0;
}
#endif
... ... @@ -34,7 +34,8 @@ gcc srs_flv_injecter.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_flv_i
#include "../../objs/include/srs_librtmp.h"
#include "srs_research_public.h"
#include "srs_flv_codec.h"
#define ERROR_INJECTED 10000
int process(const char* in_flv_file, const char* out_flv_file, srs_flv_t* pic, srs_flv_t* poc);
int inject_flv(srs_flv_t ic, srs_flv_t oc);
... ... @@ -87,7 +88,12 @@ int main(int argc, char** argv)
if (ret != 0) {
unlink(tmp_file);
trace("error, remove tmp file.");
if (ret == ERROR_INJECTED) {
ret = 0;
trace("file already injected.");
} else {
trace("error, remove tmp file.");
}
} else {
rename(tmp_file, out_flv_file);
trace("completed, rename to %s", out_flv_file);
... ... @@ -153,25 +159,32 @@ int inject_flv(srs_flv_t ic, srs_flv_t oc)
u_int32_t timestamp = 0;
char* data = NULL;
int32_t size;
int64_t offset = 0;
// metadata
srs_amf0_t amf0_name = NULL;
int amf0_name_size = 0;
srs_amf0_t amf0_data = NULL;
srs_amf0_t keyframes = NULL;
srs_amf0_t filepositions = NULL;
srs_amf0_t times = NULL;
// reset to generate metadata
srs_flv_lseek(ic, 0);
if ((ret = srs_flv_read_header(oc, header)) != 0) {
if ((ret = srs_flv_read_header(ic, header)) != 0) {
return ret;
}
trace("start inject flv");
trace("build keyframe infos from flv");
for (;;) {
offset = srs_flv_tellg(ic);
// tag header
if ((ret = srs_flv_read_tag_header(oc, &type, &size, &timestamp)) != 0) {
if ((ret = srs_flv_read_tag_header(ic, &type, &size, &timestamp)) != 0) {
if (srs_flv_is_eof(ret)) {
trace("parse completed.");
return 0;
break;
}
trace("flv get packet failed. ret=%d", ret);
return ret;
... ... @@ -184,16 +197,107 @@ int inject_flv(srs_flv_t ic, srs_flv_t oc)
// TODO: FIXME: mem leak when error.
data = (char*)malloc(size);
if ((ret = srs_flv_read_tag_data(oc, data, size)) != 0) {
if ((ret = srs_flv_read_tag_data(ic, data, size)) != 0) {
return ret;
}
// data tag
if (type == SRS_RTMP_TYPE_VIDEO) {
if (!srs_flv_is_sequence_header(data, size) && srs_flv_is_keyframe(data, size)) {
srs_amf0_strict_array_append(filepositions, srs_amf0_create_number(offset));
srs_amf0_strict_array_append(times, srs_amf0_create_number(((double)timestamp)/ 1000));
}
} else if (type == SRS_RTMP_TYPE_SCRIPT) {
if ((ret = parse_metadata(data, size, &amf0_name, &amf0_data)) != 0) {
return ret;
}
if (srs_amf0_is_object(amf0_data)) {
keyframes = srs_amf0_object_property(amf0_data, "keyframes");
if (keyframes != NULL) {
return 0;
}
keyframes = srs_amf0_create_ecma_array();
srs_amf0_object_property_set(amf0_data, "keyframes", keyframes);
filepositions = srs_amf0_create_strict_array();
srs_amf0_object_property_set(keyframes, "filepositions", filepositions);
times = srs_amf0_create_strict_array();
srs_amf0_object_property_set(keyframes, "times", times);
} else if (srs_amf0_is_ecma_array(amf0_data)) {
keyframes = srs_amf0_ecma_array_property(amf0_data, "keyframes");
if (keyframes != NULL) {
return 0;
}
keyframes = srs_amf0_create_ecma_array();
srs_amf0_ecma_array_property_set(amf0_data, "keyframes", keyframes);
filepositions = srs_amf0_create_strict_array();
srs_amf0_ecma_array_property_set(keyframes, "filepositions", filepositions);
times = srs_amf0_create_strict_array();
srs_amf0_ecma_array_property_set(keyframes, "times", times);
}
}
free(data);
}
// reset to write injected file
srs_flv_lseek(ic, 0);
if ((ret = srs_flv_read_header(ic, header)) != 0) {
return ret;
}
if ((ret = srs_flv_write_header(oc, header)) != 0) {
return ret;
}
// write metadata
if (amf0_name != NULL && amf0_data != NULL) {
amf0_name_size = srs_amf0_size(amf0_name);
size = amf0_name_size + srs_amf0_size(amf0_data);
data = (char*)malloc(size);
if ((ret = srs_amf0_serialize(amf0_name, data, amf0_name_size)) != 0) {
return ret;
}
if ((ret = srs_amf0_serialize(amf0_data, data + amf0_name_size, size - amf0_name_size)) != 0) {
return ret;
}
if ((ret = srs_flv_write_tag(oc, SRS_RTMP_TYPE_SCRIPT, 0, data, size)) != 0) {
return ret;
}
free(data);
}
trace("build keyframe infos from flv");
for (;;) {
// tag header
if ((ret = srs_flv_read_tag_header(ic, &type, &size, &timestamp)) != 0) {
if (srs_flv_is_eof(ret)) {
trace("parse completed.");
return 0;
}
trace("flv get packet failed. ret=%d", ret);
return ret;
}
if (size <= 0) {
trace("invalid size=%d", size);
break;
}
// TODO: FIXME: mem leak when error.
data = (char*)malloc(size);
if ((ret = srs_flv_read_tag_data(ic, data, size)) != 0) {
return ret;
}
// data tag
if (type == SRS_RTMP_TYPE_SCRIPT) {
continue;
}
// copy
if ((ret = srs_flv_write_tag(oc, type, timestamp, data, size)) != 0) {
return ret;
}
free(data);
... ...
... ... @@ -34,7 +34,6 @@ gcc srs_ingest_flv.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_ingest_
#include "../../objs/include/srs_librtmp.h"
#include "srs_research_public.h"
#include "srs_flv_codec.h"
int parse_flv(srs_flv_t flv);
int main(int argc, char** argv)
... ...
... ... @@ -34,9 +34,8 @@ gcc srs_ingest_flv.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_ingest_
#include "../../objs/include/srs_librtmp.h"
#include "srs_research_public.h"
#include "srs_flv_codec.h"
int proxy(int flv_fd, srs_rtmp_t ortmp);
int proxy(srs_flv_t flv, srs_rtmp_t ortmp);
int connect_oc(srs_rtmp_t ortmp);
#define RE_PULSE_MS 300
... ... @@ -59,7 +58,7 @@ int main(int argc, char** argv)
// rtmp handler
srs_rtmp_t ortmp;
// flv handler
int flv_fd;
srs_flv_t flv;
if (argc <= 2) {
printf("ingest flv file and publish to RTMP server\n"
... ... @@ -94,8 +93,7 @@ int main(int argc, char** argv)
trace("input: %s", in_flv_file);
trace("output: %s", out_rtmp_url);
flv_fd = open_flv_file(in_flv_file);
if (flv_fd <= 0) {
if ((flv = srs_flv_open_read(in_flv_file)) == NULL) {
ret = 2;
trace("open flv file failed. ret=%d", ret);
return ret;
... ... @@ -103,31 +101,46 @@ int main(int argc, char** argv)
ortmp = srs_rtmp_create(out_rtmp_url);
ret = proxy(flv_fd, ortmp);
ret = proxy(flv, ortmp);
trace("ingest flv to RTMP completed");
srs_rtmp_destroy(ortmp);
close_flv_file(flv_fd);
srs_flv_close(flv);
return ret;
}
int do_proxy(int flv_fd, srs_rtmp_t ortmp, int64_t re, u_int32_t* ptimestamp)
int do_proxy(srs_flv_t flv, srs_rtmp_t ortmp, int64_t re, u_int32_t* ptimestamp)
{
int ret = 0;
// packet data
int type, size;
char type;
int size;
char* data = NULL;
trace("start ingest flv to RTMP stream");
for (;;) {
if ((ret = flv_read_packet(flv_fd, &type, ptimestamp, &data, &size)) != 0) {
trace("irtmp get packet failed. ret=%d", ret);
// tag header
if ((ret = srs_flv_read_tag_header(flv, &type, &size, ptimestamp)) != 0) {
if (srs_flv_is_eof(ret)) {
trace("parse completed.");
return 0;
}
trace("flv get packet failed. ret=%d", ret);
return ret;
}
if (size <= 0) {
trace("invalid size=%d", size);
break;
}
// TODO: FIXME: mem leak when error.
data = (char*)malloc(size);
if ((ret = srs_flv_read_tag_data(flv, data, size)) != 0) {
return ret;
}
verbose("irtmp got packet: type=%s, time=%d, size=%d",
srs_type2string(type), timestamp, size);
if ((ret = srs_write_packet(ortmp, type, *ptimestamp, data, size)) != 0) {
trace("irtmp get packet failed. ret=%d", ret);
... ... @@ -142,12 +155,13 @@ int do_proxy(int flv_fd, srs_rtmp_t ortmp, int64_t re, u_int32_t* ptimestamp)
return ret;
}
int proxy(int flv_fd, srs_rtmp_t ortmp)
int proxy(srs_flv_t flv, srs_rtmp_t ortmp)
{
int ret = 0;
u_int32_t timestamp = 0;
if ((ret = flv_open_ic(flv_fd)) != 0) {
char header[13];
if ((ret = srs_flv_read_header(flv, header)) != 0) {
return ret;
}
if ((ret = connect_oc(ortmp)) != 0) {
... ... @@ -156,7 +170,7 @@ int proxy(int flv_fd, srs_rtmp_t ortmp)
int64_t re = re_create();
ret = do_proxy(flv_fd, ortmp, re, &timestamp);
ret = do_proxy(flv, ortmp, re, &timestamp);
// for the last pulse, always sleep.
re_cleanup(re, timestamp);
... ...
... ... @@ -207,20 +207,36 @@ int SrsFlvEncoder::write_header()
'F', 'L', 'V', // Signatures "FLV"
(char)0x01, // File version (for example, 0x01 for FLV version 1)
(char)0x00, // 4, audio; 1, video; 5 audio+video.
(char)0x00, (char)0x00, (char)0x00, (char)0x09, // DataOffset UI32 The length of this header in bytes
(char)0x00, (char)0x00, (char)0x00, (char)0x00// PreviousTagSize0 UI32 Always 0
(char)0x00, (char)0x00, (char)0x00, (char)0x09 // DataOffset UI32 The length of this header in bytes
};
// flv specification should set the audio and video flag,
// actually in practise, application generally ignore this flag,
// so we generally set the audio/video to 0.
// write 9bytes header.
if ((ret = write_header(flv_header)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int SrsFlvEncoder::write_header(char flv_header[9])
{
int ret = ERROR_SUCCESS;
// write data.
if ((ret = _fs->write(flv_header, sizeof(flv_header), NULL)) != ERROR_SUCCESS) {
if ((ret = _fs->write(flv_header, 9, NULL)) != ERROR_SUCCESS) {
srs_error("write flv header failed. ret=%d", ret);
return ret;
}
char pts[] = { 0x00, 0x00, 0x00, 0x00 };
if ((ret = _fs->write(pts, 4, NULL)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
... ...
... ... @@ -92,6 +92,7 @@ public:
* that is, 9+4=13bytes.
*/
virtual int write_header();
virtual int write_header(char flv_header[9]);
/**
* write flv metadata.
* serialize from:
... ...
... ... @@ -491,6 +491,34 @@ int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size)
return ret;
}
int srs_flv_write_header(srs_flv_t flv, char header[9])
{
int ret = ERROR_SUCCESS;
FlvContext* context = (FlvContext*)flv;
if ((ret = context->enc.write_header(header)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size)
{
int ret = ERROR_SUCCESS;
FlvContext* context = (FlvContext*)flv;
if (type == SRS_RTMP_TYPE_AUDIO) {
return context->enc.write_audio(time, data, size);
} else if (type == SRS_RTMP_TYPE_VIDEO) {
return context->enc.write_video(time, data, size);
} else {
return context->enc.write_metadata(data, size);
}
return ret;
}
int64_t srs_flv_tellg(srs_flv_t flv)
{
FlvContext* context = (FlvContext*)flv;
... ... @@ -508,6 +536,16 @@ flv_bool srs_flv_is_eof(int error_code)
return error_code == ERROR_SYSTEM_FILE_EOF;
}
flv_bool srs_flv_is_sequence_header(char* data, int32_t size)
{
return SrsCodec::video_is_sequence_header((int8_t*)data, (int)size);
}
flv_bool srs_flv_is_keyframe(char* data, int32_t size)
{
return SrsCodec::video_is_keyframe((int8_t*)data, (int)size);
}
srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed)
{
int ret = ERROR_SUCCESS;
... ... @@ -536,6 +574,21 @@ srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed)
return amf0;
}
srs_amf0_t srs_amf0_create_number(amf0_number value)
{
return SrsAmf0Any::number(value);
}
srs_amf0_t srs_amf0_create_ecma_array()
{
return SrsAmf0Any::ecma_array();
}
srs_amf0_t srs_amf0_create_strict_array()
{
return SrsAmf0Any::strict_array();
}
void srs_amf0_free(srs_amf0_t amf0)
{
SrsAmf0Any* any = (SrsAmf0Any*)amf0;
... ... @@ -547,6 +600,30 @@ void srs_amf0_free_bytes(char* data)
srs_freep(data);
}
int srs_amf0_size(srs_amf0_t amf0)
{
SrsAmf0Any* any = (SrsAmf0Any*)amf0;
return any->total_size();
}
int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size)
{
int ret = ERROR_SUCCESS;
SrsAmf0Any* any = (SrsAmf0Any*)amf0;
SrsStream stream;
if ((ret = stream.initialize(data, size)) != ERROR_SUCCESS) {
return ret;
}
if ((ret = any->write(&stream)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
amf0_bool srs_amf0_is_string(srs_amf0_t amf0)
{
SrsAmf0Any* any = (SrsAmf0Any*)amf0;
... ... @@ -625,6 +702,19 @@ srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index)
return (srs_amf0_t)obj->value_at(index);
}
srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name)
{
SrsAmf0Object* obj = (SrsAmf0Object*)amf0;
return (srs_amf0_t)obj->get_property(name);
}
void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value)
{
SrsAmf0Object* obj = (SrsAmf0Object*)amf0;
SrsAmf0Any* any = (SrsAmf0Any*)value;
obj->set(name, any);
}
int srs_amf0_ecma_array_property_count(srs_amf0_t amf0)
{
SrsAmf0EcmaArray * obj = (SrsAmf0EcmaArray*)amf0;
... ... @@ -643,16 +733,36 @@ srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index)
return (srs_amf0_t)obj->value_at(index);
}
srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name)
{
SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0;
return (srs_amf0_t)obj->get_property(name);
}
void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value)
{
SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0;
SrsAmf0Any* any = (SrsAmf0Any*)value;
obj->set(name, any);
}
int srs_amf0_strict_array_property_count(srs_amf0_t amf0)
{
SrsAmf0EcmaArray * obj = (SrsAmf0EcmaArray*)amf0;
SrsAmf0StrictArray * obj = (SrsAmf0StrictArray*)amf0;
return obj->count();
}
srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index)
{
SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0;
return (srs_amf0_t)obj->value_at(index);
SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0;
return (srs_amf0_t)obj->at(index);
}
void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value)
{
SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0;
SrsAmf0Any* any = (SrsAmf0Any*)value;
obj->append(any);
}
void __srs_fill_level_spaces(stringstream& ss, int level)
... ... @@ -735,10 +845,14 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize)
memcpy(data, str.data(), str.length());
data[str.length()] = 0;
*pdata = data;
*psize = str.length();
if (pdata) {
*pdata = data;
}
if (psize) {
*psize = str.length();
}
return *pdata;
return data;
}
#ifdef __cplusplus
... ...
... ... @@ -165,12 +165,23 @@ int srs_flv_read_header(srs_flv_t flv, char header[9]);
int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, u_int32_t* ptime);
/* read the tag data. drop the 4bytes previous tag size */
int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size);
/* write flv header to file, auto write the 4bytes zero previous tag size. */
int srs_flv_write_header(srs_flv_t flv, char header[9]);
/* write flv tag to file, auto write the 4bytes previous tag size */
int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size);
/* file stream */
/* file stream tellg to get offset */
int64_t srs_flv_tellg(srs_flv_t flv);
/* seek file stream, offset is form the start of file */
void srs_flv_lseek(srs_flv_t flv, int64_t offset);
/* error code */
/* whether the error code indicates EOF */
flv_bool srs_flv_is_eof(int error_code);
/* media codec */
/* whether the video body is sequence header */
flv_bool srs_flv_is_sequence_header(char* data, int32_t size);
/* whether the video body is keyframe */
flv_bool srs_flv_is_keyframe(char* data, int32_t size);
/**
* amf0 codec
... ... @@ -180,8 +191,14 @@ typedef void* srs_amf0_t;
typedef int amf0_bool;
typedef double amf0_number;
srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed);
srs_amf0_t srs_amf0_create_number(amf0_number value);
srs_amf0_t srs_amf0_create_ecma_array();
srs_amf0_t srs_amf0_create_strict_array();
void srs_amf0_free(srs_amf0_t amf0);
void srs_amf0_free_bytes(char* data);
/* size and to bytes */
int srs_amf0_size(srs_amf0_t amf0);
int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size);
/* type detecter */
amf0_bool srs_amf0_is_string(srs_amf0_t amf0);
amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0);
... ... @@ -198,13 +215,18 @@ amf0_number srs_amf0_to_number(srs_amf0_t amf0);
int srs_amf0_object_property_count(srs_amf0_t amf0);
const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index);
srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index);
srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name);
void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value);
/* ecma array value converter */
int srs_amf0_ecma_array_property_count(srs_amf0_t amf0);
const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index);
srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index);
srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name);
void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value);
/* strict array value converter */
int srs_amf0_strict_array_property_count(srs_amf0_t amf0);
srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index);
void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value);
/**
* human readable print
* @param pdata, output the heap data,
... ...
... ... @@ -1113,6 +1113,11 @@ SrsAmf0Any* SrsAmf0StrictArray::at(int index)
return properties.at(index);
}
void SrsAmf0StrictArray::append(SrsAmf0Any* any)
{
properties.push_back(any);
}
int SrsAmf0Size::utf8(string value)
{
return 2 + value.length();
... ...
... ... @@ -260,6 +260,7 @@ public:
virtual int count();
// @remark: max index is count().
virtual SrsAmf0Any* at(int index);
virtual void append(SrsAmf0Any* any);
};
/**
... ...