正在显示
4 个修改的文件
包含
300 行增加
和
117 行删除
| @@ -236,6 +236,7 @@ int proxy(srs_mp4_t mp4, srs_rtmp_t ortmp) | @@ -236,6 +236,7 @@ int proxy(srs_mp4_t mp4, srs_rtmp_t ortmp) | ||
| 236 | int ret = 0; | 236 | int ret = 0; |
| 237 | 237 | ||
| 238 | if ((ret = srs_mp4_init_demuxer(mp4)) != 0) { | 238 | if ((ret = srs_mp4_init_demuxer(mp4)) != 0) { |
| 239 | + srs_human_trace("init demuxer failed. ret=%d", ret); | ||
| 239 | return ret; | 240 | return ret; |
| 240 | } | 241 | } |
| 241 | if ((ret = connect_oc(ortmp)) != 0) { | 242 | if ((ret = connect_oc(ortmp)) != 0) { |
| @@ -247,6 +247,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -247,6 +247,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 247 | #define ERROR_MP4_BOX_ILLEGAL_TYPE 3071 | 247 | #define ERROR_MP4_BOX_ILLEGAL_TYPE 3071 |
| 248 | #define ERROR_MP4_BOX_ILLEGAL_SCHEMA 3072 | 248 | #define ERROR_MP4_BOX_ILLEGAL_SCHEMA 3072 |
| 249 | #define ERROR_MP4_BOX_STRING 3073 | 249 | #define ERROR_MP4_BOX_STRING 3073 |
| 250 | +#define ERROR_MP4_BOX_ILLEGAL_BRAND 3074 | ||
| 251 | +#define ERROR_MP4_NOT_NON_SEEKABLE 3075 | ||
| 250 | 252 | ||
| 251 | /////////////////////////////////////////////////////// | 253 | /////////////////////////////////////////////////////// |
| 252 | // HTTP/StreamCaster/KAFKA protocol error. | 254 | // HTTP/StreamCaster/KAFKA protocol error. |
| @@ -64,6 +64,18 @@ using namespace std; | @@ -64,6 +64,18 @@ using namespace std; | ||
| 64 | #define SRS_MP4_BOX_CO64 0x636f3634 // 'co64' | 64 | #define SRS_MP4_BOX_CO64 0x636f3634 // 'co64' |
| 65 | #define SRS_MP4_BOX_STSZ 0x7374737a // 'stsz' | 65 | #define SRS_MP4_BOX_STSZ 0x7374737a // 'stsz' |
| 66 | #define SRS_MP4_BOX_STZ2 0x73747a32 // 'stz2' | 66 | #define SRS_MP4_BOX_STZ2 0x73747a32 // 'stz2' |
| 67 | +#define SRS_MP4_BOX_AVC1 0x61766331 // 'avc1' | ||
| 68 | +#define SRS_MP4_BOX_AVCC 0x61766343 // 'avcC' | ||
| 69 | +#define SRS_MP4_BOX_MP4A 0x6d703461 // 'mp4a' | ||
| 70 | +#define SRS_MP4_BOX_ESDS 0x65736473 // 'esds' | ||
| 71 | + | ||
| 72 | +#define SRS_MP4_BRAND_ISOM 0x69736f6d // 'isom' | ||
| 73 | +#define SRS_MP4_BRAND_ISO2 0x69736f32 // 'iso2' | ||
| 74 | +#define SRS_MP4_BRAND_AVC1 0x61766331 // 'avc1' | ||
| 75 | +#define SRS_MP4_BRAND_MP41 0x6d703431 // 'mp41' | ||
| 76 | + | ||
| 77 | +#define SRS_MP4_HANDLER_VIDE 0x76696465 // 'vide' | ||
| 78 | +#define SRS_MP4_HANDLER_SOUN 0x736f756e // 'soun' | ||
| 67 | 79 | ||
| 68 | #define SRS_MP4_EOF_SIZE 0 | 80 | #define SRS_MP4_EOF_SIZE 0 |
| 69 | #define SRS_MP4_USE_LARGE_SIZE 1 | 81 | #define SRS_MP4_USE_LARGE_SIZE 1 |
| @@ -75,9 +87,12 @@ int srs_mp4_string_length(const string& v) | @@ -75,9 +87,12 @@ int srs_mp4_string_length(const string& v) | ||
| 75 | 87 | ||
| 76 | void srs_mp4_string_write(SrsBuffer* buf, const string& v) | 88 | void srs_mp4_string_write(SrsBuffer* buf, const string& v) |
| 77 | { | 89 | { |
| 78 | - if (!v.empty()) { | ||
| 79 | - buf->write_bytes((char*)v.data(), (int)v.length()); | 90 | + // Nothing for empty string. |
| 91 | + if (v.empty()) { | ||
| 92 | + return; | ||
| 80 | } | 93 | } |
| 94 | + | ||
| 95 | + buf->write_bytes((char*)v.data(), (int)v.length()); | ||
| 81 | buf->write_1bytes(0x00); | 96 | buf->write_1bytes(0x00); |
| 82 | } | 97 | } |
| 83 | 98 | ||
| @@ -85,19 +100,22 @@ int srs_mp4_string_read(SrsBuffer* buf, string& v, int left) | @@ -85,19 +100,22 @@ int srs_mp4_string_read(SrsBuffer* buf, string& v, int left) | ||
| 85 | { | 100 | { |
| 86 | int ret = ERROR_SUCCESS; | 101 | int ret = ERROR_SUCCESS; |
| 87 | 102 | ||
| 88 | - char* p = buf->data() + buf->pos(); | 103 | + if (left == 0) { |
| 104 | + return ret; | ||
| 105 | + } | ||
| 89 | 106 | ||
| 90 | - char* start = p; | ||
| 91 | - while (p < start + left) { | ||
| 92 | - if (*p == 0x00) { | ||
| 93 | - v.append(start, p - start); | ||
| 94 | - buf->skip((int)(p - start)); | ||
| 95 | - return ret; | ||
| 96 | - } | 107 | + char* start = buf->data() + buf->pos(); |
| 108 | + size_t len = strnlen(start, left); | ||
| 109 | + | ||
| 110 | + if (len == left) { | ||
| 111 | + ret = ERROR_MP4_BOX_STRING; | ||
| 112 | + srs_error("MP4 string corrupt, left=%d. ret=%d", left, ret); | ||
| 113 | + return ret; | ||
| 97 | } | 114 | } |
| 98 | 115 | ||
| 99 | - ret = ERROR_MP4_BOX_STRING; | ||
| 100 | - srs_error("MP4 string corrupt, left=%d. ret=%d", left, ret); | 116 | + v.append(start, len); |
| 117 | + buf->skip((int)len + 1); | ||
| 118 | + | ||
| 101 | return ret; | 119 | return ret; |
| 102 | } | 120 | } |
| 103 | 121 | ||
| @@ -132,6 +150,21 @@ int SrsMp4Box::left_space(SrsBuffer* buf) | @@ -132,6 +150,21 @@ int SrsMp4Box::left_space(SrsBuffer* buf) | ||
| 132 | return (int)sz() - (buf->pos() - start_pos); | 150 | return (int)sz() - (buf->pos() - start_pos); |
| 133 | } | 151 | } |
| 134 | 152 | ||
| 153 | +bool SrsMp4Box::is_ftyp() | ||
| 154 | +{ | ||
| 155 | + return type == SRS_MP4_BOX_FTYP; | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +bool SrsMp4Box::is_moov() | ||
| 159 | +{ | ||
| 160 | + return type == SRS_MP4_BOX_MOOV; | ||
| 161 | +} | ||
| 162 | + | ||
| 163 | +bool SrsMp4Box::is_mdat() | ||
| 164 | +{ | ||
| 165 | + return type == SRS_MP4_BOX_MDAT; | ||
| 166 | +} | ||
| 167 | + | ||
| 135 | int SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox) | 168 | int SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox) |
| 136 | { | 169 | { |
| 137 | *ppbox = NULL; | 170 | *ppbox = NULL; |
| @@ -196,6 +229,9 @@ int SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox) | @@ -196,6 +229,9 @@ int SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox) | ||
| 196 | case SRS_MP4_BOX_STCO: box = new SrsMp4ChunkOffsetBox(); break; | 229 | case SRS_MP4_BOX_STCO: box = new SrsMp4ChunkOffsetBox(); break; |
| 197 | case SRS_MP4_BOX_CO64: box = new SrsMp4ChunkLargeOffsetBox(); break; | 230 | case SRS_MP4_BOX_CO64: box = new SrsMp4ChunkLargeOffsetBox(); break; |
| 198 | case SRS_MP4_BOX_STSZ: box = new SrsMp4SampleSizeBox(); break; | 231 | case SRS_MP4_BOX_STSZ: box = new SrsMp4SampleSizeBox(); break; |
| 232 | + case SRS_MP4_BOX_AVC1: box = new SrsMp4VisualSampleEntry(); break; | ||
| 233 | + case SRS_MP4_BOX_AVCC: box = new SrsMp4AvccBox(); break; | ||
| 234 | + case SRS_MP4_BOX_MP4A: box = new SrsMp4AudioSampleEntry(); break; | ||
| 199 | default: | 235 | default: |
| 200 | ret = ERROR_MP4_BOX_ILLEGAL_TYPE; | 236 | ret = ERROR_MP4_BOX_ILLEGAL_TYPE; |
| 201 | srs_error("MP4 illegal box type=%d. ret=%d", type, ret); | 237 | srs_error("MP4 illegal box type=%d. ret=%d", type, ret); |
| @@ -722,7 +758,7 @@ int SrsMp4MovieHeaderBox::decode_header(SrsBuffer* buf) | @@ -722,7 +758,7 @@ int SrsMp4MovieHeaderBox::decode_header(SrsBuffer* buf) | ||
| 722 | } | 758 | } |
| 723 | 759 | ||
| 724 | rate = buf->read_4bytes(); | 760 | rate = buf->read_4bytes(); |
| 725 | - volume = buf->read_4bytes(); | 761 | + volume = buf->read_2bytes(); |
| 726 | buf->skip(2); | 762 | buf->skip(2); |
| 727 | buf->skip(8); | 763 | buf->skip(8); |
| 728 | for (int i = 0; i < 9; i++) { | 764 | for (int i = 0; i < 9; i++) { |
| @@ -862,53 +898,6 @@ SrsMp4ElstEntry::SrsMp4ElstEntry() | @@ -862,53 +898,6 @@ SrsMp4ElstEntry::SrsMp4ElstEntry() | ||
| 862 | media_rate_fraction = 0; | 898 | media_rate_fraction = 0; |
| 863 | } | 899 | } |
| 864 | 900 | ||
| 865 | -int SrsMp4ElstEntry::nb_header(uint32_t version) | ||
| 866 | -{ | ||
| 867 | - int size = 0; | ||
| 868 | - | ||
| 869 | - if (version == 1) { | ||
| 870 | - size += 8+8; | ||
| 871 | - } else { | ||
| 872 | - size += 4+4; | ||
| 873 | - } | ||
| 874 | - | ||
| 875 | - size += 2+2; | ||
| 876 | - | ||
| 877 | - return size; | ||
| 878 | -} | ||
| 879 | - | ||
| 880 | -int SrsMp4ElstEntry::encode_header(SrsBuffer* buf, uint32_t version) | ||
| 881 | -{ | ||
| 882 | - if (version == 1) { | ||
| 883 | - buf->write_8bytes(segment_duration); | ||
| 884 | - buf->write_8bytes(media_time); | ||
| 885 | - } else { | ||
| 886 | - buf->write_4bytes((uint32_t)segment_duration); | ||
| 887 | - buf->write_4bytes((int32_t)media_time); | ||
| 888 | - } | ||
| 889 | - | ||
| 890 | - buf->write_2bytes(media_rate_integer); | ||
| 891 | - buf->write_2bytes(media_rate_fraction); | ||
| 892 | - | ||
| 893 | - return ERROR_SUCCESS; | ||
| 894 | -} | ||
| 895 | - | ||
| 896 | -int SrsMp4ElstEntry::decode_header(SrsBuffer* buf, uint32_t version) | ||
| 897 | -{ | ||
| 898 | - if (version == 1) { | ||
| 899 | - segment_duration = buf->read_8bytes(); | ||
| 900 | - media_time = buf->read_8bytes(); | ||
| 901 | - } else { | ||
| 902 | - segment_duration = buf->read_4bytes(); | ||
| 903 | - media_time = buf->read_4bytes(); | ||
| 904 | - } | ||
| 905 | - | ||
| 906 | - media_rate_integer = buf->read_2bytes(); | ||
| 907 | - media_rate_fraction = buf->read_2bytes(); | ||
| 908 | - | ||
| 909 | - return ERROR_SUCCESS; | ||
| 910 | -} | ||
| 911 | - | ||
| 912 | SrsMp4EditListBox::SrsMp4EditListBox() | 901 | SrsMp4EditListBox::SrsMp4EditListBox() |
| 913 | { | 902 | { |
| 914 | type = SRS_MP4_BOX_ELST; | 903 | type = SRS_MP4_BOX_ELST; |
| @@ -924,11 +913,12 @@ SrsMp4EditListBox::~SrsMp4EditListBox() | @@ -924,11 +913,12 @@ SrsMp4EditListBox::~SrsMp4EditListBox() | ||
| 924 | 913 | ||
| 925 | int SrsMp4EditListBox::nb_header() | 914 | int SrsMp4EditListBox::nb_header() |
| 926 | { | 915 | { |
| 927 | - int size = SrsMp4FullBox::nb_header(); | 916 | + int size = SrsMp4FullBox::nb_header() + 4; |
| 928 | 917 | ||
| 929 | - for (uint32_t i = 0; i < entry_count; i++) { | ||
| 930 | - SrsMp4ElstEntry& entry = entries[i]; | ||
| 931 | - size += entry.nb_header(version); | 918 | + if (version == 1) { |
| 919 | + size += entry_count * (2+2+8+8); | ||
| 920 | + } else { | ||
| 921 | + size += entry_count * (2+2+4+4); | ||
| 932 | } | 922 | } |
| 933 | 923 | ||
| 934 | return size; | 924 | return size; |
| @@ -942,11 +932,20 @@ int SrsMp4EditListBox::encode_header(SrsBuffer* buf) | @@ -942,11 +932,20 @@ int SrsMp4EditListBox::encode_header(SrsBuffer* buf) | ||
| 942 | return ret; | 932 | return ret; |
| 943 | } | 933 | } |
| 944 | 934 | ||
| 935 | + buf->write_4bytes(entry_count); | ||
| 945 | for (uint32_t i = 0; i < entry_count; i++) { | 936 | for (uint32_t i = 0; i < entry_count; i++) { |
| 946 | SrsMp4ElstEntry& entry = entries[i]; | 937 | SrsMp4ElstEntry& entry = entries[i]; |
| 947 | - if ((ret = entry.encode_header(buf, version)) != ERROR_SUCCESS) { | ||
| 948 | - return ret; | 938 | + |
| 939 | + if (version == 1) { | ||
| 940 | + buf->write_8bytes(entry.segment_duration); | ||
| 941 | + buf->write_8bytes(entry.media_time); | ||
| 942 | + } else { | ||
| 943 | + buf->write_4bytes((uint32_t)entry.segment_duration); | ||
| 944 | + buf->write_4bytes((int32_t)entry.media_time); | ||
| 949 | } | 945 | } |
| 946 | + | ||
| 947 | + buf->write_2bytes(entry.media_rate_integer); | ||
| 948 | + buf->write_2bytes(entry.media_rate_fraction); | ||
| 950 | } | 949 | } |
| 951 | 950 | ||
| 952 | return ret; | 951 | return ret; |
| @@ -960,16 +959,23 @@ int SrsMp4EditListBox::decode_header(SrsBuffer* buf) | @@ -960,16 +959,23 @@ int SrsMp4EditListBox::decode_header(SrsBuffer* buf) | ||
| 960 | return ret; | 959 | return ret; |
| 961 | } | 960 | } |
| 962 | 961 | ||
| 963 | - int left = left_space(buf); | ||
| 964 | - entry_count = left / SrsMp4ElstEntry().nb_header(version); | 962 | + entry_count = buf->read_4bytes(); |
| 965 | if (entry_count > 0) { | 963 | if (entry_count > 0) { |
| 966 | entries = new SrsMp4ElstEntry[entry_count]; | 964 | entries = new SrsMp4ElstEntry[entry_count]; |
| 967 | } | 965 | } |
| 968 | for (int i = 0; i < entry_count; i++) { | 966 | for (int i = 0; i < entry_count; i++) { |
| 969 | SrsMp4ElstEntry& entry = entries[i]; | 967 | SrsMp4ElstEntry& entry = entries[i]; |
| 970 | - if ((ret = entry.decode_header(buf, version)) != ERROR_SUCCESS) { | ||
| 971 | - return ret; | 968 | + |
| 969 | + if (version == 1) { | ||
| 970 | + entry.segment_duration = buf->read_8bytes(); | ||
| 971 | + entry.media_time = buf->read_8bytes(); | ||
| 972 | + } else { | ||
| 973 | + entry.segment_duration = buf->read_4bytes(); | ||
| 974 | + entry.media_time = buf->read_4bytes(); | ||
| 972 | } | 975 | } |
| 976 | + | ||
| 977 | + entry.media_rate_integer = buf->read_2bytes(); | ||
| 978 | + entry.media_rate_fraction = buf->read_2bytes(); | ||
| 973 | } | 979 | } |
| 974 | 980 | ||
| 975 | return ret; | 981 | return ret; |
| @@ -1104,6 +1110,16 @@ SrsMp4HandlerReferenceBox::~SrsMp4HandlerReferenceBox() | @@ -1104,6 +1110,16 @@ SrsMp4HandlerReferenceBox::~SrsMp4HandlerReferenceBox() | ||
| 1104 | { | 1110 | { |
| 1105 | } | 1111 | } |
| 1106 | 1112 | ||
| 1113 | +bool SrsMp4HandlerReferenceBox::is_video() | ||
| 1114 | +{ | ||
| 1115 | + return handler_type == SRS_MP4_HANDLER_VIDE; | ||
| 1116 | +} | ||
| 1117 | + | ||
| 1118 | +bool SrsMp4HandlerReferenceBox::is_audio() | ||
| 1119 | +{ | ||
| 1120 | + return handler_type == SRS_MP4_HANDLER_SOUN; | ||
| 1121 | +} | ||
| 1122 | + | ||
| 1107 | int SrsMp4HandlerReferenceBox::nb_header() | 1123 | int SrsMp4HandlerReferenceBox::nb_header() |
| 1108 | { | 1124 | { |
| 1109 | return SrsMp4FullBox::nb_header()+4+4+12+srs_mp4_string_length(name); | 1125 | return SrsMp4FullBox::nb_header()+4+4+12+srs_mp4_string_length(name); |
| @@ -1268,12 +1284,26 @@ SrsMp4DataEntryBox::~SrsMp4DataEntryBox() | @@ -1268,12 +1284,26 @@ SrsMp4DataEntryBox::~SrsMp4DataEntryBox() | ||
| 1268 | { | 1284 | { |
| 1269 | } | 1285 | } |
| 1270 | 1286 | ||
| 1271 | -int SrsMp4DataEntryBox::nb_header() | 1287 | +SrsMp4DataEntryUrlBox::SrsMp4DataEntryUrlBox() |
| 1272 | { | 1288 | { |
| 1289 | + type = SRS_MP4_BOX_URL; | ||
| 1290 | +} | ||
| 1291 | + | ||
| 1292 | +SrsMp4DataEntryUrlBox::~SrsMp4DataEntryUrlBox() | ||
| 1293 | +{ | ||
| 1294 | +} | ||
| 1295 | + | ||
| 1296 | +int SrsMp4DataEntryUrlBox::nb_header() | ||
| 1297 | +{ | ||
| 1298 | + // a 24-bit integer with flags; one flag is defined (x000001) which means that the media | ||
| 1299 | + // data is in the same file as the Movie Box containing this data reference. | ||
| 1300 | + if (flags == 1) { | ||
| 1301 | + return SrsMp4FullBox::nb_header(); | ||
| 1302 | + } | ||
| 1273 | return SrsMp4FullBox::nb_header()+srs_mp4_string_length(location); | 1303 | return SrsMp4FullBox::nb_header()+srs_mp4_string_length(location); |
| 1274 | } | 1304 | } |
| 1275 | 1305 | ||
| 1276 | -int SrsMp4DataEntryBox::encode_header(SrsBuffer* buf) | 1306 | +int SrsMp4DataEntryUrlBox::encode_header(SrsBuffer* buf) |
| 1277 | { | 1307 | { |
| 1278 | int ret = ERROR_SUCCESS; | 1308 | int ret = ERROR_SUCCESS; |
| 1279 | 1309 | ||
| @@ -1281,12 +1311,19 @@ int SrsMp4DataEntryBox::encode_header(SrsBuffer* buf) | @@ -1281,12 +1311,19 @@ int SrsMp4DataEntryBox::encode_header(SrsBuffer* buf) | ||
| 1281 | return ret; | 1311 | return ret; |
| 1282 | } | 1312 | } |
| 1283 | 1313 | ||
| 1314 | + // a 24-bit integer with flags; one flag is defined (x000001) which means that the media | ||
| 1315 | + // data is in the same file as the Movie Box containing this data reference. | ||
| 1316 | + if (location.empty()) { | ||
| 1317 | + flags = 0x01; | ||
| 1318 | + return ret; | ||
| 1319 | + } | ||
| 1320 | + | ||
| 1284 | srs_mp4_string_write(buf, location); | 1321 | srs_mp4_string_write(buf, location); |
| 1285 | 1322 | ||
| 1286 | return ret; | 1323 | return ret; |
| 1287 | } | 1324 | } |
| 1288 | 1325 | ||
| 1289 | -int SrsMp4DataEntryBox::decode_header(SrsBuffer* buf) | 1326 | +int SrsMp4DataEntryUrlBox::decode_header(SrsBuffer* buf) |
| 1290 | { | 1327 | { |
| 1291 | int ret = ERROR_SUCCESS; | 1328 | int ret = ERROR_SUCCESS; |
| 1292 | 1329 | ||
| @@ -1294,23 +1331,20 @@ int SrsMp4DataEntryBox::decode_header(SrsBuffer* buf) | @@ -1294,23 +1331,20 @@ int SrsMp4DataEntryBox::decode_header(SrsBuffer* buf) | ||
| 1294 | return ret; | 1331 | return ret; |
| 1295 | } | 1332 | } |
| 1296 | 1333 | ||
| 1334 | + // a 24-bit integer with flags; one flag is defined (x000001) which means that the media | ||
| 1335 | + // data is in the same file as the Movie Box containing this data reference. | ||
| 1336 | + if (flags == 0x01) { | ||
| 1337 | + return ret; | ||
| 1338 | + } | ||
| 1339 | + | ||
| 1297 | if ((ret = srs_mp4_string_read(buf, location, left_space(buf))) != ERROR_SUCCESS) { | 1340 | if ((ret = srs_mp4_string_read(buf, location, left_space(buf))) != ERROR_SUCCESS) { |
| 1298 | - srs_error("MP4 urx read string failed. ret=%d", ret); | 1341 | + srs_error("MP4 url read location failed. ret=%d", ret); |
| 1299 | return ret; | 1342 | return ret; |
| 1300 | } | 1343 | } |
| 1301 | 1344 | ||
| 1302 | return ret; | 1345 | return ret; |
| 1303 | } | 1346 | } |
| 1304 | 1347 | ||
| 1305 | -SrsMp4DataEntryUrlBox::SrsMp4DataEntryUrlBox() | ||
| 1306 | -{ | ||
| 1307 | - type = SRS_MP4_BOX_URL; | ||
| 1308 | -} | ||
| 1309 | - | ||
| 1310 | -SrsMp4DataEntryUrlBox::~SrsMp4DataEntryUrlBox() | ||
| 1311 | -{ | ||
| 1312 | -} | ||
| 1313 | - | ||
| 1314 | SrsMp4DataEntryUrnBox::SrsMp4DataEntryUrnBox() | 1348 | SrsMp4DataEntryUrnBox::SrsMp4DataEntryUrnBox() |
| 1315 | { | 1349 | { |
| 1316 | type = SRS_MP4_BOX_URN; | 1350 | type = SRS_MP4_BOX_URN; |
| @@ -1322,7 +1356,7 @@ SrsMp4DataEntryUrnBox::~SrsMp4DataEntryUrnBox() | @@ -1322,7 +1356,7 @@ SrsMp4DataEntryUrnBox::~SrsMp4DataEntryUrnBox() | ||
| 1322 | 1356 | ||
| 1323 | int SrsMp4DataEntryUrnBox::nb_header() | 1357 | int SrsMp4DataEntryUrnBox::nb_header() |
| 1324 | { | 1358 | { |
| 1325 | - return SrsMp4DataEntryBox::nb_header()+srs_mp4_string_length(name); | 1359 | + return SrsMp4FullBox::nb_header()+srs_mp4_string_length(location)+srs_mp4_string_length(name); |
| 1326 | } | 1360 | } |
| 1327 | 1361 | ||
| 1328 | int SrsMp4DataEntryUrnBox::encode_header(SrsBuffer* buf) | 1362 | int SrsMp4DataEntryUrnBox::encode_header(SrsBuffer* buf) |
| @@ -1333,6 +1367,7 @@ int SrsMp4DataEntryUrnBox::encode_header(SrsBuffer* buf) | @@ -1333,6 +1367,7 @@ int SrsMp4DataEntryUrnBox::encode_header(SrsBuffer* buf) | ||
| 1333 | return ret; | 1367 | return ret; |
| 1334 | } | 1368 | } |
| 1335 | 1369 | ||
| 1370 | + srs_mp4_string_write(buf, location); | ||
| 1336 | srs_mp4_string_write(buf, name); | 1371 | srs_mp4_string_write(buf, name); |
| 1337 | 1372 | ||
| 1338 | return ret; | 1373 | return ret; |
| @@ -1346,8 +1381,13 @@ int SrsMp4DataEntryUrnBox::decode_header(SrsBuffer* buf) | @@ -1346,8 +1381,13 @@ int SrsMp4DataEntryUrnBox::decode_header(SrsBuffer* buf) | ||
| 1346 | return ret; | 1381 | return ret; |
| 1347 | } | 1382 | } |
| 1348 | 1383 | ||
| 1384 | + if ((ret = srs_mp4_string_read(buf, location, left_space(buf))) != ERROR_SUCCESS) { | ||
| 1385 | + srs_error("MP4 urn read location failed. ret=%d", ret); | ||
| 1386 | + return ret; | ||
| 1387 | + } | ||
| 1388 | + | ||
| 1349 | if ((ret = srs_mp4_string_read(buf, name, left_space(buf))) != ERROR_SUCCESS) { | 1389 | if ((ret = srs_mp4_string_read(buf, name, left_space(buf))) != ERROR_SUCCESS) { |
| 1350 | - srs_error("MP4 urn read string failed. ret=%d", ret); | 1390 | + srs_error("MP4 urn read name failed. ret=%d", ret); |
| 1351 | return ret; | 1391 | return ret; |
| 1352 | } | 1392 | } |
| 1353 | 1393 | ||
| @@ -1578,6 +1618,55 @@ int SrsMp4VisualSampleEntry::decode_header(SrsBuffer* buf) | @@ -1578,6 +1618,55 @@ int SrsMp4VisualSampleEntry::decode_header(SrsBuffer* buf) | ||
| 1578 | return ret; | 1618 | return ret; |
| 1579 | } | 1619 | } |
| 1580 | 1620 | ||
| 1621 | +SrsMp4AvccBox::SrsMp4AvccBox() | ||
| 1622 | +{ | ||
| 1623 | + type = SRS_MP4_BOX_AVCC; | ||
| 1624 | + nb_config = 0; | ||
| 1625 | + avc_config = NULL; | ||
| 1626 | +} | ||
| 1627 | + | ||
| 1628 | +SrsMp4AvccBox::~SrsMp4AvccBox() | ||
| 1629 | +{ | ||
| 1630 | + srs_freepa(avc_config); | ||
| 1631 | +} | ||
| 1632 | + | ||
| 1633 | +int SrsMp4AvccBox::nb_header() | ||
| 1634 | +{ | ||
| 1635 | + return SrsMp4Box::nb_header()+nb_config; | ||
| 1636 | +} | ||
| 1637 | + | ||
| 1638 | +int SrsMp4AvccBox::encode_header(SrsBuffer* buf) | ||
| 1639 | +{ | ||
| 1640 | + int ret = ERROR_SUCCESS; | ||
| 1641 | + | ||
| 1642 | + if ((ret = SrsMp4Box::encode_header(buf)) != ERROR_SUCCESS) { | ||
| 1643 | + return ret; | ||
| 1644 | + } | ||
| 1645 | + | ||
| 1646 | + if (nb_config) { | ||
| 1647 | + buf->write_bytes((char*)avc_config, nb_config); | ||
| 1648 | + } | ||
| 1649 | + | ||
| 1650 | + return ret; | ||
| 1651 | +} | ||
| 1652 | + | ||
| 1653 | +int SrsMp4AvccBox::decode_header(SrsBuffer* buf) | ||
| 1654 | +{ | ||
| 1655 | + int ret = ERROR_SUCCESS; | ||
| 1656 | + | ||
| 1657 | + if ((ret = SrsMp4Box::decode_header(buf)) != ERROR_SUCCESS) { | ||
| 1658 | + return ret; | ||
| 1659 | + } | ||
| 1660 | + | ||
| 1661 | + nb_config = left_space(buf); | ||
| 1662 | + if (nb_config) { | ||
| 1663 | + avc_config = new uint8_t[nb_config]; | ||
| 1664 | + buf->read_bytes((char*)avc_config, nb_config); | ||
| 1665 | + } | ||
| 1666 | + | ||
| 1667 | + return ret; | ||
| 1668 | +} | ||
| 1669 | + | ||
| 1581 | SrsMp4AudioSampleEntry::SrsMp4AudioSampleEntry() | 1670 | SrsMp4AudioSampleEntry::SrsMp4AudioSampleEntry() |
| 1582 | { | 1671 | { |
| 1583 | reserved0 = 0; | 1672 | reserved0 = 0; |
| @@ -1905,8 +1994,7 @@ int SrsMp4SyncSampleBox::decode_header(SrsBuffer* buf) | @@ -1905,8 +1994,7 @@ int SrsMp4SyncSampleBox::decode_header(SrsBuffer* buf) | ||
| 1905 | sample_numbers = new uint32_t[entry_count]; | 1994 | sample_numbers = new uint32_t[entry_count]; |
| 1906 | } | 1995 | } |
| 1907 | for (uint32_t i = 0; i < entry_count; i++) { | 1996 | for (uint32_t i = 0; i < entry_count; i++) { |
| 1908 | - uint32_t sample_number = sample_numbers[i]; | ||
| 1909 | - buf->write_4bytes(sample_number); | 1997 | + sample_numbers[i] = buf->read_4bytes(); |
| 1910 | } | 1998 | } |
| 1911 | 1999 | ||
| 1912 | return ret; | 2000 | return ret; |
| @@ -2143,16 +2231,18 @@ int SrsMp4SampleSizeBox::decode_header(SrsBuffer* buf) | @@ -2143,16 +2231,18 @@ int SrsMp4SampleSizeBox::decode_header(SrsBuffer* buf) | ||
| 2143 | return ret; | 2231 | return ret; |
| 2144 | } | 2232 | } |
| 2145 | 2233 | ||
| 2234 | +#define SRS_MP4_BUF_SIZE 4096 | ||
| 2235 | + | ||
| 2146 | SrsMp4Decoder::SrsMp4Decoder() | 2236 | SrsMp4Decoder::SrsMp4Decoder() |
| 2147 | { | 2237 | { |
| 2148 | reader = NULL; | 2238 | reader = NULL; |
| 2149 | - next = NULL; | 2239 | + buf = new char[SRS_MP4_BUF_SIZE]; |
| 2150 | stream = new SrsSimpleStream(); | 2240 | stream = new SrsSimpleStream(); |
| 2151 | } | 2241 | } |
| 2152 | 2242 | ||
| 2153 | SrsMp4Decoder::~SrsMp4Decoder() | 2243 | SrsMp4Decoder::~SrsMp4Decoder() |
| 2154 | { | 2244 | { |
| 2155 | - srs_freep(next); | 2245 | + srs_freepa(buf); |
| 2156 | srs_freep(stream); | 2246 | srs_freep(stream); |
| 2157 | } | 2247 | } |
| 2158 | 2248 | ||
| @@ -2163,36 +2253,90 @@ int SrsMp4Decoder::initialize(ISrsReader* r) | @@ -2163,36 +2253,90 @@ int SrsMp4Decoder::initialize(ISrsReader* r) | ||
| 2163 | srs_assert(r); | 2253 | srs_assert(r); |
| 2164 | reader = r; | 2254 | reader = r; |
| 2165 | 2255 | ||
| 2166 | - if ((ret = load_next_box(&next)) != ERROR_SUCCESS) { | ||
| 2167 | - return ret; | 2256 | + // File Type Box (ftyp) |
| 2257 | + if (true) { | ||
| 2258 | + SrsMp4Box* box = NULL; | ||
| 2259 | + SrsAutoFree(SrsMp4Box, box); | ||
| 2260 | + | ||
| 2261 | + if ((ret = load_next_box(&box, SRS_MP4_BOX_FTYP)) != ERROR_SUCCESS) { | ||
| 2262 | + return ret; | ||
| 2263 | + } | ||
| 2264 | + SrsMp4FileTypeBox* ftyp = dynamic_cast<SrsMp4FileTypeBox*>(box); | ||
| 2265 | + | ||
| 2266 | + bool legal_brand = false; | ||
| 2267 | + static uint32_t legal_brands[] = { | ||
| 2268 | + SRS_MP4_BRAND_ISOM, SRS_MP4_BRAND_ISO2, SRS_MP4_BRAND_AVC1, SRS_MP4_BRAND_MP41 | ||
| 2269 | + }; | ||
| 2270 | + for (int i = 0; i < sizeof(legal_brands)/sizeof(uint32_t); i++) { | ||
| 2271 | + if (ftyp->major_brand == legal_brands[i]) { | ||
| 2272 | + legal_brand = true; | ||
| 2273 | + break; | ||
| 2274 | + } | ||
| 2275 | + } | ||
| 2276 | + if (!legal_brand) { | ||
| 2277 | + ret = ERROR_MP4_BOX_ILLEGAL_BRAND; | ||
| 2278 | + srs_error("MP4 brand is illegal, brand=%d. ret=%d", ftyp->major_brand, ret); | ||
| 2279 | + return ret; | ||
| 2280 | + } | ||
| 2281 | + } | ||
| 2282 | + | ||
| 2283 | + // Media Data Box (mdat) or Movie Box (moov) | ||
| 2284 | + SrsMp4Box* box = NULL; | ||
| 2285 | + SrsAutoFree(SrsMp4Box, box); | ||
| 2286 | + while (true) { | ||
| 2287 | + if ((ret = load_next_box(&box, 0)) != ERROR_SUCCESS) { | ||
| 2288 | + return ret; | ||
| 2289 | + } | ||
| 2290 | + | ||
| 2291 | + if (!box->is_mdat() && !box->is_moov()) { | ||
| 2292 | + srs_freep(box); | ||
| 2293 | + continue; | ||
| 2294 | + } | ||
| 2295 | + break; | ||
| 2168 | } | 2296 | } |
| 2169 | 2297 | ||
| 2170 | - if (next->type != SRS_MP4_BOX_FTYP) { | ||
| 2171 | - ret = ERROR_MP4_BOX_ILLEGAL_SCHEMA; | ||
| 2172 | - srs_error("MP4 first box must be FTYP, not %d. ret=%d", next->type, ret); | 2298 | + // Only support non-seek mp4, that is, mdat should never before moov. |
| 2299 | + // @see https://github.com/ossrs/srs/issues/738#issuecomment-276343669 | ||
| 2300 | + if (box->is_mdat()) { | ||
| 2301 | + ret = ERROR_MP4_NOT_NON_SEEKABLE; | ||
| 2302 | + srs_error("MP4 is not non-seekable. ret=%d", ret); | ||
| 2173 | return ret; | 2303 | return ret; |
| 2174 | } | 2304 | } |
| 2175 | 2305 | ||
| 2176 | return ret; | 2306 | return ret; |
| 2177 | } | 2307 | } |
| 2178 | 2308 | ||
| 2179 | -int SrsMp4Decoder::load_next_box(SrsMp4Box** ppbox) | 2309 | +int SrsMp4Decoder::load_next_box(SrsMp4Box** ppbox, uint32_t required_box_type) |
| 2180 | { | 2310 | { |
| 2181 | int ret = ERROR_SUCCESS; | 2311 | int ret = ERROR_SUCCESS; |
| 2182 | 2312 | ||
| 2183 | - // Ignore for already loaded. | ||
| 2184 | - if (next) { | ||
| 2185 | - return ret; | 2313 | + while (true) { |
| 2314 | + SrsMp4Box* box = NULL; | ||
| 2315 | + if ((ret = do_load_next_box(&box, required_box_type)) != ERROR_SUCCESS) { | ||
| 2316 | + srs_freep(box); | ||
| 2317 | + return ret; | ||
| 2318 | + } | ||
| 2319 | + | ||
| 2320 | + if (!required_box_type || box->type == required_box_type) { | ||
| 2321 | + *ppbox = box; | ||
| 2322 | + break; | ||
| 2323 | + } | ||
| 2324 | + srs_freep(box); | ||
| 2186 | } | 2325 | } |
| 2187 | 2326 | ||
| 2188 | - char* buf = new char[4096]; | ||
| 2189 | - SrsAutoFreeA(char, buf); | 2327 | + return ret; |
| 2328 | +} | ||
| 2329 | + | ||
| 2330 | +int SrsMp4Decoder::do_load_next_box(SrsMp4Box** ppbox, uint32_t required_box_type) | ||
| 2331 | +{ | ||
| 2332 | + int ret = ERROR_SUCCESS; | ||
| 2190 | 2333 | ||
| 2334 | + SrsMp4Box* box = NULL; | ||
| 2191 | while (true) { | 2335 | while (true) { |
| 2192 | - uint64_t required = next? next->sz():4; | 2336 | + uint64_t required = box? box->sz():4; |
| 2193 | while (stream->length() < required) { | 2337 | while (stream->length() < required) { |
| 2194 | ssize_t nread; | 2338 | ssize_t nread; |
| 2195 | - if ((ret = reader->read(buf, 4096, &nread)) != ERROR_SUCCESS) { | 2339 | + if ((ret = reader->read(buf, SRS_MP4_BUF_SIZE, &nread)) != ERROR_SUCCESS) { |
| 2196 | srs_error("MP4 load failed, nread=%d, required=%d. ret=%d", nread, required, ret); | 2340 | srs_error("MP4 load failed, nread=%d, required=%d. ret=%d", nread, required, ret); |
| 2197 | return ret; | 2341 | return ret; |
| 2198 | } | 2342 | } |
| @@ -2205,7 +2349,7 @@ int SrsMp4Decoder::load_next_box(SrsMp4Box** ppbox) | @@ -2205,7 +2349,7 @@ int SrsMp4Decoder::load_next_box(SrsMp4Box** ppbox) | ||
| 2205 | SrsAutoFree(SrsBuffer, buffer); | 2349 | SrsAutoFree(SrsBuffer, buffer); |
| 2206 | 2350 | ||
| 2207 | // Discovery the box with basic header. | 2351 | // Discovery the box with basic header. |
| 2208 | - if (!next && (ret = SrsMp4Box::discovery(buffer, ppbox)) != ERROR_SUCCESS) { | 2352 | + if (!box && (ret = SrsMp4Box::discovery(buffer, &box)) != ERROR_SUCCESS) { |
| 2209 | if (ret == ERROR_MP4_BOX_REQUIRE_SPACE) { | 2353 | if (ret == ERROR_MP4_BOX_REQUIRE_SPACE) { |
| 2210 | continue; | 2354 | continue; |
| 2211 | } | 2355 | } |
| @@ -2214,13 +2358,23 @@ int SrsMp4Decoder::load_next_box(SrsMp4Box** ppbox) | @@ -2214,13 +2358,23 @@ int SrsMp4Decoder::load_next_box(SrsMp4Box** ppbox) | ||
| 2214 | } | 2358 | } |
| 2215 | 2359 | ||
| 2216 | // Decode util we can demux the whole box. | 2360 | // Decode util we can demux the whole box. |
| 2217 | - if (!buffer->require((int)next->sz())) { | 2361 | + if (!buffer->require((int)box->sz())) { |
| 2218 | continue; | 2362 | continue; |
| 2219 | } | 2363 | } |
| 2220 | - ret = next->decode(buffer); | 2364 | + |
| 2365 | + if (!required_box_type || box->type == required_box_type) { | ||
| 2366 | + ret = box->decode(buffer); | ||
| 2367 | + } | ||
| 2221 | 2368 | ||
| 2222 | // Remove the consumed bytes. | 2369 | // Remove the consumed bytes. |
| 2223 | - stream->erase((int)next->sz()); | 2370 | + stream->erase((int)box->sz()); |
| 2371 | + | ||
| 2372 | + if (ret != ERROR_SUCCESS) { | ||
| 2373 | + srs_freep(box); | ||
| 2374 | + } else { | ||
| 2375 | + *ppbox = box; | ||
| 2376 | + } | ||
| 2377 | + | ||
| 2224 | break; | 2378 | break; |
| 2225 | } | 2379 | } |
| 2226 | 2380 |
| @@ -72,6 +72,10 @@ public: | @@ -72,6 +72,10 @@ public: | ||
| 72 | virtual uint64_t sz(); | 72 | virtual uint64_t sz(); |
| 73 | // Get the left space of box, for decoder. | 73 | // Get the left space of box, for decoder. |
| 74 | virtual int left_space(SrsBuffer* buf); | 74 | virtual int left_space(SrsBuffer* buf); |
| 75 | + // Box type helper. | ||
| 76 | + virtual bool is_ftyp(); | ||
| 77 | + virtual bool is_moov(); | ||
| 78 | + virtual bool is_mdat(); | ||
| 75 | /** | 79 | /** |
| 76 | * Discovery the box from buffer. | 80 | * Discovery the box from buffer. |
| 77 | * @param ppbox Output the discoveried box, which user must free it. | 81 | * @param ppbox Output the discoveried box, which user must free it. |
| @@ -353,10 +357,6 @@ public: | @@ -353,10 +357,6 @@ public: | ||
| 353 | int16_t media_rate_fraction; | 357 | int16_t media_rate_fraction; |
| 354 | public: | 358 | public: |
| 355 | SrsMp4ElstEntry(); | 359 | SrsMp4ElstEntry(); |
| 356 | -public: | ||
| 357 | - virtual int nb_header(uint32_t version); | ||
| 358 | - virtual int encode_header(SrsBuffer* buf, uint32_t version); | ||
| 359 | - virtual int decode_header(SrsBuffer* buf, uint32_t version); | ||
| 360 | }; | 360 | }; |
| 361 | 361 | ||
| 362 | /** | 362 | /** |
| @@ -463,6 +463,9 @@ public: | @@ -463,6 +463,9 @@ public: | ||
| 463 | public: | 463 | public: |
| 464 | SrsMp4HandlerReferenceBox(); | 464 | SrsMp4HandlerReferenceBox(); |
| 465 | virtual ~SrsMp4HandlerReferenceBox(); | 465 | virtual ~SrsMp4HandlerReferenceBox(); |
| 466 | +public: | ||
| 467 | + virtual bool is_video(); | ||
| 468 | + virtual bool is_audio(); | ||
| 466 | protected: | 469 | protected: |
| 467 | virtual int nb_header(); | 470 | virtual int nb_header(); |
| 468 | virtual int encode_header(SrsBuffer* buf); | 471 | virtual int encode_header(SrsBuffer* buf); |
| @@ -542,6 +545,8 @@ public: | @@ -542,6 +545,8 @@ public: | ||
| 542 | /** | 545 | /** |
| 543 | * 8.7.2 Data Reference Box | 546 | * 8.7.2 Data Reference Box |
| 544 | * ISO_IEC_14496-12-base-format-2012.pdf, page 56 | 547 | * ISO_IEC_14496-12-base-format-2012.pdf, page 56 |
| 548 | + * a 24-bit integer with flags; one flag is defined (x000001) which means that the media | ||
| 549 | + * data is in the same file as the Movie Box containing this data reference. | ||
| 545 | */ | 550 | */ |
| 546 | class SrsMp4DataEntryBox : public SrsMp4FullBox | 551 | class SrsMp4DataEntryBox : public SrsMp4FullBox |
| 547 | { | 552 | { |
| @@ -550,10 +555,6 @@ public: | @@ -550,10 +555,6 @@ public: | ||
| 550 | public: | 555 | public: |
| 551 | SrsMp4DataEntryBox(); | 556 | SrsMp4DataEntryBox(); |
| 552 | virtual ~SrsMp4DataEntryBox(); | 557 | virtual ~SrsMp4DataEntryBox(); |
| 553 | -protected: | ||
| 554 | - virtual int nb_header(); | ||
| 555 | - virtual int encode_header(SrsBuffer* buf); | ||
| 556 | - virtual int decode_header(SrsBuffer* buf); | ||
| 557 | }; | 558 | }; |
| 558 | 559 | ||
| 559 | /** | 560 | /** |
| @@ -565,6 +566,10 @@ class SrsMp4DataEntryUrlBox : public SrsMp4DataEntryBox | @@ -565,6 +566,10 @@ class SrsMp4DataEntryUrlBox : public SrsMp4DataEntryBox | ||
| 565 | public: | 566 | public: |
| 566 | SrsMp4DataEntryUrlBox(); | 567 | SrsMp4DataEntryUrlBox(); |
| 567 | virtual ~SrsMp4DataEntryUrlBox(); | 568 | virtual ~SrsMp4DataEntryUrlBox(); |
| 569 | +protected: | ||
| 570 | + virtual int nb_header(); | ||
| 571 | + virtual int encode_header(SrsBuffer* buf); | ||
| 572 | + virtual int decode_header(SrsBuffer* buf); | ||
| 568 | }; | 573 | }; |
| 569 | 574 | ||
| 570 | /** | 575 | /** |
| @@ -680,6 +685,24 @@ protected: | @@ -680,6 +685,24 @@ protected: | ||
| 680 | }; | 685 | }; |
| 681 | 686 | ||
| 682 | /** | 687 | /** |
| 688 | + * 5.3.4 AVC Video Stream Definition (avcC) | ||
| 689 | + * ISO_IEC_14496-15-AVC-format-2012.pdf, page 19 | ||
| 690 | + */ | ||
| 691 | +class SrsMp4AvccBox : public SrsMp4Box | ||
| 692 | +{ | ||
| 693 | +public: | ||
| 694 | + int nb_config; | ||
| 695 | + uint8_t* avc_config; | ||
| 696 | +public: | ||
| 697 | + SrsMp4AvccBox(); | ||
| 698 | + virtual ~SrsMp4AvccBox(); | ||
| 699 | +protected: | ||
| 700 | + virtual int nb_header(); | ||
| 701 | + virtual int encode_header(SrsBuffer* buf); | ||
| 702 | + virtual int decode_header(SrsBuffer* buf); | ||
| 703 | +}; | ||
| 704 | + | ||
| 705 | +/** | ||
| 683 | * 8.5.2 Sample Description Box (mp4a) | 706 | * 8.5.2 Sample Description Box (mp4a) |
| 684 | * ISO_IEC_14496-12-base-format-2012.pdf, page 45 | 707 | * ISO_IEC_14496-12-base-format-2012.pdf, page 45 |
| 685 | */ | 708 | */ |
| @@ -957,8 +980,8 @@ private: | @@ -957,8 +980,8 @@ private: | ||
| 957 | // The stream used to demux the boxes. | 980 | // The stream used to demux the boxes. |
| 958 | // TODO: FIXME: refine for performance issue. | 981 | // TODO: FIXME: refine for performance issue. |
| 959 | SrsSimpleStream* stream; | 982 | SrsSimpleStream* stream; |
| 960 | - // Always load next box. | ||
| 961 | - SrsMp4Box* next; | 983 | + // The temporary buffer to read from buffer. |
| 984 | + char* buf; | ||
| 962 | public: | 985 | public: |
| 963 | SrsMp4Decoder(); | 986 | SrsMp4Decoder(); |
| 964 | virtual ~SrsMp4Decoder(); | 987 | virtual ~SrsMp4Decoder(); |
| @@ -970,7 +993,10 @@ public: | @@ -970,7 +993,10 @@ public: | ||
| 970 | */ | 993 | */ |
| 971 | virtual int initialize(ISrsReader* r); | 994 | virtual int initialize(ISrsReader* r); |
| 972 | private: | 995 | private: |
| 973 | - virtual int load_next_box(SrsMp4Box** ppbox); | 996 | + // Load the next box from reader. |
| 997 | + // @param required_box_type The box type required, 0 for any box. | ||
| 998 | + virtual int load_next_box(SrsMp4Box** ppbox, uint32_t required_box_type); | ||
| 999 | + virtual int do_load_next_box(SrsMp4Box** ppbox, uint32_t required_box_type); | ||
| 974 | }; | 1000 | }; |
| 975 | 1001 | ||
| 976 | #endif | 1002 | #endif |
-
请 注册 或 登录 后发表评论