Merge branch '3.0release' into develop

min
winlin 5 years ago
commit 1794f805ec

@ -9,15 +9,9 @@ assignees: ''
**描述(Description)**
描述你遇到了什么问题。
(Please description your issue here.)
**环境(Environment)**
> 描述你遇到了什么问题(Please description your issue here)
1. SRS版本(Version): `xxxxxx`
1. 操作系统(OS)`xxxxxx`
1. 编码器(工具和版本)(Encoder)`xxxxxx`
1. 播放器(工具和版本)(Player)`xxxxxx`
1. SRS的日志如下(Log):
```
xxxxxxxxxxxx
@ -25,20 +19,12 @@ xxxxxxxxxxxx
**重现(Replay)**
重现Bug的步骤如下
(How to replay bug?)
1. 启动SRS运行(Start SRS by) `xxxxxx`
1. 推流,运行(Publish by) `xxxxxx`
1. 播放,运行(Play by) `xxxxxx`
1. 操作(Other steps) `xxxxxx`
1. 重现了Bug关键信息如下(Bug replayed, logs)
> 重现Bug的步骤(How to replay bug?)
```
xxxxxx
```
1. `xxxxxx`
1. `xxxxxx`
1. `xxxxxx`
**期望行为(Expect)**
描述你期望发生的事情。
(Please describe your expectation.)
> 描述你期望发生的事情(Please describe your expectation)

@ -148,6 +148,11 @@ For previous versions, please read:
## V3 changes
* v3.0, 2019-12-27, For [#299][bug #299], fix some bugs in dash, it works now. 3.0.88
* v3.0, 2019-12-27, For [#1544][bug #1544], fix memory leaking for complex error. 3.0.87
* v3.0, 2019-12-27, Add links for flv.js, hls.js and dash.js.
* v3.0, 2019-12-26, For [#1105][bug #1105], http server support mp4 range.
* v3.0, 2019-12-26, For [#1105][bug #1105], dvr mp4 supports playing on Chrome/Safari/Firefox. 3.0.86
* <strong>v3.0, 2019-12-26, [3.0 alpha6(3.0.85)][r3.0a6] released. 116056 lines.</strong>
* v3.0, 2019-12-26, For [#1488][bug #1488], pass client ip to http callback. 3.0.85
* v3.0, 2019-12-25, For [#1537][bug #1537], [#1282][bug #1282], support aarch64 for armv8. 3.0.84
@ -1567,6 +1572,8 @@ Winlin
[bug #1537]: https://github.com/ossrs/srs/issues/1537
[bug #1538]: https://github.com/ossrs/srs/issues/1538
[bug #1282]: https://github.com/ossrs/srs/issues/1282
[bug #1105]: https://github.com/ossrs/srs/issues/1105
[bug #1544]: https://github.com/ossrs/srs/issues/1544
[bug #xxxxxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxxxxx
[bug #1111]: https://github.com/ossrs/srs/issues/1111

@ -9,56 +9,30 @@
padding-top: 55px;
}
</style>
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a id="srs_index" class="brand" href="#">SRS</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<hr>
<footer>
<p><a href="https://github.com/ossrs/srs">SRS Team &copy; 2013</a></p>
</footer>
</div>
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/swfobject.js"></script>
<script type="text/javascript" src="js/srs.page.js"></script>
<script type="text/javascript" src="js/srs.utility.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<script type="text/javascript">
$(function(){
update_nav();
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/swfobject.js"></script>
<script type="text/javascript" src="js/srs.page.js"></script>
<script type="text/javascript" src="js/srs.utility.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<script type="text/javascript">
$(function(){
update_nav();
var query = parse_query_string();
var url = "srs_chat.html?vhost=" + srs_get_player_vhost();
var query = parse_query_string();
var url = "srs_player.html?vhost=" + srs_get_player_vhost();
for (var key in query.user_query) {
if (key == "vhost") {
continue;
for (var key in query.user_query) {
if (key == "vhost") {
continue;
}
url += "&" + key + "=" + query[key];
}
url += "&" + key + "=" + query[key];
}
setTimeout(function(){
window.location.href = url;
}, 100);
});
</script>
});
</script>
</head>
<body>
</body>

@ -71,6 +71,15 @@
<input type="text" id="txt_url" class="input-xxlarge" value="">
<button class="btn btn-primary" id="btn_play">播放视频</button>
<button class="btn" id="btn_generate_link">生成链接</button>
<p></p>
<p>
推荐的其他播放器:
<ul>
<li><a href="http://bilibili.github.io/flv.js/demo">flv.js</a>H5/MSE播放HTTP-FLV</li>
<li><a href="https://hls-js.netlify.com/demo">hls.js</a>H5/MSE播放HLS</li>
<li><a href="http://reference.dashif.org/dash.js/nightly/samples/dash-if-reference-player/index.html">dash.js</a>H5/MSE播放MPEG-DASH</li>
</ul>
</p>
</div>
<div id="link_modal" class="modal hide fade">
<div class="modal-header">

@ -34,6 +34,7 @@
#include <srs_core_autofree.hpp>
#include <srs_kernel_mp4.hpp>
#include <stdlib.h>
#include <sstream>
using namespace std;
@ -276,10 +277,17 @@ srs_error_t SrsMpdWriter::get_fragment(bool video, std::string& home, std::strin
srs_error_t err = srs_success;
home = fragment_home;
sn = srs_update_system_time() / fragment;
// We name the segment as advanced N segments, because when we are generating segment at the current time,
// the player may also request the current segment.
srs_assert(fragment);
int64_t number = (srs_update_system_time() / fragment + 1);
// TOOD: FIXME: Should keep the segments continuous, or player may fail.
sn = number;
// The base time aligned with sn.
basetime = sn * fragment;
if (video) {
file_name = "video-" + srs_int2str(sn) + ".m4s";
} else {
@ -292,7 +300,7 @@ srs_error_t SrsMpdWriter::get_fragment(bool video, std::string& home, std::strin
SrsDashController::SrsDashController()
{
req = NULL;
video_tack_id = 2;
video_tack_id = 0;
audio_track_id = 1;
mpd = new SrsMpdWriter();
vcurrent = acurrent = NULL;
@ -332,6 +340,10 @@ srs_error_t SrsDashController::on_publish()
fragment = _srs_config->get_dash_fragment(r->vhost);
home = _srs_config->get_dash_path(r->vhost);
if ((err = mpd->on_publish()) != srs_success) {
return srs_error_wrap(err, "mpd");
}
srs_freep(vcurrent);
vcurrent = new SrsFragmentedMp4();
if ((err = vcurrent->initialize(req, true, mpd, video_tack_id)) != srs_success) {
@ -344,10 +356,6 @@ srs_error_t SrsDashController::on_publish()
return srs_error_wrap(err, "audio fragment");
}
if ((err = mpd->on_publish()) != srs_success) {
return srs_error_wrap(err, "mpd");
}
return err;
}

@ -489,7 +489,7 @@ srs_error_t SrsDvrMp4Segmenter::encode_audio(SrsSharedPtrMessage* audio, SrsForm
uint32_t nb_sample = (uint32_t)format->nb_raw;
uint32_t dts = (uint32_t)audio->timestamp;
if ((err = enc->write_sample(SrsMp4HandlerTypeSOUN, 0x00, ct, dts, dts, sample, nb_sample)) != srs_success) {
if ((err = enc->write_sample(format, SrsMp4HandlerTypeSOUN, 0x00, ct, dts, dts, sample, nb_sample)) != srs_success) {
return srs_error_wrap(err, "write sample");
}
@ -515,7 +515,7 @@ srs_error_t SrsDvrMp4Segmenter::encode_video(SrsSharedPtrMessage* video, SrsForm
uint8_t* sample = (uint8_t*)format->raw;
uint32_t nb_sample = (uint32_t)format->nb_raw;
if ((err = enc->write_sample(SrsMp4HandlerTypeVIDE, frame_type, ct, dts, pts, sample, nb_sample)) != srs_success) {
if ((err = enc->write_sample(format, SrsMp4HandlerTypeVIDE, frame_type, ct, dts, pts, sample, nb_sample)) != srs_success) {
return srs_error_wrap(err, "write sample");
}

@ -157,10 +157,10 @@ srs_error_t SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMe
// parse -1 to whole file.
if (end == -1) {
end = (int)fs->filesize();
end = (int)(fs->filesize() - 1);
}
if (end > fs->filesize() || start > end) {
if (end > fs->filesize() || start > end || end < 0) {
return srs_error_new(ERROR_HTTP_REMUX_OFFSET_OVERFLOW, "http mp4 streaming %s overflow. size=%" PRId64 ", offset=%d",
fullpath.c_str(), fs->filesize(), start);
}

@ -27,7 +27,7 @@
// The version config.
#define VERSION_MAJOR 3
#define VERSION_MINOR 0
#define VERSION_REVISION 85
#define VERSION_REVISION 88
// The macros generated by configure script.
#include <srs_auto_headers.hpp>

@ -62,6 +62,7 @@ SrsCplxError::SrsCplxError()
SrsCplxError::~SrsCplxError()
{
srs_freep(wrapped);
}
std::string SrsCplxError::description() {

@ -408,6 +408,7 @@ srs_error_t SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox)
case SrsMp4BoxTypeTFHD: box = new SrsMp4TrackFragmentHeaderBox(); break;
case SrsMp4BoxTypeTFDT: box = new SrsMp4TrackFragmentDecodeTimeBox(); break;
case SrsMp4BoxTypeTRUN: box = new SrsMp4TrackFragmentRunBox(); break;
case SrsMp4BoxTypeSIDX: box = new SrsMp4SegmentIndexBox(); break;
// Skip some unknown boxes.
case SrsMp4BoxTypeFREE: case SrsMp4BoxTypeSKIP: case SrsMp4BoxTypePASP:
box = new SrsMp4FreeSpaceBox(type); break;
@ -2047,10 +2048,8 @@ stringstream& SrsMp4TrackHeaderBox::dumps_detail(stringstream& ss, SrsMp4DumpCon
if (volume) {
ss << ", volume=" << uint32_t(volume>>8) << "." << uint32_t(volume&0xFF);
}
if (width || height) {
ss << ", size=" << uint16_t(width>>16) << "x" << uint16_t(height>>16);
}
ss << ", size=" << uint16_t(width>>16) << "x" << uint16_t(height>>16);
return ss;
}
@ -4577,6 +4576,120 @@ stringstream& SrsMp4UserDataBox::dumps_detail(stringstream& ss, SrsMp4DumpContex
return ss;
}
SrsMp4SegmentIndexBox::SrsMp4SegmentIndexBox()
{
type = SrsMp4BoxTypeSIDX;
}
SrsMp4SegmentIndexBox::~SrsMp4SegmentIndexBox()
{
}
int SrsMp4SegmentIndexBox::nb_header()
{
return SrsMp4Box::nb_header() + 4+4+4 + (version? 4:8) + 4+4 + 12*entries.size();
}
srs_error_t SrsMp4SegmentIndexBox::encode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4Box::encode_header(buf)) != srs_success) {
return srs_error_wrap(err, "encode header");
}
buf->write_1bytes(version);
buf->write_3bytes(flags);
buf->write_4bytes(reference_id);
buf->write_4bytes(timescale);
if (!version) {
buf->write_4bytes(earliest_presentation_time);
buf->write_4bytes(first_offset);
} else {
buf->write_8bytes(earliest_presentation_time);
buf->write_8bytes(first_offset);
}
buf->write_4bytes((uint32_t)entries.size());
for (int i = 0; i < (int)entries.size(); i++) {
SrsMp4SegmentIndexEntry& entry = entries.at(i);
uint32_t v = uint32_t(entry.reference_type&0x01)<<31;
v |= entry.referenced_size&0x7fffffff;
buf->write_4bytes(v);
buf->write_4bytes(entry.subsegment_duration);
v = uint32_t(entry.starts_with_SAP&0x01)<<31;
v |= uint32_t(entry.SAP_type&0x7)<<28;
v |= entry.SAP_delta_time&0xfffffff;
buf->write_4bytes(v);
}
return err;
}
srs_error_t SrsMp4SegmentIndexBox::decode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4Box::decode_header(buf)) != srs_success) {
return srs_error_wrap(err, "decode header");
}
version = buf->read_1bytes();
flags = buf->read_3bytes();
reference_id = buf->read_4bytes();
timescale = buf->read_4bytes();
if (!version) {
earliest_presentation_time = buf->read_4bytes();
first_offset = buf->read_4bytes();
} else {
earliest_presentation_time = buf->read_8bytes();
first_offset = buf->read_8bytes();
}
uint32_t nn_entries = (uint32_t)(buf->read_4bytes() & 0xffff);
for (uint32_t i = 0; i < nn_entries; i++) {
SrsMp4SegmentIndexEntry entry;
uint32_t v = buf->read_4bytes();
entry.reference_type = uint8_t((v&0x80000000)>>31);
entry.referenced_size = v&0x7fffffff;
entry.subsegment_duration = buf->read_4bytes();
v = buf->read_4bytes();
entry.starts_with_SAP = uint8_t((v&0x80000000)>>31);
entry.SAP_type = uint8_t((v&0x70000000)>>28);
entry.SAP_delta_time = v&0xfffffff;
entries.push_back(entry);
}
return err;
}
stringstream& SrsMp4SegmentIndexBox::dumps_detail(stringstream& ss, SrsMp4DumpContext dc)
{
SrsMp4Box::dumps_detail(ss, dc);
ss << ", v" << (int)version << ", flags=" << flags << ", refs#" << reference_id
<< ", TBN=" << timescale << ", ePTS=" << earliest_presentation_time;
for (int i = 0; i < (int)entries.size(); i++) {
SrsMp4SegmentIndexEntry& entry = entries.at(i);
ss << endl;
srs_padding(ss, dc.indent());
ss << "#" << i << ", ref=" << (int)entry.reference_type << "/" << entry.referenced_size
<< ", duration=" << entry.subsegment_duration << ", SAP=" << (int)entry.starts_with_SAP
<< "/" << (int)entry.SAP_type << "/" << entry.SAP_delta_time;
}
return ss;
}
SrsMp4Sample::SrsMp4Sample()
{
type = SrsFrameTypeForbidden;
@ -5528,6 +5641,26 @@ srs_error_t SrsMp4Encoder::initialize(ISrsWriteSeeker* ws)
return srs_error_wrap(err, "write ftyp");
}
}
// 8B reserved free box.
if (true) {
SrsMp4FreeSpaceBox* freeb = new SrsMp4FreeSpaceBox(SrsMp4BoxTypeFREE);
SrsAutoFree(SrsMp4FreeSpaceBox, freeb);
int nb_data = freeb->nb_bytes();
std::vector<char> data(nb_data);
SrsBuffer* buffer = new SrsBuffer(&data[0], nb_data);
SrsAutoFree(SrsBuffer, buffer);
if ((err = freeb->encode(buffer)) != srs_success) {
return srs_error_wrap(err, "encode free box");
}
if ((err = wsio->write(&data[0], nb_data, NULL)) != srs_success) {
return srs_error_wrap(err, "write free box");
}
}
// Write mdat box.
if (true) {
@ -5564,8 +5697,10 @@ srs_error_t SrsMp4Encoder::initialize(ISrsWriteSeeker* ws)
return err;
}
srs_error_t SrsMp4Encoder::write_sample(SrsMp4HandlerType ht, uint16_t ft, uint16_t ct, uint32_t dts, uint32_t pts, uint8_t* sample, uint32_t nb_sample)
{
srs_error_t SrsMp4Encoder::write_sample(
SrsFormat* format, SrsMp4HandlerType ht, uint16_t ft, uint16_t ct, uint32_t dts, uint32_t pts,
uint8_t* sample, uint32_t nb_sample
) {
srs_error_t err = srs_success;
SrsMp4Sample* ps = new SrsMp4Sample();
@ -5574,7 +5709,7 @@ srs_error_t SrsMp4Encoder::write_sample(SrsMp4HandlerType ht, uint16_t ft, uint1
bool vsh = (ht == SrsMp4HandlerTypeVIDE) && (ct == (uint16_t)SrsVideoAvcFrameTraitSequenceHeader);
bool ash = (ht == SrsMp4HandlerTypeSOUN) && (ct == (uint16_t)SrsAudioAacFrameTraitSequenceHeader);
if (vsh || ash) {
err = copy_sequence_header(vsh, sample, nb_sample);
err = copy_sequence_header(format, vsh, sample, nb_sample);
srs_freep(ps);
return err;
}
@ -5683,6 +5818,7 @@ srs_error_t SrsMp4Encoder::flush()
avc1->width = width;
avc1->height = height;
avc1->data_reference_index = 1;
SrsMp4AvccBox* avcC = new SrsMp4AvccBox();
avc1->set_avcC(avcC);
@ -5741,6 +5877,7 @@ srs_error_t SrsMp4Encoder::flush()
stbl->set_stsd(stsd);
SrsMp4AudioSampleEntry* mp4a = new SrsMp4AudioSampleEntry();
mp4a->data_reference_index = 1;
mp4a->samplerate = uint32_t(srs_flv_srates[sample_rate]) << 16;
if (sound_bits == SrsAudioSampleBits16bit) {
mp4a->samplesize = 16;
@ -5826,7 +5963,7 @@ srs_error_t SrsMp4Encoder::flush()
return err;
}
srs_error_t SrsMp4Encoder::copy_sequence_header(bool vsh, uint8_t* sample, uint32_t nb_sample)
srs_error_t SrsMp4Encoder::copy_sequence_header(SrsFormat* format, bool vsh, uint8_t* sample, uint32_t nb_sample)
{
srs_error_t err = srs_success;
@ -5848,8 +5985,10 @@ srs_error_t SrsMp4Encoder::copy_sequence_header(bool vsh, uint8_t* sample, uint3
if (vsh) {
pavcc = std::vector<char>(sample, sample + nb_sample);
// TODO: FIXME: Parse the width and height.
if (format && format->vcodec) {
width = format->vcodec->width;
height = format->vcodec->height;
}
}
if (!vsh) {
@ -5906,8 +6045,8 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid)
SrsAutoFree(SrsMp4FileTypeBox, ftyp);
if (true) {
ftyp->major_brand = SrsMp4BoxBrandISO5;
ftyp->minor_version = 0;
ftyp->set_compatible_brands(SrsMp4BoxBrandISOM, SrsMp4BoxBrandISO5, SrsMp4BoxBrandDASH, SrsMp4BoxBrandMP42);
ftyp->minor_version = 512;
ftyp->set_compatible_brands(SrsMp4BoxBrandISO6, SrsMp4BoxBrandMP41);
}
// Write moov.
@ -5977,6 +6116,7 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid)
avc1->width = format->vcodec->width;
avc1->height = format->vcodec->height;
avc1->data_reference_index = 1;
SrsMp4AvccBox* avcC = new SrsMp4AvccBox();
avc1->set_avcC(avcC);
@ -6054,6 +6194,7 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid)
stbl->set_stsd(stsd);
SrsMp4AudioSampleEntry* mp4a = new SrsMp4AudioSampleEntry();
mp4a->data_reference_index = 1;
mp4a->samplerate = uint32_t(srs_flv_srates[format->acodec->sound_rate]) << 16;
if (format->acodec->sound_size == SrsAudioSampleBits16bit) {
mp4a->samplesize = 16;
@ -6109,7 +6250,7 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid)
uint8_t* data = new uint8_t[nb_data];
SrsAutoFreeA(uint8_t, data);
SrsBuffer* buffer = new SrsBuffer();
SrsBuffer* buffer = new SrsBuffer((char*)data, nb_data);
SrsAutoFree(SrsBuffer, buffer);
if ((err = ftyp->encode(buffer)) != srs_success) {
@ -6134,7 +6275,6 @@ SrsMp4M2tsSegmentEncoder::SrsMp4M2tsSegmentEncoder()
buffer = new SrsBuffer();
sequence_number = 0;
decode_basetime = 0;
data_offset = 0;
mdat_bytes = 0;
}
@ -6160,7 +6300,7 @@ srs_error_t SrsMp4M2tsSegmentEncoder::initialize(ISrsWriter* w, uint32_t sequenc
styp->major_brand = SrsMp4BoxBrandMSDH;
styp->minor_version = 0;
styp->set_compatible_brands(SrsMp4BoxBrandMSDH, SrsMp4BoxBrandDASH);
styp->set_compatible_brands(SrsMp4BoxBrandMSDH, SrsMp4BoxBrandMSIX);
int nb_data = styp->nb_bytes();
std::vector<char> data(nb_data);
@ -6176,9 +6316,8 @@ srs_error_t SrsMp4M2tsSegmentEncoder::initialize(ISrsWriter* w, uint32_t sequenc
if ((err = writer->write(&data[0], nb_data, NULL)) != srs_success) {
return srs_error_wrap(err, "write styp");
}
data_offset = nb_data;
}
return err;
}
@ -6204,7 +6343,11 @@ srs_error_t SrsMp4M2tsSegmentEncoder::write_sample(SrsMp4HandlerType ht,
ps->tbn = 1000;
ps->dts = dts;
ps->pts = pts;
ps->data = sample;
// We should copy the sample data, which is shared ptr from video/audio message.
// Furthermore, we do free the data when freeing the sample.
ps->data = new uint8_t[nb_sample];
memcpy(ps->data, sample, nb_sample);
ps->nb_data = nb_sample;
// Append to manager to build the moof.
@ -6228,7 +6371,10 @@ srs_error_t SrsMp4M2tsSegmentEncoder::flush(uint64_t& dts)
// and we will update its header(size) when flush.
SrsMp4MediaDataBox* mdat = new SrsMp4MediaDataBox();
SrsAutoFree(SrsMp4MediaDataBox, mdat);
// Although the sidx is not required to start play DASH, but it's required for AV sync.
// TODO: FIXME: Insert a sidx box.
// Write moof.
if (true) {
SrsMp4MovieFragmentBox* moof = new SrsMp4MovieFragmentBox();
@ -6262,7 +6408,8 @@ srs_error_t SrsMp4M2tsSegmentEncoder::flush(uint64_t& dts)
}
int nb_data = moof->nb_bytes();
trun->data_offset = (int32_t)(data_offset + nb_data + mdat->sz_header());
// @remark Remember the data_offset of turn is size(moof)+header(mdat), not including styp or sidx.
trun->data_offset = (int32_t)(nb_data + mdat->sz_header());
uint8_t* data = new uint8_t[nb_data];
SrsAutoFreeA(uint8_t, data);

@ -123,6 +123,7 @@ enum SrsMp4BoxType
SrsMp4BoxTypeTFHD = 0x74666864, // 'tfhd'
SrsMp4BoxTypeTFDT = 0x74666474, // 'tfdt'
SrsMp4BoxTypeTRUN = 0x7472756e, // 'trun'
SrsMp4BoxTypeSIDX = 0x73696478, // 'sidx'
};
// 8.4.3.3 Semantics
@ -145,9 +146,11 @@ enum SrsMp4BoxBrand
SrsMp4BoxBrandAVC1 = 0x61766331, // 'avc1'
SrsMp4BoxBrandMP41 = 0x6d703431, // 'mp41'
SrsMp4BoxBrandISO5 = 0x69736f35, // 'iso5'
SrsMp4BoxBrandISO6 = 0x69736f36, // 'iso6'
SrsMp4BoxBrandMP42 = 0x6d703432, // 'mp42'
SrsMp4BoxBrandDASH = 0x64617368, // 'dash'
SrsMp4BoxBrandMSDH = 0x6d736468, // 'msdh'
SrsMp4BoxBrandMSIX = 0x6d736978, // 'msix'
};
// The context to dump.
@ -179,8 +182,10 @@ public:
// An extended type; in this case, the type field is set to uuid.
SrsMp4BoxType type;
// For box 'uuid'.
// TODO: FIXME: Should double check buffer.
std::vector<char> usertype;
protected:
// TODO: FIXME: Should double check buffer.
std::vector<SrsMp4Box*> boxes;
private:
// The position at buffer to start demux the box.
@ -274,6 +279,7 @@ public:
uint32_t minor_version;
private:
// A list, to the end of the box, of brands
// TODO: FIXME: Should double check buffer.
std::vector<SrsMp4BoxBrand> compatible_brands;
public:
SrsMp4FileTypeBox();
@ -506,6 +512,7 @@ public:
uint32_t first_sample_flags;
// all fields in the following array are optional
public:
// TODO: FIXME: Should double check buffer.
std::vector<SrsMp4TrunEntry*> entries;
public:
SrsMp4TrackFragmentRunBox();
@ -595,6 +602,7 @@ public:
class SrsMp4FreeSpaceBox : public SrsMp4Box
{
private:
// TODO: FIXME: Should double check buffer.
std::vector<char> data;
public:
SrsMp4FreeSpaceBox(SrsMp4BoxType v);
@ -902,6 +910,7 @@ class SrsMp4EditListBox : public SrsMp4FullBox
{
public:
// An integer that gives the number of entries in the following table
// TODO: FIXME: Should double check buffer.
std::vector<SrsMp4ElstEntry> entries;
public:
SrsMp4EditListBox();
@ -1152,6 +1161,7 @@ public:
class SrsMp4DataReferenceBox : public SrsMp4FullBox
{
private:
// TODO: FIXME: Should double check buffer.
std::vector<SrsMp4DataEntryBox*> entries;
public:
SrsMp4DataReferenceBox();
@ -1273,6 +1283,7 @@ public:
class SrsMp4AvccBox : public SrsMp4Box
{
public:
// TODO: FIXME: Should double check buffer.
std::vector<char> avc_config;
public:
SrsMp4AvccBox();
@ -1383,6 +1394,7 @@ class SrsMp4DecoderSpecificInfo : public SrsMp4BaseDescriptor
public:
// AAC Audio Specific Config.
// 1.6.2.1 AudioSpecificConfig, in ISO_IEC_14496-3-AAC-2001.pdf, page 33.
// TODO: FIXME: Should double check buffer.
std::vector<char> asc;
public:
SrsMp4DecoderSpecificInfo();
@ -1449,6 +1461,7 @@ public:
// if (streamDependenceFlag)
uint16_t dependsOn_ES_ID;
// if (URL_Flag)
// TODO: FIXME: Should double check buffer.
std::vector<char> URLstring;
// if (OCRstreamFlag)
uint16_t OCR_ES_Id;
@ -1494,6 +1507,7 @@ public:
class SrsMp4SampleDescriptionBox : public SrsMp4FullBox
{
private:
// TODO: FIXME: Should double check buffer.
std::vector<SrsMp4SampleEntry*> entries;
public:
SrsMp4SampleDescriptionBox();
@ -1543,6 +1557,7 @@ class SrsMp4DecodingTime2SampleBox : public SrsMp4FullBox
{
public:
// An integer that gives the number of entries in the following table.
// TODO: FIXME: Should double check buffer.
std::vector<SrsMp4SttsEntry> entries;
private:
// The index for counter to calc the dts for samples.
@ -1783,6 +1798,7 @@ public:
class SrsMp4UserDataBox : public SrsMp4Box
{
public:
// TODO: FIXME: Should double check buffer.
std::vector<char> data;
public:
SrsMp4UserDataBox();
@ -1795,6 +1811,44 @@ public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// The entry for SegmentIndexBox(sidx) for MPEG-DASH.
// @doc https://patches.videolan.org/patch/103/
struct SrsMp4SegmentIndexEntry
{
uint8_t reference_type; // 1bit
uint32_t referenced_size; // 31bits
uint32_t subsegment_duration; // 32bits
uint8_t starts_with_SAP; // 1bit
uint8_t SAP_type; // 3bits
uint32_t SAP_delta_time; // 28bits
};
// The SegmentIndexBox(sidx) for MPEG-DASH.
// @doc https://gpac.wp.imt.fr/2012/02/01/dash-support/
// @doc https://patches.videolan.org/patch/103/
// @doc https://github.com/necccc/iso-bmff-parser-stream/blob/master/lib/box/sidx.js
class SrsMp4SegmentIndexBox : public SrsMp4Box
{
public:
uint8_t version;
uint32_t flags;
uint32_t reference_id;
uint32_t timescale;
uint64_t earliest_presentation_time;
uint32_t first_offset;
// TODO: FIXME: Should double check buffer.
std::vector<SrsMp4SegmentIndexEntry> entries;
public:
SrsMp4SegmentIndexBox();
virtual ~SrsMp4SegmentIndexBox();
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// Generally, a MP4 sample contains a frame, for example, a video frame or audio frame.
class SrsMp4Sample
{
@ -2024,12 +2078,12 @@ public:
// @param pts The output pts in milliseconds.
// @param sample The output payload, user must free it.
// @param nb_sample The output size of payload.
virtual srs_error_t write_sample(SrsMp4HandlerType ht, uint16_t ft, uint16_t ct,
virtual srs_error_t write_sample(SrsFormat* format, SrsMp4HandlerType ht, uint16_t ft, uint16_t ct,
uint32_t dts, uint32_t pts, uint8_t* sample, uint32_t nb_sample);
// Flush the encoder, to write the moov.
virtual srs_error_t flush();
private:
virtual srs_error_t copy_sequence_header(bool vsh, uint8_t* sample, uint32_t nb_sample);
virtual srs_error_t copy_sequence_header(SrsFormat* format, bool vsh, uint8_t* sample, uint32_t nb_sample);
virtual srs_error_t do_write_sample(SrsMp4Sample* ps, uint8_t* sample, uint32_t nb_sample);
};
@ -2063,8 +2117,6 @@ private:
uint32_t nb_videos;
uint64_t mdat_bytes;
SrsMp4SampleManager* samples;
private:
uint64_t data_offset;
public:
SrsMp4M2tsSegmentEncoder();
virtual ~SrsMp4M2tsSegmentEncoder();

@ -40,28 +40,19 @@ using namespace std;
ISrsLog* _srs_log = new SrsConsoleLog(SrsLogLevelTrace, false);
ISrsThreadContext* _srs_context = new SrsThreadContext();
int parse(std::string mp4_file, bool verbose)
srs_error_t parse(std::string mp4_file, bool verbose)
{
int ret = ERROR_SUCCESS;
srs_error_t err = srs_success;
SrsFileReader fr;
if ((err = fr.open(mp4_file)) != srs_success) {
// TODO: FIXME: Use error
ret = srs_error_code(err);
srs_freep(err);
srs_error("Open MP4 file failed, ret=%d", ret);
return ret;
return srs_error_wrap(err, "open mp4 file %s", mp4_file.c_str());
}
srs_trace("MP4 file open success");
SrsMp4BoxReader br;
if ((err = br.initialize(&fr)) != srs_success) {
// TODO: FIXME: Use error
ret = srs_error_code(err);
srs_freep(err);
srs_error("Open MP4 box reader failed, ret=%d", ret);
return ret;
return srs_error_wrap(err, "open box reader");
}
srs_trace("MP4 box reader open success");
@ -74,34 +65,21 @@ int parse(std::string mp4_file, bool verbose)
SrsAutoFree(SrsMp4Box, box);
if ((err = br.read(stream, &box)) != srs_success) {
// TODO: FIXME: Use error
ret = srs_error_code(err);
srs_freep(err);
if (ret != ERROR_SYSTEM_FILE_EOF) {
srs_error("Read MP4 box failed, ret=%d", ret);
} else {
if (srs_error_code(err) == ERROR_SYSTEM_FILE_EOF) {
fprintf(stderr, "\n");
}
return ret;
return srs_error_wrap(err, "read box");
}
SrsBuffer* buffer = new SrsBuffer(stream->bytes(), stream->length());
SrsAutoFree(SrsBuffer, buffer);
if ((err = box->decode(buffer)) != srs_success) {
// TODO: FIXME: Use error
ret = srs_error_code(err);
srs_freep(err);
srs_error("Decode the box failed, ret=%d", ret);
return ret;
return srs_error_wrap(err, "decode box");
}
if ((err = br.skip(box, stream)) != srs_success) {
// TODO: FIXME: Use error
ret = srs_error_code(err);
srs_freep(err);
srs_error("Skip MP4 box failed, ret=%d", ret);
return ret;
return srs_error_wrap(err, "skip box");
}
SrsMp4DumpContext ctx;
@ -112,13 +90,11 @@ int parse(std::string mp4_file, bool verbose)
fprintf(stderr, "%s", box->dumps(ss, ctx).str().c_str());
}
return ret;
return err;
}
int main(int argc, char** argv)
{
int ret = ERROR_SUCCESS;
printf("SRS MP4 parser/%d.%d.%d, parse and show the mp4 boxes structure.\n",
VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
@ -140,13 +116,16 @@ int main(int argc, char** argv)
}
srs_trace("Parse MP4 file %s, verbose=%d", mp4_file.c_str(), verbose);
ret = parse(mp4_file, verbose);
if (ret == ERROR_SYSTEM_FILE_EOF) {
srs_error_t err = parse(mp4_file, verbose);
int code = srs_error_code(err);
if (code == ERROR_SYSTEM_FILE_EOF) {
srs_trace("Parse complete");
return 0;
} else {
srs_error("Parse error %s", srs_error_desc(err).c_str());
}
return ret;
srs_freep(err);
return code;
}

@ -486,6 +486,15 @@ srs_error_t SrsHttpFileServer::serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHtt
if (range.empty()) {
range = r->query_get("bytes");
}
// Fetch range from header.
SrsHttpHeader* h = r->header();
if (range.empty() && h) {
range = h->get("Range");
if (range.find("bytes=") == 0) {
range = range.substr(6);
}
}
// rollback to serve whole file.
size_t pos = string::npos;

@ -4589,7 +4589,7 @@ VOID TEST(KernelMP4Test, CoverMP4Codec)
};
EXPECT_TRUE(srs_success == fmt.on_video(0, (char*)raw, sizeof(raw)));
EXPECT_TRUE(srs_success == enc.write_sample(
SrsMp4HandlerTypeVIDE, fmt.video->frame_type, fmt.video->avc_packet_type, 0, 0, (uint8_t*)fmt.raw, fmt.nb_raw
NULL, SrsMp4HandlerTypeVIDE, fmt.video->frame_type, fmt.video->avc_packet_type, 0, 0, (uint8_t*)fmt.raw, fmt.nb_raw
));
}
@ -4599,7 +4599,7 @@ VOID TEST(KernelMP4Test, CoverMP4Codec)
};
EXPECT_TRUE(srs_success == fmt.on_audio(0, (char*)raw, sizeof(raw)));
EXPECT_TRUE(srs_success == enc.write_sample(
SrsMp4HandlerTypeSOUN, 0x00, fmt.audio->aac_packet_type, 0, 0, (uint8_t*)fmt.raw, fmt.nb_raw
NULL, SrsMp4HandlerTypeSOUN, 0x00, fmt.audio->aac_packet_type, 0, 0, (uint8_t*)fmt.raw, fmt.nb_raw
));
}
@ -4615,7 +4615,7 @@ VOID TEST(KernelMP4Test, CoverMP4Codec)
};
EXPECT_TRUE(srs_success == fmt.on_audio(0, (char*)raw, sizeof(raw)));
EXPECT_TRUE(srs_success == enc.write_sample(
SrsMp4HandlerTypeSOUN, 0x00, fmt.audio->aac_packet_type, 34, 34, (uint8_t*)fmt.raw, fmt.nb_raw
NULL, SrsMp4HandlerTypeSOUN, 0x00, fmt.audio->aac_packet_type, 34, 34, (uint8_t*)fmt.raw, fmt.nb_raw
));
}
@ -4634,7 +4634,7 @@ VOID TEST(KernelMP4Test, CoverMP4Codec)
};
EXPECT_TRUE(srs_success == fmt.on_video(0, (char*)raw, sizeof(raw)));
EXPECT_TRUE(srs_success == enc.write_sample(
SrsMp4HandlerTypeVIDE, fmt.video->frame_type, fmt.video->avc_packet_type, 40, 40, (uint8_t*)fmt.raw, fmt.nb_raw
NULL, SrsMp4HandlerTypeVIDE, fmt.video->frame_type, fmt.video->avc_packet_type, 40, 40, (uint8_t*)fmt.raw, fmt.nb_raw
));
}

Loading…
Cancel
Save