mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
for #304, support config default acodec/vcodec. 2.0.118.
This commit is contained in:
parent
922150b2cf
commit
78f34ad46f
10 changed files with 114 additions and 381 deletions
|
@ -527,6 +527,7 @@ Supported operating systems and hardware:
|
|||
|
||||
### SRS 2.0 history
|
||||
|
||||
* v2.0, 2015-02-15, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), support config default acodec/vcodec. 2.0.118.
|
||||
* v2.0, 2015-02-15, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), rewrite hls/ts code, support h.264+mp3 for hls. 2.0.117.
|
||||
* v2.0, 2015-02-12, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), use stringstream to generate m3u8, add hls_td_ratio. 2.0.116.
|
||||
* v2.0, 2015-02-11, dev code ZhouGuowen for 2.0.115.
|
||||
|
|
|
@ -495,10 +495,19 @@ vhost with-hls.srs.com {
|
|||
# the default audio codec of hls.
|
||||
# when codec changed, write the PAT/PMT table, but maybe ok util next ts.
|
||||
# so user can set the default codec for mp3.
|
||||
# the available audio codec: aac, mp3
|
||||
# the available audio codec:
|
||||
# aac, mp3
|
||||
# default: aac
|
||||
# TODO: FIXME: update wiki for it.
|
||||
hls_acodec aac;
|
||||
# the default video codec of hls.
|
||||
# when codec changed, write the PAT/PMT table, but maybe ok util next ts.
|
||||
# so user can set the default codec for pure audio(without video) to vn.
|
||||
# the available video codec:
|
||||
# h264, vn
|
||||
# default: h264
|
||||
# TODO: FIXME: update wiki for it.
|
||||
hls_vcodec h264;
|
||||
}
|
||||
}
|
||||
# the vhost with hls disabled.
|
||||
|
@ -713,9 +722,9 @@ vhost example.transcode.srs.com {
|
|||
filter_complex 'overlay=10:10';
|
||||
}
|
||||
# video encoder name. can be:
|
||||
# libx264: use h.264(libx264) video encoder.
|
||||
# copy: donot encoder the video stream, copy it.
|
||||
# vn: disable video output.
|
||||
# libx264: use h.264(libx264) video encoder.
|
||||
# copy: donot encoder the video stream, copy it.
|
||||
# vn: disable video output.
|
||||
vcodec libx264;
|
||||
# video bitrate, in kbps
|
||||
vbitrate 1500;
|
||||
|
@ -731,8 +740,8 @@ vhost example.transcode.srs.com {
|
|||
# high,main,baseline
|
||||
vprofile main;
|
||||
# x264 preset, @see x264 -help, can be:
|
||||
# ultrafast,superfast,veryfast,faster,fast
|
||||
# medium,slow,slower,veryslow,placebo
|
||||
# ultrafast,superfast,veryfast,faster,fast
|
||||
# medium,slow,slower,veryslow,placebo
|
||||
vpreset medium;
|
||||
# other x264 or ffmpeg video params
|
||||
vparams {
|
||||
|
@ -745,14 +754,15 @@ vhost example.transcode.srs.com {
|
|||
refs 10;
|
||||
}
|
||||
# audio encoder name. can be:
|
||||
# libaacplus: use aac(libaacplus) audio encoder.
|
||||
# copy: donot encoder the audio stream, copy it.
|
||||
# an: disable audio output.
|
||||
# libaacplus: use aac(libaacplus) audio encoder.
|
||||
# libfdk_aac: use aac(libfdk_aac) audio encoder.
|
||||
# copy: donot encoder the audio stream, copy it.
|
||||
# an: disable audio output.
|
||||
acodec libaacplus;
|
||||
# audio bitrate, in kbps. [16, 72] for libaacplus.
|
||||
abitrate 70;
|
||||
# audio sample rate. for flv/rtmp, it must be:
|
||||
# 44100,22050,11025,5512
|
||||
# 44100,22050,11025,5512
|
||||
asample_rate 44100;
|
||||
# audio channel, 1 for mono, 2 for stereo.
|
||||
achannels 2;
|
||||
|
@ -762,17 +772,17 @@ vhost example.transcode.srs.com {
|
|||
profile:a aac_low;
|
||||
}
|
||||
# output format, can be:
|
||||
# off, do not specifies the format, ffmpeg will guess it.
|
||||
# flv, for flv or RTMP stream.
|
||||
# other format, for example, mp4/aac whatever.
|
||||
# off, do not specifies the format, ffmpeg will guess it.
|
||||
# flv, for flv or RTMP stream.
|
||||
# other format, for example, mp4/aac whatever.
|
||||
# default: flv
|
||||
oformat flv;
|
||||
# output stream. variables:
|
||||
# [vhost] the input stream vhost.
|
||||
# [port] the intput stream port.
|
||||
# [app] the input stream app.
|
||||
# [stream] the input stream name.
|
||||
# [engine] the tanscode engine name.
|
||||
# [vhost] the input stream vhost.
|
||||
# [port] the intput stream port.
|
||||
# [app] the input stream app.
|
||||
# [stream] the input stream name.
|
||||
# [engine] the tanscode engine name.
|
||||
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1480,7 +1480,7 @@ int SrsConfig::check_config()
|
|||
for (int j = 0; j < (int)conf->directives.size(); j++) {
|
||||
string m = conf->at(j)->name.c_str();
|
||||
if (m != "enabled" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error"
|
||||
&& m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_acodec"
|
||||
&& m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_acodec" && m != "hls_vcodec"
|
||||
) {
|
||||
ret = ERROR_SYSTEM_CONFIG_INVALID;
|
||||
srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret);
|
||||
|
@ -3349,6 +3349,23 @@ string SrsConfig::get_hls_acodec(string vhost)
|
|||
return conf->arg0();
|
||||
}
|
||||
|
||||
string SrsConfig::get_hls_vcodec(string vhost)
|
||||
{
|
||||
SrsConfDirective* hls = get_hls(vhost);
|
||||
|
||||
if (!hls) {
|
||||
return SRS_CONF_DEFAULT_HLS_VCODEC;
|
||||
}
|
||||
|
||||
SrsConfDirective* conf = hls->get("hls_vcodec");
|
||||
|
||||
if (!conf) {
|
||||
return SRS_CONF_DEFAULT_HLS_VCODEC;
|
||||
}
|
||||
|
||||
return conf->arg0();
|
||||
}
|
||||
|
||||
SrsConfDirective* SrsConfig::get_dvr(string vhost)
|
||||
{
|
||||
SrsConfDirective* conf = get_vhost(vhost);
|
||||
|
|
|
@ -56,6 +56,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
#define SRS_CONF_DEFAULT_HLS_STORAGE "disk"
|
||||
#define SRS_CONF_DEFAULT_HLS_MOUNT "[vhost]/[app]/[stream].m3u8"
|
||||
#define SRS_CONF_DEFAULT_HLS_ACODEC "aac"
|
||||
#define SRS_CONF_DEFAULT_HLS_VCODEC "h264"
|
||||
#define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html"
|
||||
#define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session"
|
||||
#define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment"
|
||||
|
@ -927,6 +928,10 @@ public:
|
|||
* get the HLS default audio codec.
|
||||
*/
|
||||
virtual std::string get_hls_acodec(std::string vhost);
|
||||
/**
|
||||
* get the HLS default video codec.
|
||||
*/
|
||||
virtual std::string get_hls_vcodec(std::string vhost);
|
||||
// dvr section
|
||||
private:
|
||||
/**
|
||||
|
|
|
@ -131,14 +131,14 @@ string SrsHlsCacheWriter::cache()
|
|||
return data;
|
||||
}
|
||||
|
||||
SrsHlsSegment::SrsHlsSegment(bool write_cache, bool write_file, SrsCodecAudio ac)
|
||||
SrsHlsSegment::SrsHlsSegment(bool write_cache, bool write_file, SrsCodecAudio ac, SrsCodecVideo vc)
|
||||
{
|
||||
duration = 0;
|
||||
sequence_no = 0;
|
||||
segment_start_dts = 0;
|
||||
is_sequence_header = false;
|
||||
writer = new SrsHlsCacheWriter(write_cache, write_file);
|
||||
muxer = new SrsTSMuxer(writer, ac);
|
||||
muxer = new SrsTSMuxer(writer, ac, vc);
|
||||
}
|
||||
|
||||
SrsHlsSegment::~SrsHlsSegment()
|
||||
|
@ -246,19 +246,36 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
|
|||
|
||||
// load the default acodec from config.
|
||||
SrsCodecAudio default_acodec = SrsCodecAudioAAC;
|
||||
std::string default_acodec_str = _srs_config->get_hls_acodec(req->vhost);
|
||||
if (default_acodec_str == "mp3") {
|
||||
default_acodec = SrsCodecAudioMP3;
|
||||
srs_info("hls: use default mp3 acodec");
|
||||
} else if (default_acodec_str == "aac") {
|
||||
default_acodec = SrsCodecAudioAAC;
|
||||
srs_info("hls: use default aac acodec");
|
||||
} else {
|
||||
srs_warn("hls: use aac for other codec=%s", default_acodec_str.c_str());
|
||||
if (true) {
|
||||
std::string default_acodec_str = _srs_config->get_hls_acodec(req->vhost);
|
||||
if (default_acodec_str == "mp3") {
|
||||
default_acodec = SrsCodecAudioMP3;
|
||||
srs_info("hls: use default mp3 acodec");
|
||||
} else if (default_acodec_str == "aac") {
|
||||
default_acodec = SrsCodecAudioAAC;
|
||||
srs_info("hls: use default aac acodec");
|
||||
} else {
|
||||
srs_warn("hls: use aac for other codec=%s", default_acodec_str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// load the default vcodec from config.
|
||||
SrsCodecVideo default_vcodec = SrsCodecVideoAVC;
|
||||
if (true) {
|
||||
std::string default_vcodec_str = _srs_config->get_hls_vcodec(req->vhost);
|
||||
if (default_vcodec_str == "h264") {
|
||||
default_vcodec = SrsCodecVideoAVC;
|
||||
srs_info("hls: use default h264 vcodec");
|
||||
} else if (default_vcodec_str == "vn") {
|
||||
default_vcodec = SrsCodecVideoDisabled;
|
||||
srs_info("hls: use default vn vcodec for pure audio");
|
||||
} else {
|
||||
srs_warn("hls: use h264 for other codec=%s", default_vcodec_str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// new segment.
|
||||
current = new SrsHlsSegment(should_write_cache, should_write_file, default_acodec);
|
||||
current = new SrsHlsSegment(should_write_cache, should_write_file, default_acodec, default_vcodec);
|
||||
current->sequence_no = _sequence_no++;
|
||||
current->segment_start_dts = segment_start_dts;
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ public:
|
|||
// whether current segement is sequence header.
|
||||
bool is_sequence_header;
|
||||
public:
|
||||
SrsHlsSegment(bool write_cache, bool write_file, SrsCodecAudio ac);
|
||||
SrsHlsSegment(bool write_cache, bool write_file, SrsCodecAudio ac, SrsCodecVideo vc);
|
||||
virtual ~SrsHlsSegment();
|
||||
public:
|
||||
/**
|
||||
|
|
|
@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
// current release version
|
||||
#define VERSION_MAJOR 2
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 117
|
||||
#define VERSION_REVISION 118
|
||||
|
||||
// server info.
|
||||
#define RTMP_SIG_SRS_KEY "SRS"
|
||||
|
|
|
@ -98,7 +98,10 @@ enum SrsCodecVideo
|
|||
// set to the zero to reserved, for array map.
|
||||
SrsCodecVideoReserved = 0,
|
||||
SrsCodecVideoReserved1 = 1,
|
||||
SrsCodecVideoReserved2 = 8,
|
||||
SrsCodecVideoReserved2 = 9,
|
||||
|
||||
// for user to disable video, for example, use pure audio hls.
|
||||
SrsCodecVideoDisabled = 8,
|
||||
|
||||
SrsCodecVideoSorensonH263 = 2,
|
||||
SrsCodecVideoScreenVideo = 3,
|
||||
|
|
|
@ -75,327 +75,6 @@ int aac_sample_rates[] =
|
|||
7350, 0, 0, 0
|
||||
};
|
||||
|
||||
// @see: ngx_rtmp_mpegts_header
|
||||
u_int8_t mpegts_header[] = {
|
||||
/* TS */
|
||||
0x47, 0x40, 0x00, 0x10, 0x00,
|
||||
/* PSI */
|
||||
0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00,
|
||||
/* PAT */
|
||||
0x00, 0x01, 0xf0, 0x01,
|
||||
/* CRC */
|
||||
0x2e, 0x70, 0x19, 0x05,
|
||||
/* stuffing 167 bytes */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
|
||||
/* TS */
|
||||
0x47, 0x50, 0x01, 0x10, 0x00,
|
||||
/* PSI */
|
||||
0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00,
|
||||
/* PMT */
|
||||
0xe1, 0x00,
|
||||
0xf0, 0x00,
|
||||
// must generate header with/without video, @see:
|
||||
// https://github.com/winlinvip/simple-rtmp-server/issues/40
|
||||
0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264, pid=0x100=256 */
|
||||
};
|
||||
u_int8_t mpegts_header_aac[] = {
|
||||
0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac, pid=0x101=257 */
|
||||
/* CRC */
|
||||
0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */
|
||||
};
|
||||
u_int8_t mpegts_header_mp3[] = {
|
||||
0x03, 0xe1, 0x01, 0xf0, 0x00, /* mp3 */
|
||||
/* CRC */
|
||||
0x4e, 0x59, 0x3d, 0x1e, /* crc for mp3 */
|
||||
};
|
||||
u_int8_t mpegts_header_padding[] = {
|
||||
/* stuffing 157 bytes */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
// @see: ngx_rtmp_mpegts.c
|
||||
// TODO: support full mpegts feature in future.
|
||||
class SrsMpegtsWriter
|
||||
{
|
||||
public:
|
||||
static int write_header(SrsFileWriter* writer, SrsCodecAudio acodec)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
if ((ret = writer->write(mpegts_header, sizeof(mpegts_header), NULL)) != ERROR_SUCCESS) {
|
||||
ret = ERROR_HLS_WRITE_FAILED;
|
||||
srs_error("write ts file header failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (acodec == SrsCodecAudioAAC) {
|
||||
if ((ret = writer->write(mpegts_header_aac, sizeof(mpegts_header_aac), NULL)) != ERROR_SUCCESS) {
|
||||
ret = ERROR_HLS_WRITE_FAILED;
|
||||
srs_error("write ts file aac header failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if ((ret = writer->write(mpegts_header_mp3, sizeof(mpegts_header_mp3), NULL)) != ERROR_SUCCESS) {
|
||||
ret = ERROR_HLS_WRITE_FAILED;
|
||||
srs_error("write ts file mp3 header failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret = writer->write(mpegts_header_padding, sizeof(mpegts_header_padding), NULL)) != ERROR_SUCCESS) {
|
||||
ret = ERROR_HLS_WRITE_FAILED;
|
||||
srs_error("write ts file padding header failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int write_frame(SrsFileWriter* writer, SrsMpegtsFrame* frame, SrsSimpleBuffer* buffer)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
if (!buffer->bytes() || buffer->length() <= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
char* last = buffer->bytes() + buffer->length();
|
||||
char* pos = buffer->bytes();
|
||||
|
||||
bool first = true;
|
||||
while (pos < last) {
|
||||
static char packet[188];
|
||||
char* p = packet;
|
||||
|
||||
frame->cc++;
|
||||
|
||||
// sync_byte; //8bits
|
||||
*p++ = 0x47;
|
||||
// pid; //13bits
|
||||
*p++ = (frame->pid >> 8) & 0x1f;
|
||||
// payload_unit_start_indicator; //1bit
|
||||
if (first) {
|
||||
p[-1] |= 0x40;
|
||||
}
|
||||
*p++ = frame->pid;
|
||||
|
||||
// transport_scrambling_control; //2bits
|
||||
// adaption_field_control; //2bits, 0x01: PayloadOnly
|
||||
// continuity_counter; //4bits
|
||||
*p++ = 0x10 | (frame->cc & 0x0f);
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
if (frame->write_pcr) {
|
||||
p[-1] |= 0x20; // Both Adaption and Payload
|
||||
*p++ = 7; // size
|
||||
*p++ = 0x50; // random access + PCR
|
||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/311
|
||||
p = write_pcr(p, frame->dts);
|
||||
}
|
||||
|
||||
// PES header
|
||||
// packet_start_code_prefix; //24bits, '00 00 01'
|
||||
*p++ = 0x00;
|
||||
*p++ = 0x00;
|
||||
*p++ = 0x01;
|
||||
//8bits
|
||||
*p++ = frame->sid;
|
||||
|
||||
// pts(33bits) need 5bytes.
|
||||
u_int8_t header_size = 5;
|
||||
u_int8_t flags = 0x80; // pts
|
||||
|
||||
// dts(33bits) need 5bytes also
|
||||
if (frame->dts != frame->pts) {
|
||||
header_size += 5;
|
||||
flags |= 0x40; // dts
|
||||
}
|
||||
|
||||
// 3bytes: flag fields from PES_packet_length to PES_header_data_length
|
||||
int pes_size = (last - pos) + header_size + 3;
|
||||
if (pes_size > 0xffff) {
|
||||
/**
|
||||
* when actual packet length > 0xffff(65535),
|
||||
* which exceed the max u_int16_t packet length,
|
||||
* use 0 packet length, the next unit start indicates the end of packet.
|
||||
*/
|
||||
pes_size = 0;
|
||||
}
|
||||
|
||||
// PES_packet_length; //16bits
|
||||
*p++ = (pes_size >> 8);
|
||||
*p++ = pes_size;
|
||||
|
||||
// PES_scrambling_control; //2bits, '10'
|
||||
// PES_priority; //1bit
|
||||
// data_alignment_indicator; //1bit
|
||||
// copyright; //1bit
|
||||
// original_or_copy; //1bit
|
||||
*p++ = 0x80; /* H222 */
|
||||
|
||||
// PTS_DTS_flags; //2bits
|
||||
// ESCR_flag; //1bit
|
||||
// ES_rate_flag; //1bit
|
||||
// DSM_trick_mode_flag; //1bit
|
||||
// additional_copy_info_flag; //1bit
|
||||
// PES_CRC_flag; //1bit
|
||||
// PES_extension_flag; //1bit
|
||||
*p++ = flags;
|
||||
|
||||
// PES_header_data_length; //8bits
|
||||
*p++ = header_size;
|
||||
|
||||
// pts; // 33bits
|
||||
p = write_dts_pts(p, flags >> 6, frame->pts);
|
||||
|
||||
// dts; // 33bits
|
||||
if (frame->dts != frame->pts) {
|
||||
p = write_dts_pts(p, 1, frame->dts);
|
||||
}
|
||||
}
|
||||
|
||||
int body_size = sizeof(packet) - (p - packet);
|
||||
int in_size = last - pos;
|
||||
|
||||
if (body_size <= in_size) {
|
||||
memcpy(p, pos, body_size);
|
||||
pos += body_size;
|
||||
} else {
|
||||
p = fill_stuff(p, packet, body_size, in_size);
|
||||
memcpy(p, pos, in_size);
|
||||
pos = last;
|
||||
}
|
||||
|
||||
// write ts packet
|
||||
if ((ret = writer->write(packet, sizeof(packet), NULL)) != ERROR_SUCCESS) {
|
||||
if (!srs_is_client_gracefully_close(ret)) {
|
||||
srs_error("write ts file failed. ret=%d", ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
private:
|
||||
static char* fill_stuff(char* pes_body_end, char* packet, int body_size, int in_size)
|
||||
{
|
||||
char* p = pes_body_end;
|
||||
|
||||
// insert the stuff bytes before PES body
|
||||
int stuff_size = (body_size - in_size);
|
||||
|
||||
// adaption_field_control; //2bits
|
||||
if (packet[3] & 0x20) {
|
||||
// has adaptation
|
||||
// packet[4]: adaption_field_length
|
||||
// packet[5]: adaption field data
|
||||
// base: start of PES body
|
||||
char* base = &packet[5] + packet[4];
|
||||
int len = p - base;
|
||||
p = (char*)memmove(base + stuff_size, base, len) + len;
|
||||
// increase the adaption field size.
|
||||
packet[4] += stuff_size;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// create adaption field.
|
||||
// adaption_field_control; //2bits
|
||||
packet[3] |= 0x20;
|
||||
// base: start of PES body
|
||||
char* base = &packet[4];
|
||||
int len = p - base;
|
||||
p = (char*)memmove(base + stuff_size, base, len) + len;
|
||||
// adaption_field_length; //8bits
|
||||
packet[4] = (stuff_size - 1);
|
||||
if (stuff_size >= 2) {
|
||||
// adaption field flags.
|
||||
packet[5] = 0;
|
||||
// adaption data.
|
||||
if (stuff_size > 2) {
|
||||
memset(&packet[6], 0xff, stuff_size - 2);
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
static char* write_pcr(char* p, int64_t pcr)
|
||||
{
|
||||
// the pcr=dts-delay, where dts = frame->dts + delay
|
||||
// and the pcr should never be negative
|
||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/268
|
||||
srs_assert(pcr >= 0);
|
||||
|
||||
int64_t v = pcr;
|
||||
|
||||
*p++ = (char) (v >> 25);
|
||||
*p++ = (char) (v >> 17);
|
||||
*p++ = (char) (v >> 9);
|
||||
*p++ = (char) (v >> 1);
|
||||
*p++ = (char) (v << 7 | 0x7e);
|
||||
*p++ = 0;
|
||||
|
||||
return p;
|
||||
}
|
||||
static char* write_dts_pts(char* p, u_int8_t fb, int64_t pts)
|
||||
{
|
||||
int32_t val;
|
||||
|
||||
val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1;
|
||||
*p++ = val;
|
||||
|
||||
val = (((pts >> 15) & 0x7fff) << 1) | 1;
|
||||
*p++ = (val >> 8);
|
||||
*p++ = val;
|
||||
|
||||
val = (((pts) & 0x7fff) << 1) | 1;
|
||||
*p++ = (val >> 8);
|
||||
*p++ = val;
|
||||
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
SrsMpegtsFrame::SrsMpegtsFrame()
|
||||
{
|
||||
pts = dts = 0;
|
||||
pid = sid = cc = 0;
|
||||
write_pcr = false;
|
||||
}
|
||||
|
||||
string srs_ts_stream2string(SrsTsStream stream)
|
||||
{
|
||||
switch (stream) {
|
||||
|
@ -600,6 +279,7 @@ int SrsTsContext::encode(SrsFileWriter* writer, SrsTsMessage* msg, SrsCodecVideo
|
|||
case SrsCodecVideoReserved:
|
||||
case SrsCodecVideoReserved1:
|
||||
case SrsCodecVideoReserved2:
|
||||
case SrsCodecVideoDisabled:
|
||||
case SrsCodecVideoSorensonH263:
|
||||
case SrsCodecVideoScreenVideo:
|
||||
case SrsCodecVideoOn2VP6:
|
||||
|
@ -645,9 +325,9 @@ int SrsTsContext::encode(SrsFileWriter* writer, SrsTsMessage* msg, SrsCodecVideo
|
|||
|
||||
// encode the media frame to PES packets over TS.
|
||||
if (msg->is_audio()) {
|
||||
return encode_pes(writer, msg, audio_pid, as);
|
||||
return encode_pes(writer, msg, audio_pid, as, vs == SrsTsStreamReserved);
|
||||
} else {
|
||||
return encode_pes(writer, msg, video_pid, vs);
|
||||
return encode_pes(writer, msg, video_pid, vs, vs == SrsTsStreamReserved);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -711,7 +391,7 @@ int SrsTsContext::encode_pat_pmt(SrsFileWriter* writer, int16_t vpid, SrsTsStrea
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsTsContext::encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t pid, SrsTsStream sid)
|
||||
int SrsTsContext::encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t pid, SrsTsStream sid, bool pure_audio)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -719,6 +399,11 @@ int SrsTsContext::encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t p
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (sid != SrsTsStreamVideoH264 && sid != SrsTsStreamAudioMp3 && sid != SrsTsStreamAudioAAC) {
|
||||
srs_info("ts: ignore the unknown stream, sid=%d", sid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SrsTsChannel* channel = get(pid);
|
||||
srs_assert(channel);
|
||||
|
||||
|
@ -729,9 +414,15 @@ int SrsTsContext::encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t p
|
|||
while (p < end) {
|
||||
SrsTsPacket* pkt = NULL;
|
||||
if (p == start) {
|
||||
// for pure audio stream, always write pcr.
|
||||
bool write_pcr = msg->write_pcr;
|
||||
if (pure_audio && msg->is_audio()) {
|
||||
write_pcr = true;
|
||||
}
|
||||
|
||||
pkt = SrsTsPacket::create_pes_first(this,
|
||||
pid, msg->sid, channel->continuity_counter++, msg->discontinuity,
|
||||
msg->write_pcr? msg->dts:-1, msg->dts, msg->pts, msg->payload->length()
|
||||
write_pcr? msg->dts:-1, msg->dts, msg->pts, msg->payload->length()
|
||||
);
|
||||
} else {
|
||||
pkt = SrsTsPacket::create_pes_continue(this,
|
||||
|
@ -1030,9 +721,13 @@ SrsTsPacket* SrsTsPacket::create_pmt(SrsTsContext* context, int16_t pmt_number,
|
|||
pmt->current_next_indicator = 1;
|
||||
pmt->section_number = 0;
|
||||
pmt->last_section_number = 0;
|
||||
pmt->PCR_PID = vpid;
|
||||
pmt->program_info_length = 0;
|
||||
pmt->infos.push_back(new SrsTsPayloadPMTESInfo(vs, vpid));
|
||||
if (vs == SrsTsStreamVideoH264) {
|
||||
pmt->PCR_PID = vpid;
|
||||
pmt->infos.push_back(new SrsTsPayloadPMTESInfo(vs, vpid));
|
||||
} else {
|
||||
pmt->PCR_PID = apid;
|
||||
}
|
||||
pmt->infos.push_back(new SrsTsPayloadPMTESInfo(as, apid));
|
||||
pmt->CRC_32 = 0; // calc in encode.
|
||||
return pkt;
|
||||
|
@ -2916,14 +2611,13 @@ int SrsTsPayloadPMT::psi_encode(SrsStream* stream)
|
|||
return ret;
|
||||
}
|
||||
|
||||
SrsTSMuxer::SrsTSMuxer(SrsFileWriter* w, SrsCodecAudio ac)
|
||||
SrsTSMuxer::SrsTSMuxer(SrsFileWriter* w, SrsCodecAudio ac, SrsCodecVideo vc)
|
||||
{
|
||||
writer = w;
|
||||
context = NULL;
|
||||
|
||||
acodec = ac;
|
||||
// default to avc(h.264)
|
||||
vcodec = SrsCodecVideoAVC;
|
||||
vcodec = vc;
|
||||
}
|
||||
|
||||
SrsTSMuxer::~SrsTSMuxer()
|
||||
|
@ -3295,7 +2989,7 @@ int SrsTsEncoder::initialize(SrsFileWriter* fs)
|
|||
_fs = fs;
|
||||
|
||||
srs_freep(muxer);
|
||||
muxer = new SrsTSMuxer(fs, SrsCodecAudioAAC);
|
||||
muxer = new SrsTSMuxer(fs, SrsCodecAudioAAC, SrsCodecVideoAVC);
|
||||
|
||||
if ((ret = muxer->open("")) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
|
|
|
@ -51,20 +51,6 @@ class SrsTsPacket;
|
|||
// Transport Stream packets are 188 bytes in length.
|
||||
#define SRS_TS_PACKET_SIZE 188
|
||||
|
||||
// @see: ngx_rtmp_SrsMpegtsFrame_t
|
||||
class SrsMpegtsFrame
|
||||
{
|
||||
public:
|
||||
int64_t pts;
|
||||
int64_t dts;
|
||||
int pid;
|
||||
int sid;
|
||||
int cc; // continuity_counter
|
||||
bool write_pcr;
|
||||
|
||||
SrsMpegtsFrame();
|
||||
};
|
||||
|
||||
/**
|
||||
* the pid of ts packet,
|
||||
* Table 2-3 - PID table, hls-mpeg-ts-iso13818-1.pdf, page 37
|
||||
|
@ -387,7 +373,7 @@ public:
|
|||
virtual int encode(SrsFileWriter* writer, SrsTsMessage* msg, SrsCodecVideo vc, SrsCodecAudio ac);
|
||||
private:
|
||||
virtual int encode_pat_pmt(SrsFileWriter* writer, int16_t vpid, SrsTsStream vs, int16_t apid, SrsTsStream as);
|
||||
virtual int encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t pid, SrsTsStream sid);
|
||||
virtual int encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t pid, SrsTsStream sid, bool pure_audio);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1547,7 +1533,7 @@ private:
|
|||
SrsFileWriter* writer;
|
||||
std::string path;
|
||||
public:
|
||||
SrsTSMuxer(SrsFileWriter* w, SrsCodecAudio ac);
|
||||
SrsTSMuxer(SrsFileWriter* w, SrsCodecAudio ac, SrsCodecVideo vc);
|
||||
virtual ~SrsTSMuxer();
|
||||
public:
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue