diff --git a/README.md b/README.md index 2c48923..e9200a3 100755 --- a/README.md +++ b/README.md @@ -123,7 +123,6 @@ Please select your language: * [SRS 2.0 English][v2_EN_Home] * [SRS 2.0 Chinese][v2_CN_Home] -<<<<<<< HEAD ### SRS 3.0 wiki Please select your language: @@ -182,6 +181,8 @@ Please select your language: - [ ] Support MPEG-DASH, the future streaming protocol, read [#299][bug #299]. - [ ] Support HLS+, please read [#466][bug #466] and [#468][bug #468]. +### Change Logs + ### V3 changes * v3.0, 2017-01-19, for [#742][bug #742] refine source, meta and origin hub. 3.0.16 diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 4063a03..6528ce3 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -250,6 +250,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_MP4_BOX_ILLEGAL_BRAND 3074 #define ERROR_MP4_NOT_NON_SEEKABLE 3075 #define ERROR_MP4_ESDS_SL_Config 3076 +#define ERROR_MP4_ILLEGAL_MOOV 3077 /////////////////////////////////////////////////////// // HTTP/StreamCaster/KAFKA protocol error. diff --git a/trunk/src/kernel/srs_kernel_log.hpp b/trunk/src/kernel/srs_kernel_log.hpp index d68f85d..4d9b3e0 100644 --- a/trunk/src/kernel/srs_kernel_log.hpp +++ b/trunk/src/kernel/srs_kernel_log.hpp @@ -141,15 +141,17 @@ extern ISrsThreadContext* _srs_context; #define srs_trace(msg, ...) _srs_log->trace(NULL, _srs_context->get_id(), msg, ##__VA_ARGS__) #define srs_warn(msg, ...) _srs_log->warn(NULL, _srs_context->get_id(), msg, ##__VA_ARGS__) #define srs_error(msg, ...) _srs_log->error(NULL, _srs_context->get_id(), msg, ##__VA_ARGS__) +#endif // use __FUNCTION__ to print c method -#elif 0 +#if 0 #define srs_verbose(msg, ...) _srs_log->verbose(__FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__) #define srs_info(msg, ...) _srs_log->info(__FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__) #define srs_trace(msg, ...) _srs_log->trace(__FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__) #define srs_warn(msg, ...) _srs_log->warn(__FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__) #define srs_error(msg, ...) _srs_log->error(__FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__) +#endif // use __PRETTY_FUNCTION__ to print c++ class:method -#else +#if 0 #define srs_verbose(msg, ...) _srs_log->verbose(__PRETTY_FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__) #define srs_info(msg, ...) _srs_log->info(__PRETTY_FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__) #define srs_trace(msg, ...) _srs_log->trace(__PRETTY_FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__) diff --git a/trunk/src/kernel/srs_kernel_mp4.cpp b/trunk/src/kernel/srs_kernel_mp4.cpp index 0fc9f1e..343eef5 100644 --- a/trunk/src/kernel/srs_kernel_mp4.cpp +++ b/trunk/src/kernel/srs_kernel_mp4.cpp @@ -121,6 +121,19 @@ bool SrsMp4Box::is_mdat() return type == SrsMp4BoxTypeMDAT; } +SrsMp4Box* SrsMp4Box::get(SrsMp4BoxType bt) +{ + vector<SrsMp4Box*>::iterator it; + for (it = boxes.begin(); it != boxes.end(); ++it) { + SrsMp4Box* box = *it; + if (box->type == bt) { + return box; + } + } + + return NULL; +} + int SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox) { *ppbox = NULL; @@ -625,6 +638,40 @@ SrsMp4MovieBox::~SrsMp4MovieBox() { } +SrsMp4MovieHeaderBox* SrsMp4MovieBox::mvhd() +{ + SrsMp4Box* box = get(SrsMp4BoxTypeMVHD); + return dynamic_cast<SrsMp4MovieHeaderBox*>(box); +} + +SrsMp4TrackBox* SrsMp4MovieBox::video() +{ + for (int i = 0; i < boxes.size(); i++) { + SrsMp4Box* box = boxes.at(i); + if (box->type == SrsMp4BoxTypeTRAK) { + SrsMp4TrackBox* trak = dynamic_cast<SrsMp4TrackBox*>(box); + if ((trak->track_type() & SrsMp4TrackTypeVideo) == SrsMp4TrackTypeVideo) { + return trak; + } + } + } + return NULL; +} + +SrsMp4TrackBox* SrsMp4MovieBox::audio() +{ + for (int i = 0; i < boxes.size(); i++) { + SrsMp4Box* box = boxes.at(i); + if (box->type == SrsMp4BoxTypeTRAK) { + SrsMp4TrackBox* trak = dynamic_cast<SrsMp4TrackBox*>(box); + if ((trak->track_type() & SrsMp4TrackTypeAudio) == SrsMp4TrackTypeAudio) { + return trak; + } + } + } + return NULL; +} + int SrsMp4MovieBox::nb_header() { return SrsMp4Box::nb_header(); @@ -659,6 +706,11 @@ SrsMp4MovieHeaderBox::~SrsMp4MovieHeaderBox() { } +uint64_t SrsMp4MovieHeaderBox::duration() +{ + return duration_in_tbn * 1000 / timescale; +} + int SrsMp4MovieHeaderBox::nb_header() { int size = SrsMp4FullBox::nb_header(); @@ -686,12 +738,12 @@ int SrsMp4MovieHeaderBox::encode_header(SrsBuffer* buf) buf->write_8bytes(creation_time); buf->write_8bytes(modification_time); buf->write_4bytes(timescale); - buf->write_8bytes(duration); + buf->write_8bytes(duration_in_tbn); } else { buf->write_4bytes((uint32_t)creation_time); buf->write_4bytes((uint32_t)modification_time); buf->write_4bytes(timescale); - buf->write_4bytes((uint32_t)duration); + buf->write_4bytes((uint32_t)duration_in_tbn); } buf->write_4bytes(rate); @@ -721,12 +773,12 @@ int SrsMp4MovieHeaderBox::decode_header(SrsBuffer* buf) creation_time = buf->read_8bytes(); modification_time = buf->read_8bytes(); timescale = buf->read_4bytes(); - duration = buf->read_8bytes(); + duration_in_tbn = buf->read_8bytes(); } else { creation_time = buf->read_4bytes(); modification_time = buf->read_4bytes(); timescale = buf->read_4bytes(); - duration = buf->read_4bytes(); + duration_in_tbn = buf->read_4bytes(); } rate = buf->read_4bytes(); @@ -769,6 +821,18 @@ SrsMp4TrackHeaderBox::~SrsMp4TrackHeaderBox() { } +SrsMp4TrackType SrsMp4TrackBox::track_type() +{ + SrsMp4Box* box = get(SrsMp4BoxTypeMDIA); + if (!box) { + return SrsMp4TrackTypeForbidden; + } + + // TODO: Maybe should discovery all mdia boxes. + SrsMp4MediaBox* mdia = dynamic_cast<SrsMp4MediaBox*>(box); + return mdia->track_type(); +} + int SrsMp4TrackHeaderBox::nb_header() { int size = SrsMp4FullBox::nb_header(); @@ -962,6 +1026,23 @@ SrsMp4MediaBox::~SrsMp4MediaBox() { } +SrsMp4TrackType SrsMp4MediaBox::track_type() +{ + SrsMp4Box* box = get(SrsMp4BoxTypeHDLR); + if (!box) { + return SrsMp4TrackTypeForbidden; + } + + SrsMp4HandlerReferenceBox* hdlr = dynamic_cast<SrsMp4HandlerReferenceBox*>(box); + if (hdlr->handler_type == SrsMp4HandlerTypeSOUN) { + return SrsMp4TrackTypeAudio; + } else if (hdlr->handler_type == SrsMp4HandlerTypeVIDE) { + return SrsMp4TrackTypeVideo; + } else { + return SrsMp4TrackTypeForbidden; + } +} + SrsMp4MediaHeaderBox::SrsMp4MediaHeaderBox() { type = SrsMp4BoxTypeMDHD; @@ -1084,12 +1165,12 @@ SrsMp4HandlerReferenceBox::~SrsMp4HandlerReferenceBox() bool SrsMp4HandlerReferenceBox::is_video() { - return handler_type == SrsMp4BoxTypeVIDE; + return handler_type == SrsMp4HandlerTypeVIDE; } bool SrsMp4HandlerReferenceBox::is_audio() { - return handler_type == SrsMp4BoxTypeSOUN; + return handler_type == SrsMp4HandlerTypeSOUN; } int SrsMp4HandlerReferenceBox::nb_header() @@ -1124,7 +1205,7 @@ int SrsMp4HandlerReferenceBox::decode_header(SrsBuffer* buf) } buf->skip(4); - handler_type = buf->read_4bytes(); + handler_type = (SrsMp4HandlerType)buf->read_4bytes(); buf->skip(12); if ((ret = srs_mp4_string_read(buf, name, left_space(buf))) != ERROR_SUCCESS) { @@ -2766,6 +2847,24 @@ int SrsMp4Decoder::initialize(ISrsReader* r) int SrsMp4Decoder::parse_moov(SrsMp4MovieBox* moov) { int ret = ERROR_SUCCESS; + + SrsMp4MovieHeaderBox* mvhd = moov->mvhd(); + if (!mvhd) { + ret = ERROR_MP4_ILLEGAL_MOOV; + srs_error("MP4 missing mvhd. ret=%d", ret); + return ret; + } + + SrsMp4TrackBox* vide = moov->video(); + SrsMp4TrackBox* soun = moov->audio(); + if (!vide && !soun) { + ret = ERROR_MP4_ILLEGAL_MOOV; + srs_error("MP4 missing audio and video track. ret=%d", ret); + return ret; + } + + srs_trace("MP4 moov dur=%dms, vide=%d, soun=%d", mvhd->duration(), vide != NULL, soun != NULL); + return ret; } diff --git a/trunk/src/kernel/srs_kernel_mp4.hpp b/trunk/src/kernel/srs_kernel_mp4.hpp index eeb7ea9..ed171d0 100644 --- a/trunk/src/kernel/srs_kernel_mp4.hpp +++ b/trunk/src/kernel/srs_kernel_mp4.hpp @@ -81,9 +81,18 @@ enum SrsMp4BoxType SrsMp4BoxTypeMP4A = 0x6d703461, // 'mp4a' SrsMp4BoxTypeESDS = 0x65736473, // 'esds' SrsMp4BoxTypeUDTA = 0x75647461, // 'udta' +}; + +/** + * 8.4.3.3 Semantics + * ISO_IEC_14496-12-base-format-2012.pdf, page 37 + */ +enum SrsMp4HandlerType +{ + SrsMp4HandlerTypeForbidden = 0x00, - SrsMp4BoxTypeVIDE = 0x76696465, // 'vide' - SrsMp4BoxTypeSOUN = 0x736f756e, // 'soun' + SrsMp4HandlerTypeVIDE = 0x76696465, // 'vide' + SrsMp4HandlerTypeSOUN = 0x736f756e, // 'soun' }; /** @@ -121,7 +130,7 @@ public: SrsMp4BoxType type; // For box 'uuid'. uint8_t* usertype; -private: +protected: std::vector<SrsMp4Box*> boxes; private: // The position at buffer to start demux the box. @@ -138,6 +147,9 @@ public: virtual bool is_ftyp(); virtual bool is_moov(); virtual bool is_mdat(); + // Get the contained box of specific type. + // @return The first matched box. + virtual SrsMp4Box* get(SrsMp4BoxType bt); /** * Discovery the box from buffer. * @param ppbox Output the discoveried box, which user must free it. @@ -252,6 +264,9 @@ protected: virtual int decode_header(SrsBuffer* buf); }; +class SrsMp4TrackBox; +class SrsMp4MovieHeaderBox; + /** * 8.2.1 Movie Box (moov) * ISO_IEC_14496-12-base-format-2012.pdf, page 30 @@ -263,6 +278,13 @@ class SrsMp4MovieBox : public SrsMp4Box public: SrsMp4MovieBox(); virtual ~SrsMp4MovieBox(); +public: + // Get the header of moov. + virtual SrsMp4MovieHeaderBox* mvhd(); + // Get the first video track. + virtual SrsMp4TrackBox* video(); + // Get the first audio track. + virtual SrsMp4TrackBox* audio(); protected: virtual int nb_header(); virtual int encode_header(SrsBuffer* buf); @@ -282,6 +304,7 @@ public: // an integer that declares the most recent time the presentation was modified (in // seconds since midnight, Jan. 1, 1904, in UTC time) uint64_t modification_time; +private: // an integer that specifies the time-scale for the entire presentation; this is the number of // time units that pass in one second. For example, a time coordinate system that measures time in // sixtieths of a second has a time scale of 60. @@ -289,7 +312,7 @@ public: // an integer that declares length of the presentation (in the indicated timescale). This property // is derived from the presentation’s tracks: the value of this field corresponds to the duration of the // longest track in the presentation. If the duration cannot be determined then duration is set to all 1s. - uint64_t duration; + uint64_t duration_in_tbn; public: // a fixed point 16.16 number that indicates the preferred rate to play the presentation; 1.0 // (0x00010000) is normal forward playback @@ -309,12 +332,23 @@ public: public: SrsMp4MovieHeaderBox(); virtual ~SrsMp4MovieHeaderBox(); +public: + // Get the duration in ms. + virtual uint64_t duration(); protected: virtual int nb_header(); virtual int encode_header(SrsBuffer* buf); virtual int decode_header(SrsBuffer* buf); }; +// The type of track, maybe combine of types. +enum SrsMp4TrackType +{ + SrsMp4TrackTypeForbidden = 0x00, + SrsMp4TrackTypeAudio = 0x01, + SrsMp4TrackTypeVideo = 0x02, +}; + /** * 8.3.1 Track Box (trak) * ISO_IEC_14496-12-base-format-2012.pdf, page 32 @@ -327,6 +361,11 @@ class SrsMp4TrackBox : public SrsMp4Box public: SrsMp4TrackBox(); virtual ~SrsMp4TrackBox(); +public: + // Get the type of track, maybe combine of track type, + // for example, it maybe Audio|Video when contains both. + // Generally, only single type, no combination. + virtual SrsMp4TrackType track_type(); }; /** @@ -458,6 +497,11 @@ class SrsMp4MediaBox : public SrsMp4Box public: SrsMp4MediaBox(); virtual ~SrsMp4MediaBox(); +public: + // Get the type of track, maybe combine of track type, + // for example, it maybe Audio|Video when contains both. + // Generally, only single type, no combination. + virtual SrsMp4TrackType track_type(); }; /** @@ -521,7 +565,7 @@ public: // an integer containing one of the following values, or a value from a derived specification: // ‘vide’, Video track // ‘soun’, Audio track - uint32_t handler_type; + SrsMp4HandlerType handler_type; uint32_t reserved[3]; // a null-terminated string in UTF-8 characters which gives a human-readable name for the track // type (for debugging and inspection purposes).