Skip to content

Commit

Permalink
for ossrs#293, support http ts stream. 2.0.101
Browse files Browse the repository at this point in the history
  • Loading branch information
winlinvip committed Jan 22, 2015
1 parent dfe385d commit 2a05783
Show file tree
Hide file tree
Showing 7 changed files with 419 additions and 375 deletions.
353 changes: 4 additions & 349 deletions trunk/src/app/srs_app_hls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,369 +51,23 @@ using namespace std;
#include <srs_kernel_file.hpp>
#include <srs_protocol_buffer.hpp>

// max PES packets size to flush the video.
#define SRS_AUTO_HLS_AUDIO_CACHE_SIZE 1024 * 1024

// drop the segment when duration of ts too small.
#define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100

// @see: NGX_RTMP_HLS_DELAY,
// 63000: 700ms, ts_tbn=90000
#define SRS_AUTO_HLS_DELAY 63000

// @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 */
0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac, pid=0x101=257 */
/*0x03, 0xe1, 0x01, 0xf0, 0x00,*/ /* mp3 */
/* CRC */
0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */
/*0x4e, 0x59, 0x3d, 0x1e,*/ /* crc for mp3 */
/* 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)
{
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;
}

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->key) {
p[-1] |= 0x20; // Both Adaption and Payload
*p++ = 7; // size
*p++ = 0x50; // random access + PCR
p = write_pcr(p, frame->dts - SRS_AUTO_HLS_DELAY);
}

// 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_pts(p, flags >> 6, frame->pts + SRS_AUTO_HLS_DELAY);

// dts; // 33bits
if (frame->dts != frame->pts) {
p = write_pts(p, 1, frame->dts + SRS_AUTO_HLS_DELAY);
}
}

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) {
ret = ERROR_HLS_WRITE_FAILED;
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
// and the pcr maybe negative
// @see https://github.com/winlinvip/simple-rtmp-server/issues/268
int64_t v = srs_max(0, 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_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;
}
};

SrsTSMuxer::SrsTSMuxer()
{
writer = new SrsFileWriter();
}

SrsTSMuxer::~SrsTSMuxer()
{
close();
srs_freep(writer);
}

int SrsTSMuxer::open(string _path)
{
int ret = ERROR_SUCCESS;

path = _path;

close();

if ((ret = writer->open(path)) != ERROR_SUCCESS) {
return ret;
}

// write mpegts header
if ((ret = SrsMpegtsWriter::write_header(writer)) != ERROR_SUCCESS) {
return ret;
}

return ret;
}

int SrsTSMuxer::write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab)
{
int ret = ERROR_SUCCESS;

if ((ret = SrsMpegtsWriter::write_frame(writer, af, ab)) != ERROR_SUCCESS) {
return ret;
}

return ret;
}

int SrsTSMuxer::write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb)
{
int ret = ERROR_SUCCESS;

if ((ret = SrsMpegtsWriter::write_frame(writer, vf, vb)) != ERROR_SUCCESS) {
return ret;
}

return ret;
}

void SrsTSMuxer::close()
{
writer->close();
}

SrsHlsSegment::SrsHlsSegment()
{
duration = 0;
sequence_no = 0;
muxer = new SrsTSMuxer();
segment_start_dts = 0;
is_sequence_header = false;
writer = new SrsFileWriter();
muxer = new SrsTSMuxer(writer);
}

SrsHlsSegment::~SrsHlsSegment()
{
srs_freep(muxer);
srs_freep(writer);
}

void SrsHlsSegment::update_duration(int64_t current_frame_dts)
Expand Down Expand Up @@ -926,6 +580,7 @@ int SrsHlsCache::write_audio(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t
return ret;
}
}

// TODO: config it.
// in ms, audio delay to flush the audios.
int64_t audio_delay = SRS_CONF_DEFAULT_AAC_DELAY;
Expand Down
Loading

0 comments on commit 2a05783

Please sign in to comment.