mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
support write ts file
This commit is contained in:
parent
ca6720aab2
commit
4fca914694
7 changed files with 441 additions and 9 deletions
|
@ -50,6 +50,7 @@ url: rtmp://127.0.0.1:1935/live/livestream
|
||||||
* nginx v1.5.0: 139524 lines <br/>
|
* nginx v1.5.0: 139524 lines <br/>
|
||||||
|
|
||||||
### History
|
### History
|
||||||
|
* v0.5, 2013-11-24, support write ts file.
|
||||||
* v0.5, 2013-11-21, add ts_info tool to demux ts file.
|
* v0.5, 2013-11-21, add ts_info tool to demux ts file.
|
||||||
* v0.5, 2013-11-16, add rtmp players(OSMF/jwplayer5/jwplayer6).
|
* v0.5, 2013-11-16, add rtmp players(OSMF/jwplayer5/jwplayer6).
|
||||||
* v0.4, 2013-11-10, v0.4 released. 12500 lines.
|
* v0.4, 2013-11-10, v0.4 released. 12500 lines.
|
||||||
|
|
0
trunk/configure
vendored
Normal file → Executable file
0
trunk/configure
vendored
Normal file → Executable file
|
@ -24,12 +24,35 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#include <srs_core_codec.hpp>
|
#include <srs_core_codec.hpp>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <srs_core_error.hpp>
|
#include <srs_core_error.hpp>
|
||||||
#include <srs_core_stream.hpp>
|
#include <srs_core_stream.hpp>
|
||||||
#include <srs_core_log.hpp>
|
#include <srs_core_log.hpp>
|
||||||
#include <srs_core_autofree.hpp>
|
#include <srs_core_autofree.hpp>
|
||||||
|
|
||||||
|
SrsCodecBuffer::SrsCodecBuffer()
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
bytes = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsCodecBuffer::append(void* data, int len)
|
||||||
|
{
|
||||||
|
srs_assert(data);
|
||||||
|
srs_assert(len > 0);
|
||||||
|
|
||||||
|
bytes = (char*)realloc(bytes, size + len);
|
||||||
|
memcpy(bytes + size, data, len);
|
||||||
|
size += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsCodecBuffer::free()
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
srs_freepa(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
SrsCodecSample::SrsCodecSample()
|
SrsCodecSample::SrsCodecSample()
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
|
@ -83,8 +106,11 @@ SrsCodec::SrsCodec()
|
||||||
video_codec_id = 0;
|
video_codec_id = 0;
|
||||||
audio_data_rate = 0;
|
audio_data_rate = 0;
|
||||||
audio_codec_id = 0;
|
audio_codec_id = 0;
|
||||||
profile = 0;
|
avc_profile = 0;
|
||||||
level = 0;
|
avc_level = 0;
|
||||||
|
aac_profile = 0;
|
||||||
|
aac_sample_rate = 0;
|
||||||
|
aac_channels = 0;
|
||||||
avc_extra_size = 0;
|
avc_extra_size = 0;
|
||||||
avc_extra_data = NULL;
|
avc_extra_data = NULL;
|
||||||
aac_extra_size = 0;
|
aac_extra_size = 0;
|
||||||
|
@ -160,6 +186,39 @@ int SrsCodec::audio_aac_demux(int8_t* data, int size, SrsCodecSample* sample)
|
||||||
aac_extra_data = new char[aac_extra_size];
|
aac_extra_data = new char[aac_extra_size];
|
||||||
memcpy(aac_extra_data, stream->current(), aac_extra_size);
|
memcpy(aac_extra_data, stream->current(), aac_extra_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only need to decode the first 2bytes:
|
||||||
|
// audioObjectType, aac_profile, 5bits.
|
||||||
|
// samplingFrequencyIndex, aac_sample_rate, 4bits.
|
||||||
|
// channelConfiguration, aac_channels, 4bits
|
||||||
|
if (!stream->require(2)) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("hls decode audio aac sequence header failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
aac_profile = stream->read_1bytes();
|
||||||
|
aac_sample_rate = stream->read_1bytes();
|
||||||
|
|
||||||
|
aac_channels = (aac_sample_rate >> 3) & 0x0f;
|
||||||
|
aac_sample_rate = ((aac_profile << 1) & 0x0e) | ((aac_sample_rate >> 7) & 0x01);
|
||||||
|
aac_profile = (aac_profile >> 3) & 0x1f;
|
||||||
|
|
||||||
|
if (aac_profile == 0 || aac_profile == 0x1f) {
|
||||||
|
ret = ERROR_HLS_DECODE_ERROR;
|
||||||
|
srs_error("hls decode audio aac sequence header failed, "
|
||||||
|
"adts object=%d invalid. ret=%d", aac_profile, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// aac_profile = audioObjectType - 1
|
||||||
|
aac_profile--;
|
||||||
|
|
||||||
|
if (aac_profile > 3) {
|
||||||
|
// Mark all extended profiles as LC
|
||||||
|
// to make Android as happy as possible.
|
||||||
|
// @see: ngx_rtmp_hls_parse_aac_header
|
||||||
|
aac_profile = 1;
|
||||||
|
}
|
||||||
} else if (aac_packet_type == SrsCodecAudioTypeRawData) {
|
} else if (aac_packet_type == SrsCodecAudioTypeRawData) {
|
||||||
// ensure the sequence header demuxed
|
// ensure the sequence header demuxed
|
||||||
if (aac_extra_size <= 0 || !aac_extra_data) {
|
if (aac_extra_size <= 0 || !aac_extra_data) {
|
||||||
|
|
|
@ -181,8 +181,21 @@ enum SrsCodecAudioSoundType
|
||||||
*/
|
*/
|
||||||
struct SrsCodecBuffer
|
struct SrsCodecBuffer
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @remark user must manage the bytes.
|
||||||
|
*/
|
||||||
int size;
|
int size;
|
||||||
char* bytes;
|
char* bytes;
|
||||||
|
|
||||||
|
SrsCodecBuffer();
|
||||||
|
void append(void* data, int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* free the bytes,
|
||||||
|
* user can invoke it to free the bytes,
|
||||||
|
* the SrsCodecBuffer never free automatically.
|
||||||
|
*/
|
||||||
|
void free();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -227,9 +240,9 @@ public:
|
||||||
// @see: SrsCodecVideo
|
// @see: SrsCodecVideo
|
||||||
int video_codec_id;
|
int video_codec_id;
|
||||||
// profile_idc, H.264-AVC-ISO_IEC_14496-10.pdf, page 45.
|
// profile_idc, H.264-AVC-ISO_IEC_14496-10.pdf, page 45.
|
||||||
u_int8_t profile;
|
u_int8_t avc_profile;
|
||||||
// level_idc, H.264-AVC-ISO_IEC_14496-10.pdf, page 45.
|
// level_idc, H.264-AVC-ISO_IEC_14496-10.pdf, page 45.
|
||||||
u_int8_t level;
|
u_int8_t avc_level;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int video_data_rate; // in bps
|
int video_data_rate; // in bps
|
||||||
|
@ -243,6 +256,13 @@ public:
|
||||||
// @see: SrsCodecAudioType
|
// @see: SrsCodecAudioType
|
||||||
int audio_codec_id;
|
int audio_codec_id;
|
||||||
int audio_data_rate; // in bps
|
int audio_data_rate; // in bps
|
||||||
|
// 1.6.2.1 AudioSpecificConfig, in aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 33.
|
||||||
|
// audioObjectType, value defines in 7.1 Profiles, aac-iso-13818-7.pdf, page 40.
|
||||||
|
u_int8_t aac_profile;
|
||||||
|
// samplingFrequencyIndex
|
||||||
|
u_int8_t aac_sample_rate;
|
||||||
|
// channelConfiguration
|
||||||
|
u_int8_t aac_channels;
|
||||||
// the avc extra data, the AVC sequence header,
|
// the avc extra data, the AVC sequence header,
|
||||||
// without the flv codec header,
|
// without the flv codec header,
|
||||||
// @see: ffmpeg, AVCodecContext::extradata
|
// @see: ffmpeg, AVCodecContext::extradata
|
||||||
|
|
|
@ -112,5 +112,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#define ERROR_HLS_BUSY 602
|
#define ERROR_HLS_BUSY 602
|
||||||
#define ERROR_HLS_OPEN_FAILED 603
|
#define ERROR_HLS_OPEN_FAILED 603
|
||||||
#define ERROR_HLS_WRITE_FAILED 604
|
#define ERROR_HLS_WRITE_FAILED 604
|
||||||
|
#define ERROR_HLS_AAC_FRAME_LENGTH 605
|
||||||
|
#define ERROR_HLS_AVC_SAMPLE_SIZE 606
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -26,6 +26,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <srs_core_error.hpp>
|
#include <srs_core_error.hpp>
|
||||||
#include <srs_core_codec.hpp>
|
#include <srs_core_codec.hpp>
|
||||||
|
@ -275,18 +277,249 @@ u_int8_t mpegts_header[] = {
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// @see: NGX_RTMP_HLS_DELAY, 700ms, ts_tbn=90000
|
||||||
|
#define SRS_HLS_DELAY 63000
|
||||||
|
|
||||||
|
// @see: ngx_rtmp_mpegts.c
|
||||||
|
// TODO: support full mpegts feature in future.
|
||||||
|
class SrsMpegtsWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static int write_header(int fd)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (::write(fd, mpegts_header, sizeof(mpegts_header)) != sizeof(mpegts_header)) {
|
||||||
|
ret = ERROR_HLS_WRITE_FAILED;
|
||||||
|
srs_error("write ts file header failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static int write_frame(int fd, mpegts_frame* frame, SrsCodecBuffer* buffer)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
char* last = buffer->bytes + buffer->size;
|
||||||
|
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_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_HLS_DELAY);
|
||||||
|
|
||||||
|
// dts; // 33bits
|
||||||
|
if (frame->dts != frame->pts) {
|
||||||
|
p = write_pts(p, 1, frame->dts + SRS_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 (::write(fd, packet, sizeof(packet)) != sizeof(packet)) {
|
||||||
|
ret = ERROR_HLS_WRITE_FAILED;
|
||||||
|
srs_error("write ts file failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write success, clear and free the buffer
|
||||||
|
buffer->free();
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
*p++ = (char) (pcr >> 25);
|
||||||
|
*p++ = (char) (pcr >> 17);
|
||||||
|
*p++ = (char) (pcr >> 9);
|
||||||
|
*p++ = (char) (pcr >> 1);
|
||||||
|
*p++ = (char) (pcr << 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// the mpegts header specifed the video/audio pid.
|
// the mpegts header specifed the video/audio pid.
|
||||||
#define TS_VIDEO_PID 256
|
#define TS_VIDEO_PID 256
|
||||||
#define TS_AUDIO_PID 257
|
#define TS_AUDIO_PID 257
|
||||||
|
|
||||||
|
// ts aac stream id.
|
||||||
|
#define TS_AUDIO_AAC 0xc0
|
||||||
|
// ts avc stream id.
|
||||||
|
#define TS_VIDEO_AVC 0xe0
|
||||||
|
|
||||||
SrsTSMuxer::SrsTSMuxer()
|
SrsTSMuxer::SrsTSMuxer()
|
||||||
{
|
{
|
||||||
fd = -1;
|
fd = -1;
|
||||||
|
|
||||||
|
audio_buffer = new SrsCodecBuffer();
|
||||||
|
video_buffer = new SrsCodecBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsTSMuxer::~SrsTSMuxer()
|
SrsTSMuxer::~SrsTSMuxer()
|
||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
|
|
||||||
|
audio_buffer->free();
|
||||||
|
video_buffer->free();
|
||||||
|
|
||||||
|
srs_freep(audio_buffer);
|
||||||
|
srs_freep(video_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsTSMuxer::open(std::string _path)
|
int SrsTSMuxer::open(std::string _path)
|
||||||
|
@ -306,9 +539,7 @@ int SrsTSMuxer::open(std::string _path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// write mpegts header
|
// write mpegts header
|
||||||
if (::write(fd, mpegts_header, sizeof(mpegts_header)) != sizeof(mpegts_header)) {
|
if ((ret = SrsMpegtsWriter::write_header(fd)) != ERROR_SUCCESS) {
|
||||||
ret = ERROR_HLS_WRITE_FAILED;
|
|
||||||
srs_error("write ts file header %s failed. ret=%d", path.c_str(), ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,7 +550,64 @@ int SrsTSMuxer::write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sam
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
static u_int8_t packet[188];
|
for (int i = 0; i < sample->nb_buffers; i++) {
|
||||||
|
SrsCodecBuffer* buf = &sample->buffers[i];
|
||||||
|
int32_t size = buf->size;
|
||||||
|
|
||||||
|
if (!buf->bytes || size <= 0 || size > 0x1fff) {
|
||||||
|
ret = ERROR_HLS_AAC_FRAME_LENGTH;
|
||||||
|
srs_error("invalid aac frame length=%d, ret=%d", size, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AAC-ADTS
|
||||||
|
// 6.2 Audio Data Transport Stream, ADTS
|
||||||
|
// in aac-iso-13818-7.pdf, page 26.
|
||||||
|
// fixed 7bytes header
|
||||||
|
static u_int8_t adts_header[7] = {0xff, 0xf1, 0x00, 0x00, 0x00, 0x0f, 0xfc};
|
||||||
|
/*
|
||||||
|
// adts_fixed_header
|
||||||
|
// 2B, 16bits
|
||||||
|
int16_t syncword; //12bits, '1111 1111 1111'
|
||||||
|
int8_t ID; //1bit, '0'
|
||||||
|
int8_t layer; //2bits, '00'
|
||||||
|
int8_t protection_absent; //1bit, can be '1'
|
||||||
|
// 12bits
|
||||||
|
int8_t profile; //2bit, 7.1 Profiles, page 40
|
||||||
|
TSAacSampleFrequency sampling_frequency_index; //4bits, Table 35, page 46
|
||||||
|
int8_t private_bit; //1bit, can be '0'
|
||||||
|
int8_t channel_configuration; //3bits, Table 8
|
||||||
|
int8_t original_or_copy; //1bit, can be '0'
|
||||||
|
int8_t home; //1bit, can be '0'
|
||||||
|
|
||||||
|
// adts_variable_header
|
||||||
|
// 28bits
|
||||||
|
int8_t copyright_identification_bit; //1bit, can be '0'
|
||||||
|
int8_t copyright_identification_start; //1bit, can be '0'
|
||||||
|
int16_t frame_length; //13bits
|
||||||
|
int16_t adts_buffer_fullness; //11bits, 7FF signals that the bitstream is a variable rate bitstream.
|
||||||
|
int8_t number_of_raw_data_blocks_in_frame; //2bits, 0 indicating 1 raw_data_block()
|
||||||
|
*/
|
||||||
|
// profile, 2bits
|
||||||
|
adts_header[2] = (codec->aac_profile << 6) & 0xc0;
|
||||||
|
// sampling_frequency_index 4bits
|
||||||
|
adts_header[2] |= (codec->aac_sample_rate << 2) & 0x3c;
|
||||||
|
// channel_configuration 3bits
|
||||||
|
adts_header[2] |= (codec->aac_channels >> 1) & 0x01;
|
||||||
|
adts_header[3] = (codec->aac_channels << 5) & 0xc0;
|
||||||
|
// frame_length 13bits
|
||||||
|
adts_header[3] |= (size >> 11) & 0x03;
|
||||||
|
adts_header[4] = (size >> 3) & 0xff;
|
||||||
|
adts_header[5] = (size << 5) & 0xcf;
|
||||||
|
|
||||||
|
// copy to audio buffer
|
||||||
|
audio_buffer->append(adts_header, sizeof(adts_header));
|
||||||
|
audio_buffer->append(buf->bytes, buf->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_frame.dts = audio_frame.pts = time * 90;
|
||||||
|
audio_frame.pid = TS_AUDIO_PID;
|
||||||
|
audio_frame.sid = TS_AUDIO_AAC;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -328,7 +616,45 @@ int SrsTSMuxer::write_video(u_int32_t time, SrsCodec* codec, SrsCodecSample* sam
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
static u_int8_t packet[188];
|
static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 };
|
||||||
|
video_buffer->append(aud_nal, sizeof(aud_nal));
|
||||||
|
|
||||||
|
for (int i = 0; i < sample->nb_buffers; i++) {
|
||||||
|
SrsCodecBuffer* buf = &sample->buffers[i];
|
||||||
|
int32_t size = buf->size;
|
||||||
|
|
||||||
|
if (!buf->bytes || size <= 0) {
|
||||||
|
ret = ERROR_HLS_AVC_SAMPLE_SIZE;
|
||||||
|
srs_error("invalid avc sample length=%d, ret=%d", size, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sample start prefix, '00 00 00 01' or '00 00 01'
|
||||||
|
u_int8_t* p = aud_nal + 1;
|
||||||
|
u_int8_t* end = p + 3;
|
||||||
|
|
||||||
|
// first AnnexB prefix is long (4 bytes)
|
||||||
|
if (i == 0) {
|
||||||
|
p = aud_nal;
|
||||||
|
}
|
||||||
|
video_buffer->append(p, end - p);
|
||||||
|
|
||||||
|
// sample data
|
||||||
|
video_buffer->append(buf->bytes, buf->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
video_frame.dts = time * 90;
|
||||||
|
video_frame.pts = video_frame.dts + sample->cts * 90;
|
||||||
|
video_frame.pid = TS_VIDEO_PID;
|
||||||
|
video_frame.sid = TS_VIDEO_AVC;
|
||||||
|
video_frame.key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame;
|
||||||
|
|
||||||
|
if ((ret = SrsMpegtsWriter::write_frame(fd, &video_frame, video_buffer)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if ((ret = SrsMpegtsWriter::write_frame(fd, &audio_frame, audio_buffer)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
class SrsOnMetaDataPacket;
|
class SrsOnMetaDataPacket;
|
||||||
class SrsCommonMessage;
|
class SrsCommonMessage;
|
||||||
class SrsCodecSample;
|
class SrsCodecSample;
|
||||||
|
class SrsCodecBuffer;
|
||||||
class SrsTSMuxer;
|
class SrsTSMuxer;
|
||||||
class SrsCodec;
|
class SrsCodec;
|
||||||
|
|
||||||
|
@ -56,11 +57,34 @@ public:
|
||||||
virtual int on_video(SrsCommonMessage* video);
|
virtual int on_video(SrsCommonMessage* video);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// @see: ngx_rtmp_mpegts_frame_t
|
||||||
|
struct mpegts_frame
|
||||||
|
{
|
||||||
|
int64_t pts;
|
||||||
|
int64_t dts;
|
||||||
|
int pid;
|
||||||
|
int sid;
|
||||||
|
int cc;
|
||||||
|
bool key;
|
||||||
|
|
||||||
|
mpegts_frame()
|
||||||
|
{
|
||||||
|
pts = dts = 0;
|
||||||
|
pid = sid = cc = 0;
|
||||||
|
key = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class SrsTSMuxer
|
class SrsTSMuxer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int fd;
|
int fd;
|
||||||
std::string path;
|
std::string path;
|
||||||
|
private:
|
||||||
|
mpegts_frame audio_frame;
|
||||||
|
SrsCodecBuffer* audio_buffer;
|
||||||
|
mpegts_frame video_frame;
|
||||||
|
SrsCodecBuffer* video_buffer;
|
||||||
public:
|
public:
|
||||||
SrsTSMuxer();
|
SrsTSMuxer();
|
||||||
virtual ~SrsTSMuxer();
|
virtual ~SrsTSMuxer();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue