mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
adjust the api, move audio and h264 after rtmp
This commit is contained in:
parent
aa69f6197a
commit
2a9bec3d89
2 changed files with 549 additions and 549 deletions
|
@ -858,6 +858,397 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char*
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write audio raw frame to SRS.
|
||||||
|
*/
|
||||||
|
int srs_audio_write_raw_frame(srs_rtmp_t rtmp,
|
||||||
|
char sound_format, char sound_rate, char sound_size, char sound_type,
|
||||||
|
char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp
|
||||||
|
) {
|
||||||
|
Context* context = (Context*)rtmp;
|
||||||
|
srs_assert(context);
|
||||||
|
|
||||||
|
// TODO: FIXME: for aac, must send the sequence header first.
|
||||||
|
|
||||||
|
// for audio frame, there is 1 or 2 bytes header:
|
||||||
|
// 1bytes, SoundFormat|SoundRate|SoundSize|SoundType
|
||||||
|
// 1bytes, AACPacketType for SoundFormat == 10
|
||||||
|
int size = frame_size + 1;
|
||||||
|
if (aac_packet_type == SrsCodecAudioAAC) {
|
||||||
|
size += 1;
|
||||||
|
}
|
||||||
|
char* data = new char[size];
|
||||||
|
char* p = data;
|
||||||
|
|
||||||
|
u_int8_t audio_header = sound_type & 0x01;
|
||||||
|
audio_header |= (sound_size << 1) & 0x02;
|
||||||
|
audio_header |= (sound_rate << 2) & 0x0c;
|
||||||
|
audio_header |= (sound_format << 4) & 0xf0;
|
||||||
|
|
||||||
|
*p++ = audio_header;
|
||||||
|
|
||||||
|
if (aac_packet_type == SrsCodecAudioAAC) {
|
||||||
|
*p++ = aac_packet_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(p, frame, frame_size);
|
||||||
|
|
||||||
|
return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write h264 packet, with rtmp header.
|
||||||
|
* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame.
|
||||||
|
* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU.
|
||||||
|
* @param h264_raw_data the h.264 raw data, user must free it.
|
||||||
|
*/
|
||||||
|
int __srs_write_h264_packet(Context* context,
|
||||||
|
int8_t frame_type, int8_t avc_packet_type,
|
||||||
|
char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts
|
||||||
|
) {
|
||||||
|
// the timestamp in rtmp message header is dts.
|
||||||
|
u_int32_t timestamp = dts;
|
||||||
|
|
||||||
|
// for h264 in RTMP video payload, there is 5bytes header:
|
||||||
|
// 1bytes, FrameType | CodecID
|
||||||
|
// 1bytes, AVCPacketType
|
||||||
|
// 3bytes, CompositionTime, the cts.
|
||||||
|
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
|
||||||
|
int size = h264_raw_size + 5;
|
||||||
|
char* data = new char[size];
|
||||||
|
char* p = data;
|
||||||
|
|
||||||
|
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
|
||||||
|
// Frame Type, Type of video frame.
|
||||||
|
// CodecID, Codec Identifier.
|
||||||
|
// set the rtmp header
|
||||||
|
*p++ = (frame_type << 4) | SrsCodecVideoAVC;
|
||||||
|
|
||||||
|
// AVCPacketType
|
||||||
|
*p++ = avc_packet_type;
|
||||||
|
|
||||||
|
// CompositionTime
|
||||||
|
// pts = dts + cts, or
|
||||||
|
// cts = pts - dts.
|
||||||
|
// where cts is the header in rtmp video packet payload header.
|
||||||
|
u_int32_t cts = pts - dts;
|
||||||
|
char* pp = (char*)&cts;
|
||||||
|
*p++ = pp[2];
|
||||||
|
*p++ = pp[1];
|
||||||
|
*p++ = pp[0];
|
||||||
|
|
||||||
|
// h.264 raw data.
|
||||||
|
memcpy(p, h264_raw_data, h264_raw_size);
|
||||||
|
|
||||||
|
return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write the h264 sps/pps in context over RTMP.
|
||||||
|
*/
|
||||||
|
int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// only send when both sps and pps changed.
|
||||||
|
if (!context->h264_sps_changed || !context->h264_pps_changed) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5bytes sps/pps header:
|
||||||
|
// configurationVersion, AVCProfileIndication, profile_compatibility,
|
||||||
|
// AVCLevelIndication, lengthSizeMinusOne
|
||||||
|
// 3bytes size of sps:
|
||||||
|
// numOfSequenceParameterSets, sequenceParameterSetLength(2B)
|
||||||
|
// Nbytes of sps.
|
||||||
|
// sequenceParameterSetNALUnit
|
||||||
|
// 3bytes size of pps:
|
||||||
|
// numOfPictureParameterSets, pictureParameterSetLength
|
||||||
|
// Nbytes of pps:
|
||||||
|
// pictureParameterSetNALUnit
|
||||||
|
int nb_packet = 5
|
||||||
|
+ 3 + (int)context->h264_sps.length()
|
||||||
|
+ 3 + (int)context->h264_pps.length();
|
||||||
|
char* packet = new char[nb_packet];
|
||||||
|
SrsAutoFree(char, packet);
|
||||||
|
|
||||||
|
// use stream to generate the h264 packet.
|
||||||
|
SrsStream stream;
|
||||||
|
if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode the SPS:
|
||||||
|
// @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62
|
||||||
|
if (true) {
|
||||||
|
srs_assert((int)context->h264_sps.length() >= 4);
|
||||||
|
char* frame = (char*)context->h264_sps.data();
|
||||||
|
|
||||||
|
// @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205
|
||||||
|
// Baseline profile profile_idc is 66(0x42).
|
||||||
|
// Main profile profile_idc is 77(0x4d).
|
||||||
|
// Extended profile profile_idc is 88(0x58).
|
||||||
|
u_int8_t profile_idc = frame[1];
|
||||||
|
//u_int8_t constraint_set = frame[2];
|
||||||
|
u_int8_t level_idc = frame[3];
|
||||||
|
|
||||||
|
// generate the sps/pps header
|
||||||
|
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
|
||||||
|
// configurationVersion
|
||||||
|
stream.write_1bytes(0x01);
|
||||||
|
// AVCProfileIndication
|
||||||
|
stream.write_1bytes(profile_idc);
|
||||||
|
// profile_compatibility
|
||||||
|
stream.write_1bytes(0x00);
|
||||||
|
// AVCLevelIndication
|
||||||
|
stream.write_1bytes(level_idc);
|
||||||
|
// lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size,
|
||||||
|
// so we always set it to 0x03.
|
||||||
|
stream.write_1bytes(0x03);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sps
|
||||||
|
if (true) {
|
||||||
|
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
|
||||||
|
// numOfSequenceParameterSets, always 1
|
||||||
|
stream.write_1bytes(0x01);
|
||||||
|
// sequenceParameterSetLength
|
||||||
|
stream.write_2bytes(context->h264_sps.length());
|
||||||
|
// sequenceParameterSetNALUnit
|
||||||
|
stream.write_string(context->h264_sps);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pps
|
||||||
|
if (true) {
|
||||||
|
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
|
||||||
|
// numOfPictureParameterSets, always 1
|
||||||
|
stream.write_1bytes(0x01);
|
||||||
|
// pictureParameterSetLength
|
||||||
|
stream.write_2bytes(context->h264_pps.length());
|
||||||
|
// pictureParameterSetNALUnit
|
||||||
|
stream.write_string(context->h264_pps);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset sps and pps.
|
||||||
|
context->h264_sps_changed = false;
|
||||||
|
context->h264_pps_changed = false;
|
||||||
|
context->h264_sps_pps_sent = true;
|
||||||
|
|
||||||
|
// TODO: FIXME: for more profile.
|
||||||
|
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
|
||||||
|
// profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144
|
||||||
|
|
||||||
|
// send out h264 packet.
|
||||||
|
int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame;
|
||||||
|
int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader;
|
||||||
|
return __srs_write_h264_packet(
|
||||||
|
context, frame_type, avc_packet_type,
|
||||||
|
packet, nb_packet, dts, pts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write h264 IPB-frame.
|
||||||
|
*/
|
||||||
|
int __srs_write_h264_ipb_frame(Context* context,
|
||||||
|
char* data, int size, u_int32_t dts, u_int32_t pts
|
||||||
|
) {
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// when sps or pps not sent, ignore the packet.
|
||||||
|
// @see https://github.com/winlinvip/simple-rtmp-server/issues/203
|
||||||
|
if (!context->h264_sps_pps_sent) {
|
||||||
|
return ERROR_H264_DROP_BEFORE_SPS_PPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5bits, 7.3.1 NAL unit syntax,
|
||||||
|
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
|
||||||
|
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
|
||||||
|
u_int8_t nal_unit_type = (char)data[0] & 0x1f;
|
||||||
|
|
||||||
|
// 4bytes size of nalu:
|
||||||
|
// NALUnitLength
|
||||||
|
// Nbytes of nalu.
|
||||||
|
// NALUnit
|
||||||
|
int nb_packet = 4 + size;
|
||||||
|
char* packet = new char[nb_packet];
|
||||||
|
SrsAutoFree(char, packet);
|
||||||
|
|
||||||
|
// use stream to generate the h264 packet.
|
||||||
|
SrsStream stream;
|
||||||
|
if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
|
||||||
|
// lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size
|
||||||
|
u_int32_t NAL_unit_length = size;
|
||||||
|
|
||||||
|
// mux the avc NALU in "ISO Base Media File Format"
|
||||||
|
// from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
|
||||||
|
// NALUnitLength
|
||||||
|
stream.write_4bytes(NAL_unit_length);
|
||||||
|
// NALUnit
|
||||||
|
stream.write_bytes(data, size);
|
||||||
|
|
||||||
|
// send out h264 packet.
|
||||||
|
int8_t frame_type = SrsCodecVideoAVCFrameInterFrame;
|
||||||
|
if (nal_unit_type != 1) {
|
||||||
|
frame_type = SrsCodecVideoAVCFrameKeyFrame;
|
||||||
|
}
|
||||||
|
int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU;
|
||||||
|
return __srs_write_h264_packet(
|
||||||
|
context, frame_type, avc_packet_type,
|
||||||
|
packet, nb_packet, dts, pts
|
||||||
|
);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write h264 raw frame, maybe sps/pps/IPB-frame.
|
||||||
|
*/
|
||||||
|
int __srs_write_h264_raw_frame(Context* context,
|
||||||
|
char* frame, int frame_size, u_int32_t dts, u_int32_t pts
|
||||||
|
) {
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// ignore invalid frame,
|
||||||
|
// atleast 1bytes for SPS to decode the type
|
||||||
|
if (frame_size < 1) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5bits, 7.3.1 NAL unit syntax,
|
||||||
|
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
|
||||||
|
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
|
||||||
|
u_int8_t nal_unit_type = (char)frame[0] & 0x1f;
|
||||||
|
|
||||||
|
if (nal_unit_type == 7) {
|
||||||
|
// atleast 1bytes for SPS to decode the type, profile, constrain and level.
|
||||||
|
if (frame_size < 4) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sps;
|
||||||
|
sps.append(frame, frame_size);
|
||||||
|
|
||||||
|
if (context->h264_sps == sps) {
|
||||||
|
return ERROR_H264_DUPLICATED_SPS;
|
||||||
|
}
|
||||||
|
context->h264_sps_changed = true;
|
||||||
|
context->h264_sps = sps;
|
||||||
|
|
||||||
|
return __srs_write_h264_sps_pps(context, dts, pts);
|
||||||
|
} else if (nal_unit_type == 8) {
|
||||||
|
|
||||||
|
std::string pps;
|
||||||
|
pps.append(frame, frame_size);
|
||||||
|
|
||||||
|
if (context->h264_pps == pps) {
|
||||||
|
return ERROR_H264_DUPLICATED_PPS;
|
||||||
|
}
|
||||||
|
context->h264_pps_changed = true;
|
||||||
|
context->h264_pps = pps;
|
||||||
|
|
||||||
|
return __srs_write_h264_sps_pps(context, dts, pts);
|
||||||
|
} else {
|
||||||
|
return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write h264 multiple frames, in annexb format.
|
||||||
|
*/
|
||||||
|
int srs_h264_write_raw_frames(srs_rtmp_t rtmp,
|
||||||
|
char* frames, int frames_size, u_int32_t dts, u_int32_t pts
|
||||||
|
) {
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
srs_assert(frames != NULL);
|
||||||
|
srs_assert(frames_size > 0);
|
||||||
|
|
||||||
|
srs_assert(rtmp != NULL);
|
||||||
|
Context* context = (Context*)rtmp;
|
||||||
|
|
||||||
|
if ((ret = context->h264_raw_stream.initialize(frames, frames_size)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use the last error
|
||||||
|
// @see https://github.com/winlinvip/simple-rtmp-server/issues/203
|
||||||
|
// @see https://github.com/winlinvip/simple-rtmp-server/issues/204
|
||||||
|
int error_code_return = ret;
|
||||||
|
|
||||||
|
// send each frame.
|
||||||
|
while (!context->h264_raw_stream.empty()) {
|
||||||
|
// each frame must prefixed by annexb format.
|
||||||
|
// about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
|
||||||
|
int pnb_start_code = 0;
|
||||||
|
if (!srs_avc_startswith_annexb(&context->h264_raw_stream, &pnb_start_code)) {
|
||||||
|
return ERROR_H264_API_NO_PREFIXED;
|
||||||
|
}
|
||||||
|
int start = context->h264_raw_stream.pos() + pnb_start_code;
|
||||||
|
|
||||||
|
// find the last frame prefixed by annexb format.
|
||||||
|
context->h264_raw_stream.skip(pnb_start_code);
|
||||||
|
while (!context->h264_raw_stream.empty()) {
|
||||||
|
if (srs_avc_startswith_annexb(&context->h264_raw_stream, NULL)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
context->h264_raw_stream.skip(1);
|
||||||
|
}
|
||||||
|
int size = context->h264_raw_stream.pos() - start;
|
||||||
|
|
||||||
|
// send out the frame.
|
||||||
|
char* frame = context->h264_raw_stream.data() + start;
|
||||||
|
|
||||||
|
// it may be return error, but we must process all packets.
|
||||||
|
if ((ret = __srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) {
|
||||||
|
error_code_return = ret;
|
||||||
|
|
||||||
|
// ignore known error, process all packets.
|
||||||
|
if (srs_h264_is_dvbsp_error(ret)
|
||||||
|
|| srs_h264_is_duplicated_sps_error(ret)
|
||||||
|
|| srs_h264_is_duplicated_pps_error(ret)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error_code_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_h264_bool srs_h264_is_dvbsp_error(int error_code)
|
||||||
|
{
|
||||||
|
return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code)
|
||||||
|
{
|
||||||
|
return error_code == ERROR_H264_DUPLICATED_SPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code)
|
||||||
|
{
|
||||||
|
return error_code == ERROR_H264_DUPLICATED_PPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code)
|
||||||
|
{
|
||||||
|
SrsStream stream;
|
||||||
|
if (stream.initialize(h264_raw_data, h264_raw_size) != ERROR_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return srs_avc_startswith_annexb(&stream, pnb_start_code);
|
||||||
|
}
|
||||||
|
|
||||||
struct FlvContext
|
struct FlvContext
|
||||||
{
|
{
|
||||||
SrsFileReader reader;
|
SrsFileReader reader;
|
||||||
|
@ -1321,397 +1712,6 @@ void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value)
|
||||||
obj->append(any);
|
obj->append(any);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* write audio raw frame to SRS.
|
|
||||||
*/
|
|
||||||
int srs_audio_write_raw_frame(srs_rtmp_t rtmp,
|
|
||||||
char sound_format, char sound_rate, char sound_size, char sound_type,
|
|
||||||
char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp
|
|
||||||
) {
|
|
||||||
Context* context = (Context*)rtmp;
|
|
||||||
srs_assert(context);
|
|
||||||
|
|
||||||
// TODO: FIXME: for aac, must send the sequence header first.
|
|
||||||
|
|
||||||
// for audio frame, there is 1 or 2 bytes header:
|
|
||||||
// 1bytes, SoundFormat|SoundRate|SoundSize|SoundType
|
|
||||||
// 1bytes, AACPacketType for SoundFormat == 10
|
|
||||||
int size = frame_size + 1;
|
|
||||||
if (aac_packet_type == SrsCodecAudioAAC) {
|
|
||||||
size += 1;
|
|
||||||
}
|
|
||||||
char* data = new char[size];
|
|
||||||
char* p = data;
|
|
||||||
|
|
||||||
u_int8_t audio_header = sound_type & 0x01;
|
|
||||||
audio_header |= (sound_size << 1) & 0x02;
|
|
||||||
audio_header |= (sound_rate << 2) & 0x0c;
|
|
||||||
audio_header |= (sound_format << 4) & 0xf0;
|
|
||||||
|
|
||||||
*p++ = audio_header;
|
|
||||||
|
|
||||||
if (aac_packet_type == SrsCodecAudioAAC) {
|
|
||||||
*p++ = aac_packet_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(p, frame, frame_size);
|
|
||||||
|
|
||||||
return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* write h264 packet, with rtmp header.
|
|
||||||
* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame.
|
|
||||||
* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU.
|
|
||||||
* @param h264_raw_data the h.264 raw data, user must free it.
|
|
||||||
*/
|
|
||||||
int __srs_write_h264_packet(Context* context,
|
|
||||||
int8_t frame_type, int8_t avc_packet_type,
|
|
||||||
char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts
|
|
||||||
) {
|
|
||||||
// the timestamp in rtmp message header is dts.
|
|
||||||
u_int32_t timestamp = dts;
|
|
||||||
|
|
||||||
// for h264 in RTMP video payload, there is 5bytes header:
|
|
||||||
// 1bytes, FrameType | CodecID
|
|
||||||
// 1bytes, AVCPacketType
|
|
||||||
// 3bytes, CompositionTime, the cts.
|
|
||||||
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
|
|
||||||
int size = h264_raw_size + 5;
|
|
||||||
char* data = new char[size];
|
|
||||||
char* p = data;
|
|
||||||
|
|
||||||
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
|
|
||||||
// Frame Type, Type of video frame.
|
|
||||||
// CodecID, Codec Identifier.
|
|
||||||
// set the rtmp header
|
|
||||||
*p++ = (frame_type << 4) | SrsCodecVideoAVC;
|
|
||||||
|
|
||||||
// AVCPacketType
|
|
||||||
*p++ = avc_packet_type;
|
|
||||||
|
|
||||||
// CompositionTime
|
|
||||||
// pts = dts + cts, or
|
|
||||||
// cts = pts - dts.
|
|
||||||
// where cts is the header in rtmp video packet payload header.
|
|
||||||
u_int32_t cts = pts - dts;
|
|
||||||
char* pp = (char*)&cts;
|
|
||||||
*p++ = pp[2];
|
|
||||||
*p++ = pp[1];
|
|
||||||
*p++ = pp[0];
|
|
||||||
|
|
||||||
// h.264 raw data.
|
|
||||||
memcpy(p, h264_raw_data, h264_raw_size);
|
|
||||||
|
|
||||||
return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* write the h264 sps/pps in context over RTMP.
|
|
||||||
*/
|
|
||||||
int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts)
|
|
||||||
{
|
|
||||||
int ret = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
// only send when both sps and pps changed.
|
|
||||||
if (!context->h264_sps_changed || !context->h264_pps_changed) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5bytes sps/pps header:
|
|
||||||
// configurationVersion, AVCProfileIndication, profile_compatibility,
|
|
||||||
// AVCLevelIndication, lengthSizeMinusOne
|
|
||||||
// 3bytes size of sps:
|
|
||||||
// numOfSequenceParameterSets, sequenceParameterSetLength(2B)
|
|
||||||
// Nbytes of sps.
|
|
||||||
// sequenceParameterSetNALUnit
|
|
||||||
// 3bytes size of pps:
|
|
||||||
// numOfPictureParameterSets, pictureParameterSetLength
|
|
||||||
// Nbytes of pps:
|
|
||||||
// pictureParameterSetNALUnit
|
|
||||||
int nb_packet = 5
|
|
||||||
+ 3 + (int)context->h264_sps.length()
|
|
||||||
+ 3 + (int)context->h264_pps.length();
|
|
||||||
char* packet = new char[nb_packet];
|
|
||||||
SrsAutoFree(char, packet);
|
|
||||||
|
|
||||||
// use stream to generate the h264 packet.
|
|
||||||
SrsStream stream;
|
|
||||||
if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode the SPS:
|
|
||||||
// @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62
|
|
||||||
if (true) {
|
|
||||||
srs_assert((int)context->h264_sps.length() >= 4);
|
|
||||||
char* frame = (char*)context->h264_sps.data();
|
|
||||||
|
|
||||||
// @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205
|
|
||||||
// Baseline profile profile_idc is 66(0x42).
|
|
||||||
// Main profile profile_idc is 77(0x4d).
|
|
||||||
// Extended profile profile_idc is 88(0x58).
|
|
||||||
u_int8_t profile_idc = frame[1];
|
|
||||||
//u_int8_t constraint_set = frame[2];
|
|
||||||
u_int8_t level_idc = frame[3];
|
|
||||||
|
|
||||||
// generate the sps/pps header
|
|
||||||
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
|
|
||||||
// configurationVersion
|
|
||||||
stream.write_1bytes(0x01);
|
|
||||||
// AVCProfileIndication
|
|
||||||
stream.write_1bytes(profile_idc);
|
|
||||||
// profile_compatibility
|
|
||||||
stream.write_1bytes(0x00);
|
|
||||||
// AVCLevelIndication
|
|
||||||
stream.write_1bytes(level_idc);
|
|
||||||
// lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size,
|
|
||||||
// so we always set it to 0x03.
|
|
||||||
stream.write_1bytes(0x03);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sps
|
|
||||||
if (true) {
|
|
||||||
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
|
|
||||||
// numOfSequenceParameterSets, always 1
|
|
||||||
stream.write_1bytes(0x01);
|
|
||||||
// sequenceParameterSetLength
|
|
||||||
stream.write_2bytes(context->h264_sps.length());
|
|
||||||
// sequenceParameterSetNALUnit
|
|
||||||
stream.write_string(context->h264_sps);
|
|
||||||
}
|
|
||||||
|
|
||||||
// pps
|
|
||||||
if (true) {
|
|
||||||
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
|
|
||||||
// numOfPictureParameterSets, always 1
|
|
||||||
stream.write_1bytes(0x01);
|
|
||||||
// pictureParameterSetLength
|
|
||||||
stream.write_2bytes(context->h264_pps.length());
|
|
||||||
// pictureParameterSetNALUnit
|
|
||||||
stream.write_string(context->h264_pps);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset sps and pps.
|
|
||||||
context->h264_sps_changed = false;
|
|
||||||
context->h264_pps_changed = false;
|
|
||||||
context->h264_sps_pps_sent = true;
|
|
||||||
|
|
||||||
// TODO: FIXME: for more profile.
|
|
||||||
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
|
|
||||||
// profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144
|
|
||||||
|
|
||||||
// send out h264 packet.
|
|
||||||
int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame;
|
|
||||||
int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader;
|
|
||||||
return __srs_write_h264_packet(
|
|
||||||
context, frame_type, avc_packet_type,
|
|
||||||
packet, nb_packet, dts, pts
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* write h264 IPB-frame.
|
|
||||||
*/
|
|
||||||
int __srs_write_h264_ipb_frame(Context* context,
|
|
||||||
char* data, int size, u_int32_t dts, u_int32_t pts
|
|
||||||
) {
|
|
||||||
int ret = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
// when sps or pps not sent, ignore the packet.
|
|
||||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/203
|
|
||||||
if (!context->h264_sps_pps_sent) {
|
|
||||||
return ERROR_H264_DROP_BEFORE_SPS_PPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5bits, 7.3.1 NAL unit syntax,
|
|
||||||
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
|
|
||||||
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
|
|
||||||
u_int8_t nal_unit_type = (char)data[0] & 0x1f;
|
|
||||||
|
|
||||||
// 4bytes size of nalu:
|
|
||||||
// NALUnitLength
|
|
||||||
// Nbytes of nalu.
|
|
||||||
// NALUnit
|
|
||||||
int nb_packet = 4 + size;
|
|
||||||
char* packet = new char[nb_packet];
|
|
||||||
SrsAutoFree(char, packet);
|
|
||||||
|
|
||||||
// use stream to generate the h264 packet.
|
|
||||||
SrsStream stream;
|
|
||||||
if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
|
|
||||||
// lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size
|
|
||||||
u_int32_t NAL_unit_length = size;
|
|
||||||
|
|
||||||
// mux the avc NALU in "ISO Base Media File Format"
|
|
||||||
// from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
|
|
||||||
// NALUnitLength
|
|
||||||
stream.write_4bytes(NAL_unit_length);
|
|
||||||
// NALUnit
|
|
||||||
stream.write_bytes(data, size);
|
|
||||||
|
|
||||||
// send out h264 packet.
|
|
||||||
int8_t frame_type = SrsCodecVideoAVCFrameInterFrame;
|
|
||||||
if (nal_unit_type != 1) {
|
|
||||||
frame_type = SrsCodecVideoAVCFrameKeyFrame;
|
|
||||||
}
|
|
||||||
int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU;
|
|
||||||
return __srs_write_h264_packet(
|
|
||||||
context, frame_type, avc_packet_type,
|
|
||||||
packet, nb_packet, dts, pts
|
|
||||||
);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* write h264 raw frame, maybe sps/pps/IPB-frame.
|
|
||||||
*/
|
|
||||||
int __srs_write_h264_raw_frame(Context* context,
|
|
||||||
char* frame, int frame_size, u_int32_t dts, u_int32_t pts
|
|
||||||
) {
|
|
||||||
int ret = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
// ignore invalid frame,
|
|
||||||
// atleast 1bytes for SPS to decode the type
|
|
||||||
if (frame_size < 1) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5bits, 7.3.1 NAL unit syntax,
|
|
||||||
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
|
|
||||||
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
|
|
||||||
u_int8_t nal_unit_type = (char)frame[0] & 0x1f;
|
|
||||||
|
|
||||||
if (nal_unit_type == 7) {
|
|
||||||
// atleast 1bytes for SPS to decode the type, profile, constrain and level.
|
|
||||||
if (frame_size < 4) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string sps;
|
|
||||||
sps.append(frame, frame_size);
|
|
||||||
|
|
||||||
if (context->h264_sps == sps) {
|
|
||||||
return ERROR_H264_DUPLICATED_SPS;
|
|
||||||
}
|
|
||||||
context->h264_sps_changed = true;
|
|
||||||
context->h264_sps = sps;
|
|
||||||
|
|
||||||
return __srs_write_h264_sps_pps(context, dts, pts);
|
|
||||||
} else if (nal_unit_type == 8) {
|
|
||||||
|
|
||||||
std::string pps;
|
|
||||||
pps.append(frame, frame_size);
|
|
||||||
|
|
||||||
if (context->h264_pps == pps) {
|
|
||||||
return ERROR_H264_DUPLICATED_PPS;
|
|
||||||
}
|
|
||||||
context->h264_pps_changed = true;
|
|
||||||
context->h264_pps = pps;
|
|
||||||
|
|
||||||
return __srs_write_h264_sps_pps(context, dts, pts);
|
|
||||||
} else {
|
|
||||||
return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* write h264 multiple frames, in annexb format.
|
|
||||||
*/
|
|
||||||
int srs_h264_write_raw_frames(srs_rtmp_t rtmp,
|
|
||||||
char* frames, int frames_size, u_int32_t dts, u_int32_t pts
|
|
||||||
) {
|
|
||||||
int ret = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
srs_assert(frames != NULL);
|
|
||||||
srs_assert(frames_size > 0);
|
|
||||||
|
|
||||||
srs_assert(rtmp != NULL);
|
|
||||||
Context* context = (Context*)rtmp;
|
|
||||||
|
|
||||||
if ((ret = context->h264_raw_stream.initialize(frames, frames_size)) != ERROR_SUCCESS) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// use the last error
|
|
||||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/203
|
|
||||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/204
|
|
||||||
int error_code_return = ret;
|
|
||||||
|
|
||||||
// send each frame.
|
|
||||||
while (!context->h264_raw_stream.empty()) {
|
|
||||||
// each frame must prefixed by annexb format.
|
|
||||||
// about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
|
|
||||||
int pnb_start_code = 0;
|
|
||||||
if (!srs_avc_startswith_annexb(&context->h264_raw_stream, &pnb_start_code)) {
|
|
||||||
return ERROR_H264_API_NO_PREFIXED;
|
|
||||||
}
|
|
||||||
int start = context->h264_raw_stream.pos() + pnb_start_code;
|
|
||||||
|
|
||||||
// find the last frame prefixed by annexb format.
|
|
||||||
context->h264_raw_stream.skip(pnb_start_code);
|
|
||||||
while (!context->h264_raw_stream.empty()) {
|
|
||||||
if (srs_avc_startswith_annexb(&context->h264_raw_stream, NULL)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
context->h264_raw_stream.skip(1);
|
|
||||||
}
|
|
||||||
int size = context->h264_raw_stream.pos() - start;
|
|
||||||
|
|
||||||
// send out the frame.
|
|
||||||
char* frame = context->h264_raw_stream.data() + start;
|
|
||||||
|
|
||||||
// it may be return error, but we must process all packets.
|
|
||||||
if ((ret = __srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) {
|
|
||||||
error_code_return = ret;
|
|
||||||
|
|
||||||
// ignore known error, process all packets.
|
|
||||||
if (srs_h264_is_dvbsp_error(ret)
|
|
||||||
|| srs_h264_is_duplicated_sps_error(ret)
|
|
||||||
|| srs_h264_is_duplicated_pps_error(ret)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return error_code_return;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_h264_bool srs_h264_is_dvbsp_error(int error_code)
|
|
||||||
{
|
|
||||||
return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code)
|
|
||||||
{
|
|
||||||
return error_code == ERROR_H264_DUPLICATED_SPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code)
|
|
||||||
{
|
|
||||||
return error_code == ERROR_H264_DUPLICATED_PPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code)
|
|
||||||
{
|
|
||||||
SrsStream stream;
|
|
||||||
if (stream.initialize(h264_raw_data, h264_raw_size) != ERROR_SUCCESS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return srs_avc_startswith_annexb(&stream, pnb_start_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t srs_utils_get_time_ms()
|
int64_t srs_utils_get_time_ms()
|
||||||
{
|
{
|
||||||
srs_update_system_time_ms();
|
srs_update_system_time_ms();
|
||||||
|
|
|
@ -262,164 +262,6 @@ extern int srs_rtmp_write_packet(srs_rtmp_t rtmp,
|
||||||
char type, u_int32_t timestamp, char* data, int size
|
char type, u_int32_t timestamp, char* data, int size
|
||||||
);
|
);
|
||||||
|
|
||||||
/*************************************************************
|
|
||||||
**************************************************************
|
|
||||||
* flv codec
|
|
||||||
* @example /trunk/research/librtmp/srs_flv_injecter.c
|
|
||||||
* @example /trunk/research/librtmp/srs_flv_parser.c
|
|
||||||
* @example /trunk/research/librtmp/srs_ingest_flv.c
|
|
||||||
* @example /trunk/research/librtmp/srs_ingest_rtmp.c
|
|
||||||
**************************************************************
|
|
||||||
*************************************************************/
|
|
||||||
typedef void* srs_flv_t;
|
|
||||||
typedef int srs_flv_bool;
|
|
||||||
/* open flv file for both read/write. */
|
|
||||||
extern srs_flv_t srs_flv_open_read(const char* file);
|
|
||||||
extern srs_flv_t srs_flv_open_write(const char* file);
|
|
||||||
extern void srs_flv_close(srs_flv_t flv);
|
|
||||||
/**
|
|
||||||
* read the flv header. 9bytes header.
|
|
||||||
* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc.
|
|
||||||
* 3bytes, signature, "FLV",
|
|
||||||
* 1bytes, version, 0x01,
|
|
||||||
* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
|
|
||||||
* 4bytes, dataoffset, 0x09, The length of this header in bytes
|
|
||||||
*
|
|
||||||
* @return 0, success; otherswise, failed.
|
|
||||||
* @remark, drop the 4bytes zero previous tag size.
|
|
||||||
*/
|
|
||||||
extern int srs_flv_read_header(srs_flv_t flv, char header[9]);
|
|
||||||
/**
|
|
||||||
* read the flv tag header, 1bytes tag, 3bytes data_size,
|
|
||||||
* 4bytes time, 3bytes stream id.
|
|
||||||
* @param ptype, output the type of tag, macros:
|
|
||||||
* SRS_RTMP_TYPE_AUDIO, FlvTagAudio
|
|
||||||
* SRS_RTMP_TYPE_VIDEO, FlvTagVideo
|
|
||||||
* SRS_RTMP_TYPE_SCRIPT, FlvTagScript
|
|
||||||
* @param pdata_size, output the size of tag data.
|
|
||||||
* @param ptime, output the time of tag, the dts in ms.
|
|
||||||
*
|
|
||||||
* @return 0, success; otherswise, failed.
|
|
||||||
* @remark, user must ensure the next is a tag, srs never check it.
|
|
||||||
*/
|
|
||||||
extern int srs_flv_read_tag_header(srs_flv_t flv,
|
|
||||||
char* ptype, int32_t* pdata_size, u_int32_t* ptime
|
|
||||||
);
|
|
||||||
/**
|
|
||||||
* read the tag data. drop the 4bytes previous tag size
|
|
||||||
* @param data, the data to read, user alloc and free it.
|
|
||||||
* @param size, the size of data to read, get by srs_flv_read_tag_header().
|
|
||||||
* @remark, srs will ignore and drop the 4bytes previous tag size.
|
|
||||||
*/
|
|
||||||
extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size);
|
|
||||||
/**
|
|
||||||
* write the flv header. 9bytes header.
|
|
||||||
* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc.
|
|
||||||
* 3bytes, signature, "FLV",
|
|
||||||
* 1bytes, version, 0x01,
|
|
||||||
* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
|
|
||||||
* 4bytes, dataoffset, 0x09, The length of this header in bytes
|
|
||||||
*
|
|
||||||
* @return 0, success; otherswise, failed.
|
|
||||||
* @remark, auto write the 4bytes zero previous tag size.
|
|
||||||
*/
|
|
||||||
extern int srs_flv_write_header(srs_flv_t flv, char header[9]);
|
|
||||||
/**
|
|
||||||
* write the flv tag to file.
|
|
||||||
*
|
|
||||||
* @return 0, success; otherswise, failed.
|
|
||||||
* @remark, auto write the 4bytes zero previous tag size.
|
|
||||||
*/
|
|
||||||
/* write flv tag to file, auto write the 4bytes previous tag size */
|
|
||||||
extern int srs_flv_write_tag(srs_flv_t flv,
|
|
||||||
char type, int32_t time, char* data, int size
|
|
||||||
);
|
|
||||||
/**
|
|
||||||
* get the tag size, for flv injecter to adjust offset,
|
|
||||||
* size = tag_header(11B) + data_size + previous_tag(4B)
|
|
||||||
* @return the size of tag.
|
|
||||||
*/
|
|
||||||
extern int srs_flv_size_tag(int data_size);
|
|
||||||
/* file stream */
|
|
||||||
/* file stream tellg to get offset */
|
|
||||||
extern int64_t srs_flv_tellg(srs_flv_t flv);
|
|
||||||
/* seek file stream, offset is form the start of file */
|
|
||||||
extern void srs_flv_lseek(srs_flv_t flv, int64_t offset);
|
|
||||||
/* error code */
|
|
||||||
/* whether the error code indicates EOF */
|
|
||||||
extern srs_flv_bool srs_flv_is_eof(int error_code);
|
|
||||||
/* media codec */
|
|
||||||
/**
|
|
||||||
* whether the video body is sequence header
|
|
||||||
* @param data, the data of tag, read by srs_flv_read_tag_data().
|
|
||||||
* @param size, the size of tag, read by srs_flv_read_tag_data().
|
|
||||||
*/
|
|
||||||
extern srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size);
|
|
||||||
/**
|
|
||||||
* whether the video body is keyframe
|
|
||||||
* @param data, the data of tag, read by srs_flv_read_tag_data().
|
|
||||||
* @param size, the size of tag, read by srs_flv_read_tag_data().
|
|
||||||
*/
|
|
||||||
extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size);
|
|
||||||
|
|
||||||
/*************************************************************
|
|
||||||
**************************************************************
|
|
||||||
* amf0 codec
|
|
||||||
* @example /trunk/research/librtmp/srs_ingest_flv.c
|
|
||||||
* @example /trunk/research/librtmp/srs_ingest_rtmp.c
|
|
||||||
**************************************************************
|
|
||||||
*************************************************************/
|
|
||||||
/* the output handler. */
|
|
||||||
typedef void* srs_amf0_t;
|
|
||||||
typedef int srs_amf0_bool;
|
|
||||||
typedef double srs_amf0_number;
|
|
||||||
/**
|
|
||||||
* parse amf0 from data.
|
|
||||||
* @param nparsed, the parsed size, NULL to ignore.
|
|
||||||
* @return the parsed amf0 object. NULL for error.
|
|
||||||
*/
|
|
||||||
extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed);
|
|
||||||
extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value);
|
|
||||||
extern srs_amf0_t srs_amf0_create_ecma_array();
|
|
||||||
extern srs_amf0_t srs_amf0_create_strict_array();
|
|
||||||
extern srs_amf0_t srs_amf0_create_object();
|
|
||||||
extern void srs_amf0_free(srs_amf0_t amf0);
|
|
||||||
extern void srs_amf0_free_bytes(char* data);
|
|
||||||
/* size and to bytes */
|
|
||||||
extern int srs_amf0_size(srs_amf0_t amf0);
|
|
||||||
extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size);
|
|
||||||
/* type detecter */
|
|
||||||
extern srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0);
|
|
||||||
extern srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0);
|
|
||||||
extern srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0);
|
|
||||||
extern srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0);
|
|
||||||
extern srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0);
|
|
||||||
extern srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0);
|
|
||||||
extern srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0);
|
|
||||||
/* value converter */
|
|
||||||
extern const char* srs_amf0_to_string(srs_amf0_t amf0);
|
|
||||||
extern srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0);
|
|
||||||
extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0);
|
|
||||||
/* value setter */
|
|
||||||
extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value);
|
|
||||||
/* object value converter */
|
|
||||||
extern int srs_amf0_object_property_count(srs_amf0_t amf0);
|
|
||||||
extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index);
|
|
||||||
extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index);
|
|
||||||
extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name);
|
|
||||||
extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value);
|
|
||||||
extern void srs_amf0_object_clear(srs_amf0_t amf0);
|
|
||||||
/* ecma array value converter */
|
|
||||||
extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0);
|
|
||||||
extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index);
|
|
||||||
extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index);
|
|
||||||
extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name);
|
|
||||||
extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value);
|
|
||||||
/* strict array value converter */
|
|
||||||
extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0);
|
|
||||||
extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index);
|
|
||||||
extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value);
|
|
||||||
|
|
||||||
/*************************************************************
|
/*************************************************************
|
||||||
**************************************************************
|
**************************************************************
|
||||||
* audio raw codec
|
* audio raw codec
|
||||||
|
@ -583,6 +425,164 @@ extern int srs_h264_startswith_annexb(
|
||||||
int* pnb_start_code
|
int* pnb_start_code
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*************************************************************
|
||||||
|
**************************************************************
|
||||||
|
* flv codec
|
||||||
|
* @example /trunk/research/librtmp/srs_flv_injecter.c
|
||||||
|
* @example /trunk/research/librtmp/srs_flv_parser.c
|
||||||
|
* @example /trunk/research/librtmp/srs_ingest_flv.c
|
||||||
|
* @example /trunk/research/librtmp/srs_ingest_rtmp.c
|
||||||
|
**************************************************************
|
||||||
|
*************************************************************/
|
||||||
|
typedef void* srs_flv_t;
|
||||||
|
typedef int srs_flv_bool;
|
||||||
|
/* open flv file for both read/write. */
|
||||||
|
extern srs_flv_t srs_flv_open_read(const char* file);
|
||||||
|
extern srs_flv_t srs_flv_open_write(const char* file);
|
||||||
|
extern void srs_flv_close(srs_flv_t flv);
|
||||||
|
/**
|
||||||
|
* read the flv header. 9bytes header.
|
||||||
|
* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc.
|
||||||
|
* 3bytes, signature, "FLV",
|
||||||
|
* 1bytes, version, 0x01,
|
||||||
|
* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
|
||||||
|
* 4bytes, dataoffset, 0x09, The length of this header in bytes
|
||||||
|
*
|
||||||
|
* @return 0, success; otherswise, failed.
|
||||||
|
* @remark, drop the 4bytes zero previous tag size.
|
||||||
|
*/
|
||||||
|
extern int srs_flv_read_header(srs_flv_t flv, char header[9]);
|
||||||
|
/**
|
||||||
|
* read the flv tag header, 1bytes tag, 3bytes data_size,
|
||||||
|
* 4bytes time, 3bytes stream id.
|
||||||
|
* @param ptype, output the type of tag, macros:
|
||||||
|
* SRS_RTMP_TYPE_AUDIO, FlvTagAudio
|
||||||
|
* SRS_RTMP_TYPE_VIDEO, FlvTagVideo
|
||||||
|
* SRS_RTMP_TYPE_SCRIPT, FlvTagScript
|
||||||
|
* @param pdata_size, output the size of tag data.
|
||||||
|
* @param ptime, output the time of tag, the dts in ms.
|
||||||
|
*
|
||||||
|
* @return 0, success; otherswise, failed.
|
||||||
|
* @remark, user must ensure the next is a tag, srs never check it.
|
||||||
|
*/
|
||||||
|
extern int srs_flv_read_tag_header(srs_flv_t flv,
|
||||||
|
char* ptype, int32_t* pdata_size, u_int32_t* ptime
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* read the tag data. drop the 4bytes previous tag size
|
||||||
|
* @param data, the data to read, user alloc and free it.
|
||||||
|
* @param size, the size of data to read, get by srs_flv_read_tag_header().
|
||||||
|
* @remark, srs will ignore and drop the 4bytes previous tag size.
|
||||||
|
*/
|
||||||
|
extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size);
|
||||||
|
/**
|
||||||
|
* write the flv header. 9bytes header.
|
||||||
|
* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc.
|
||||||
|
* 3bytes, signature, "FLV",
|
||||||
|
* 1bytes, version, 0x01,
|
||||||
|
* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
|
||||||
|
* 4bytes, dataoffset, 0x09, The length of this header in bytes
|
||||||
|
*
|
||||||
|
* @return 0, success; otherswise, failed.
|
||||||
|
* @remark, auto write the 4bytes zero previous tag size.
|
||||||
|
*/
|
||||||
|
extern int srs_flv_write_header(srs_flv_t flv, char header[9]);
|
||||||
|
/**
|
||||||
|
* write the flv tag to file.
|
||||||
|
*
|
||||||
|
* @return 0, success; otherswise, failed.
|
||||||
|
* @remark, auto write the 4bytes zero previous tag size.
|
||||||
|
*/
|
||||||
|
/* write flv tag to file, auto write the 4bytes previous tag size */
|
||||||
|
extern int srs_flv_write_tag(srs_flv_t flv,
|
||||||
|
char type, int32_t time, char* data, int size
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* get the tag size, for flv injecter to adjust offset,
|
||||||
|
* size = tag_header(11B) + data_size + previous_tag(4B)
|
||||||
|
* @return the size of tag.
|
||||||
|
*/
|
||||||
|
extern int srs_flv_size_tag(int data_size);
|
||||||
|
/* file stream */
|
||||||
|
/* file stream tellg to get offset */
|
||||||
|
extern int64_t srs_flv_tellg(srs_flv_t flv);
|
||||||
|
/* seek file stream, offset is form the start of file */
|
||||||
|
extern void srs_flv_lseek(srs_flv_t flv, int64_t offset);
|
||||||
|
/* error code */
|
||||||
|
/* whether the error code indicates EOF */
|
||||||
|
extern srs_flv_bool srs_flv_is_eof(int error_code);
|
||||||
|
/* media codec */
|
||||||
|
/**
|
||||||
|
* whether the video body is sequence header
|
||||||
|
* @param data, the data of tag, read by srs_flv_read_tag_data().
|
||||||
|
* @param size, the size of tag, read by srs_flv_read_tag_data().
|
||||||
|
*/
|
||||||
|
extern srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size);
|
||||||
|
/**
|
||||||
|
* whether the video body is keyframe
|
||||||
|
* @param data, the data of tag, read by srs_flv_read_tag_data().
|
||||||
|
* @param size, the size of tag, read by srs_flv_read_tag_data().
|
||||||
|
*/
|
||||||
|
extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size);
|
||||||
|
|
||||||
|
/*************************************************************
|
||||||
|
**************************************************************
|
||||||
|
* amf0 codec
|
||||||
|
* @example /trunk/research/librtmp/srs_ingest_flv.c
|
||||||
|
* @example /trunk/research/librtmp/srs_ingest_rtmp.c
|
||||||
|
**************************************************************
|
||||||
|
*************************************************************/
|
||||||
|
/* the output handler. */
|
||||||
|
typedef void* srs_amf0_t;
|
||||||
|
typedef int srs_amf0_bool;
|
||||||
|
typedef double srs_amf0_number;
|
||||||
|
/**
|
||||||
|
* parse amf0 from data.
|
||||||
|
* @param nparsed, the parsed size, NULL to ignore.
|
||||||
|
* @return the parsed amf0 object. NULL for error.
|
||||||
|
*/
|
||||||
|
extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed);
|
||||||
|
extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value);
|
||||||
|
extern srs_amf0_t srs_amf0_create_ecma_array();
|
||||||
|
extern srs_amf0_t srs_amf0_create_strict_array();
|
||||||
|
extern srs_amf0_t srs_amf0_create_object();
|
||||||
|
extern void srs_amf0_free(srs_amf0_t amf0);
|
||||||
|
extern void srs_amf0_free_bytes(char* data);
|
||||||
|
/* size and to bytes */
|
||||||
|
extern int srs_amf0_size(srs_amf0_t amf0);
|
||||||
|
extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size);
|
||||||
|
/* type detecter */
|
||||||
|
extern srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0);
|
||||||
|
extern srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0);
|
||||||
|
extern srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0);
|
||||||
|
extern srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0);
|
||||||
|
extern srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0);
|
||||||
|
extern srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0);
|
||||||
|
extern srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0);
|
||||||
|
/* value converter */
|
||||||
|
extern const char* srs_amf0_to_string(srs_amf0_t amf0);
|
||||||
|
extern srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0);
|
||||||
|
extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0);
|
||||||
|
/* value setter */
|
||||||
|
extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value);
|
||||||
|
/* object value converter */
|
||||||
|
extern int srs_amf0_object_property_count(srs_amf0_t amf0);
|
||||||
|
extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index);
|
||||||
|
extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index);
|
||||||
|
extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name);
|
||||||
|
extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value);
|
||||||
|
extern void srs_amf0_object_clear(srs_amf0_t amf0);
|
||||||
|
/* ecma array value converter */
|
||||||
|
extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0);
|
||||||
|
extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index);
|
||||||
|
extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index);
|
||||||
|
extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name);
|
||||||
|
extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value);
|
||||||
|
/* strict array value converter */
|
||||||
|
extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0);
|
||||||
|
extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index);
|
||||||
|
extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value);
|
||||||
|
|
||||||
/*************************************************************
|
/*************************************************************
|
||||||
**************************************************************
|
**************************************************************
|
||||||
* utilities
|
* utilities
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue