mirror of
https://github.com/ossrs/srs.git
synced 2025-02-12 19:31:53 +00:00
fix #250, support push MPEGTS over UDP to SRS. 2.0.111
This commit is contained in:
parent
4246be92c9
commit
16afe7ddbb
7 changed files with 508 additions and 252 deletions
|
@ -486,7 +486,7 @@ Supported operating systems and hardware:
|
|||
).
|
||||
1. Support HLS(h.264+mp3) streaming, read
|
||||
[#301](https://github.com/winlinvip/simple-rtmp-server/issues/301).
|
||||
1. [dev] Support push MPEG-TS over UDP to SRS, read
|
||||
1. Support push MPEG-TS over UDP to SRS, read
|
||||
[#250](https://github.com/winlinvip/simple-rtmp-server/issues/250).
|
||||
1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech).
|
||||
1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92).
|
||||
|
@ -525,6 +525,7 @@ Supported operating systems and hardware:
|
|||
|
||||
### SRS 2.0 history
|
||||
|
||||
* v2.0, 2015-01-31, for [#250](https://github.com/winlinvip/simple-rtmp-server/issues/250), support push MPEGTS over UDP to SRS. 2.0.111
|
||||
* v2.0, 2015-01-29, build libfdk-aac in ffmpeg. 2.0.108
|
||||
* v2.0, 2015-01-25, for [#301](https://github.com/winlinvip/simple-rtmp-server/issues/301), hls support h.264+mp3, ok for vlc. 2.0.107
|
||||
* v2.0, 2015-01-25, for [#301](https://github.com/winlinvip/simple-rtmp-server/issues/301), http ts stream support h.264+mp3. 2.0.106
|
||||
|
|
|
@ -76,10 +76,20 @@ int SrsMpegtsQueue::push(SrsSharedPtrMessage* msg)
|
|||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
if (msgs.find(msg->timestamp) != msgs.end()) {
|
||||
srs_warn("mpegts: free the msg for dts exists, dts=%"PRId64, msg->timestamp);
|
||||
srs_freep(msg);
|
||||
return ret;
|
||||
// TODO: FIXME: use right way.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (msgs.find(msg->timestamp) == msgs.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// adjust the ts, add 1ms.
|
||||
msg->timestamp += 1;
|
||||
|
||||
if (i >= 5) {
|
||||
srs_warn("mpegts: free the msg for dts exists, dts=%"PRId64, msg->timestamp);
|
||||
srs_freep(msg);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->is_audio()) {
|
||||
|
@ -114,6 +124,8 @@ SrsSharedPtrMessage* SrsMpegtsQueue::dequeue()
|
|||
if (msg->is_video()) {
|
||||
nb_videos--;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -131,6 +143,7 @@ SrsMpegtsOverUdp::SrsMpegtsOverUdp(SrsConfDirective* c)
|
|||
stfd = NULL;
|
||||
stream_id = 0;
|
||||
avc = new SrsRawH264Stream();
|
||||
aac = new SrsRawAacStream();
|
||||
h264_sps_changed = false;
|
||||
h264_pps_changed = false;
|
||||
h264_sps_pps_sent = false;
|
||||
|
@ -145,6 +158,7 @@ SrsMpegtsOverUdp::~SrsMpegtsOverUdp()
|
|||
srs_freep(stream);
|
||||
srs_freep(context);
|
||||
srs_freep(avc);
|
||||
srs_freep(aac);
|
||||
srs_freep(queue);
|
||||
}
|
||||
|
||||
|
@ -309,6 +323,9 @@ int SrsMpegtsOverUdp::on_ts_message(SrsTsMessage* msg)
|
|||
if (msg->channel->stream == SrsTsStreamVideoH264) {
|
||||
return on_ts_video(msg, &avs);
|
||||
}
|
||||
if (msg->channel->stream == SrsTsStreamAudioAAC) {
|
||||
return on_ts_audio(msg, &avs);
|
||||
}
|
||||
|
||||
// TODO: FIXME: implements it.
|
||||
return ret;
|
||||
|
@ -326,6 +343,10 @@ int SrsMpegtsOverUdp::on_ts_video(SrsTsMessage* msg, SrsStream* avs)
|
|||
// ts tbn to flv tbn.
|
||||
u_int32_t dts = msg->dts / 90;
|
||||
u_int32_t pts = msg->dts / 90;
|
||||
|
||||
// the whole ts pes video packet must be a flv frame packet.
|
||||
char* ibpframe = avs->data() + avs->pos();
|
||||
int ibpframe_size = avs->size() - avs->pos();
|
||||
|
||||
// send each frame.
|
||||
while (!avs->empty()) {
|
||||
|
@ -342,59 +363,50 @@ int SrsMpegtsOverUdp::on_ts_video(SrsTsMessage* msg, SrsStream* avs)
|
|||
continue;
|
||||
}
|
||||
|
||||
// it may be return error, but we must process all packets.
|
||||
if ((ret = write_h264_raw_frame(frame, frame_size, dts, pts)) != ERROR_SUCCESS) {
|
||||
if (ret == ERROR_H264_DROP_BEFORE_SPS_PPS) {
|
||||
// for sps
|
||||
if (avc->is_sps(frame, frame_size)) {
|
||||
std::string sps;
|
||||
if ((ret = avc->sps_demux(frame, frame_size, sps)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (h264_sps == sps) {
|
||||
continue;
|
||||
}
|
||||
return ret;
|
||||
h264_sps_changed = true;
|
||||
h264_sps = sps;
|
||||
|
||||
if ((ret = write_h264_sps_pps(dts, pts)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// for pps
|
||||
if (avc->is_pps(frame, frame_size)) {
|
||||
std::string pps;
|
||||
if ((ret = avc->pps_demux(frame, frame_size, pps)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (h264_pps == pps) {
|
||||
continue;
|
||||
}
|
||||
h264_pps_changed = true;
|
||||
h264_pps = pps;
|
||||
|
||||
if ((ret = write_h264_sps_pps(dts, pts)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// for video, drop others with same pts/dts.
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsMpegtsOverUdp::write_h264_raw_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
// for sps
|
||||
if (avc->is_sps(frame, frame_size)) {
|
||||
std::string sps;
|
||||
if ((ret = avc->sps_demux(frame, frame_size, sps)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (h264_sps == sps) {
|
||||
return ret;
|
||||
}
|
||||
h264_sps_changed = true;
|
||||
h264_sps = sps;
|
||||
|
||||
return write_h264_sps_pps(dts, pts);
|
||||
}
|
||||
|
||||
// for pps
|
||||
if (avc->is_pps(frame, frame_size)) {
|
||||
std::string pps;
|
||||
if ((ret = avc->pps_demux(frame, frame_size, pps)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (h264_pps == pps) {
|
||||
return ret;
|
||||
}
|
||||
h264_pps_changed = true;
|
||||
h264_pps = pps;
|
||||
|
||||
return write_h264_sps_pps(dts, pts);
|
||||
}
|
||||
|
||||
// ibp frame.
|
||||
return write_h264_ipb_frame(frame, frame_size, dts, pts);
|
||||
srs_info("mpegts: demux avc ibp frame size=%d, dts=%d", ibpframe_size, dts);
|
||||
return write_h264_ipb_frame(ibpframe, ibpframe_size, dts, pts);
|
||||
}
|
||||
|
||||
int SrsMpegtsOverUdp::write_h264_sps_pps(u_int32_t dts, u_int32_t pts)
|
||||
|
@ -421,14 +433,18 @@ int SrsMpegtsOverUdp::write_h264_sps_pps(u_int32_t dts, u_int32_t pts)
|
|||
return ret;
|
||||
}
|
||||
|
||||
// the timestamp in rtmp message header is dts.
|
||||
u_int32_t timestamp = dts;
|
||||
if ((ret = rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// reset sps and pps.
|
||||
h264_sps_changed = false;
|
||||
h264_pps_changed = false;
|
||||
h264_sps_pps_sent = true;
|
||||
|
||||
// the timestamp in rtmp message header is dts.
|
||||
u_int32_t timestamp = dts;
|
||||
return rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsMpegtsOverUdp::write_h264_ipb_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts)
|
||||
|
@ -459,6 +475,72 @@ int SrsMpegtsOverUdp::write_h264_ipb_frame(char* frame, int frame_size, u_int32_
|
|||
return rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv);
|
||||
}
|
||||
|
||||
int SrsMpegtsOverUdp::on_ts_audio(SrsTsMessage* msg, SrsStream* avs)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
// ensure rtmp connected.
|
||||
if ((ret = connect()) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ts tbn to flv tbn.
|
||||
u_int32_t dts = msg->dts / 90;
|
||||
|
||||
// send each frame.
|
||||
while (!avs->empty()) {
|
||||
char* frame = NULL;
|
||||
int frame_size = 0;
|
||||
SrsRawAacStreamCodec codec;
|
||||
if ((ret = aac->adts_demux(avs, &frame, &frame_size, codec)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ignore invalid frame,
|
||||
// * atleast 1bytes for aac to decode the data.
|
||||
if (frame_size <= 0) {
|
||||
continue;
|
||||
}
|
||||
srs_info("mpegts: demux aac frame size=%d, dts=%d", frame_size, dts);
|
||||
|
||||
// generate sh.
|
||||
if (aac_specific_config.empty()) {
|
||||
std::string sh;
|
||||
if ((ret = aac->mux_sequence_header(&codec, sh)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
aac_specific_config = sh;
|
||||
|
||||
codec.aac_packet_type = 0;
|
||||
|
||||
if ((ret = write_audio_raw_frame((char*)sh.data(), (int)sh.length(), &codec, dts)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// audio raw data.
|
||||
codec.aac_packet_type = 1;
|
||||
if ((ret = write_audio_raw_frame(frame, frame_size, &codec, dts)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsMpegtsOverUdp::write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, u_int32_t dts)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
char* data = NULL;
|
||||
int size = 0;
|
||||
if ((ret = aac->mux_aac2flv(frame, frame_size, codec, dts, &data, &size)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return rtmp_write_packet(SrsCodecFlvTagAudio, dts, data, size);
|
||||
}
|
||||
|
||||
int SrsMpegtsOverUdp::rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
@ -482,6 +564,10 @@ int SrsMpegtsOverUdp::rtmp_write_packet(char type, u_int32_t timestamp, char* da
|
|||
if ((msg = queue->dequeue()) == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: FIXME: use pithy print.
|
||||
srs_info("mpegts: send msg %s dts=%"PRId64", size=%d",
|
||||
msg->is_audio()? "A":msg->is_video()? "V":"N", msg->timestamp, msg->size);
|
||||
|
||||
// send out encoded msg.
|
||||
if ((ret = client->send_and_free_message(msg, stream_id)) != ERROR_SUCCESS) {
|
||||
|
|
|
@ -45,6 +45,8 @@ class SrsStSocket;
|
|||
class SrsRequest;
|
||||
class SrsRawH264Stream;
|
||||
class SrsSharedPtrMessage;
|
||||
class SrsRawAacStream;
|
||||
class SrsRawAacStreamCodec;
|
||||
|
||||
#include <srs_app_st.hpp>
|
||||
#include <srs_kernel_ts.hpp>
|
||||
|
@ -114,6 +116,10 @@ private:
|
|||
std::string h264_pps;
|
||||
bool h264_pps_changed;
|
||||
bool h264_sps_pps_sent;
|
||||
private:
|
||||
SrsRawAacStream* aac;
|
||||
std::string aac_specific_config;
|
||||
private:
|
||||
SrsMpegtsQueue* queue;
|
||||
public:
|
||||
SrsMpegtsOverUdp(SrsConfDirective* c);
|
||||
|
@ -126,9 +132,11 @@ public:
|
|||
virtual int on_ts_message(SrsTsMessage* msg);
|
||||
private:
|
||||
virtual int on_ts_video(SrsTsMessage* msg, SrsStream* avs);
|
||||
virtual int write_h264_raw_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts);
|
||||
virtual int write_h264_sps_pps(u_int32_t dts, u_int32_t pts);
|
||||
virtual int write_h264_ipb_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts);
|
||||
virtual int on_ts_audio(SrsTsMessage* msg, SrsStream* avs);
|
||||
virtual int write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, u_int32_t dts);
|
||||
private:
|
||||
virtual int rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size);
|
||||
private:
|
||||
// connect to rtmp output url.
|
||||
|
|
|
@ -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 110
|
||||
#define VERSION_REVISION 111
|
||||
|
||||
// server info.
|
||||
#define RTMP_SIG_SRS_KEY "SRS"
|
||||
|
|
|
@ -81,9 +81,10 @@ struct Context
|
|||
SimpleSocketStream* skt;
|
||||
int stream_id;
|
||||
|
||||
// for h264 raw stream,
|
||||
// @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521
|
||||
// the remux raw codec.
|
||||
SrsRawH264Stream avc_raw;
|
||||
SrsRawAacStream aac_raw;
|
||||
|
||||
// for h264 raw stream,
|
||||
// @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521
|
||||
SrsStream h264_raw_stream;
|
||||
|
@ -1073,32 +1074,15 @@ srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size)
|
|||
* directly write a audio frame.
|
||||
*/
|
||||
int __srs_write_audio_raw_frame(Context* context,
|
||||
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
|
||||
char* frame, int frame_size, SrsRawAacStreamCodec* codec, u_int32_t timestamp
|
||||
) {
|
||||
|
||||
// for audio frame, there is 1 or 2 bytes header:
|
||||
// 1bytes, SoundFormat|SoundRate|SoundSize|SoundType
|
||||
// 1bytes, AACPacketType for SoundFormat == 10, 0 is sequence header.
|
||||
int size = frame_size + 1;
|
||||
if (sound_format == SrsCodecAudioAAC) {
|
||||
size += 1;
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
char* data = NULL;
|
||||
int size = 0;
|
||||
if ((ret = context->aac_raw.mux_aac2flv(frame, frame_size, codec, timestamp, &data, &size)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
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 (sound_format == SrsCodecAudioAAC) {
|
||||
*p++ = aac_packet_type;
|
||||
}
|
||||
|
||||
memcpy(p, frame, frame_size);
|
||||
|
||||
return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size);
|
||||
}
|
||||
|
@ -1106,73 +1090,28 @@ int __srs_write_audio_raw_frame(Context* context,
|
|||
/**
|
||||
* write aac frame in adts.
|
||||
*/
|
||||
int __srs_write_aac_adts_frame(Context* context,
|
||||
char sound_format, char sound_rate, char sound_size, char sound_type,
|
||||
char aac_profile, char aac_samplerate, char aac_channel,
|
||||
char* frame, int frame_size, u_int32_t timestamp
|
||||
int __srs_write_aac_adts_frame(Context* context,
|
||||
SrsRawAacStreamCodec* codec, char* frame, int frame_size, u_int32_t timestamp
|
||||
) {
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
// override the aac samplerate by user specified.
|
||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64146899
|
||||
switch (sound_rate) {
|
||||
case SrsCodecAudioSampleRate11025:
|
||||
aac_samplerate = 0x0a; break;
|
||||
case SrsCodecAudioSampleRate22050:
|
||||
aac_samplerate = 0x07; break;
|
||||
case SrsCodecAudioSampleRate44100:
|
||||
aac_samplerate = 0x04; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// send out aac sequence header if not sent.
|
||||
if (context->aac_specific_config.empty()) {
|
||||
char ch = 0;
|
||||
// @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf
|
||||
// AudioSpecificConfig (), page 33
|
||||
// 1.6.2.1 AudioSpecificConfig
|
||||
// audioObjectType; 5 bslbf
|
||||
ch = (aac_profile << 3) & 0xf8;
|
||||
// 3bits left.
|
||||
|
||||
// samplingFrequencyIndex; 4 bslbf
|
||||
ch |= (aac_samplerate >> 1) & 0x07;
|
||||
context->aac_specific_config += ch;
|
||||
ch = (aac_samplerate << 7) & 0x80;
|
||||
if (aac_samplerate == 0x0f) {
|
||||
return ERROR_AAC_DATA_INVALID;
|
||||
std::string sh;
|
||||
if ((ret = context->aac_raw.mux_sequence_header(codec, sh)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
// 7bits left.
|
||||
|
||||
// channelConfiguration; 4 bslbf
|
||||
ch |= (aac_channel << 3) & 0x78;
|
||||
// 3bits left.
|
||||
|
||||
// only support aac profile 1-4.
|
||||
if (aac_profile < 1 || aac_profile > 4) {
|
||||
return ERROR_AAC_DATA_INVALID;
|
||||
}
|
||||
// GASpecificConfig(), page 451
|
||||
// 4.4.1 Decoder configuration (GASpecificConfig)
|
||||
// frameLengthFlag; 1 bslbf
|
||||
// dependsOnCoreCoder; 1 bslbf
|
||||
// extensionFlag; 1 bslbf
|
||||
context->aac_specific_config += ch;
|
||||
|
||||
char* sh = (char*)context->aac_specific_config.data();
|
||||
int nb_sh = (int)context->aac_specific_config.length();
|
||||
if ((ret = __srs_write_audio_raw_frame(context,
|
||||
sound_format, sound_rate, sound_size, sound_type,
|
||||
0, sh, nb_sh, timestamp)) != ERROR_SUCCESS
|
||||
) {
|
||||
context->aac_specific_config = sh;
|
||||
|
||||
codec->aac_packet_type = 0;
|
||||
|
||||
if ((ret = __srs_write_audio_raw_frame(context, (char*)sh.data(), (int)sh.length(), codec, timestamp)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return __srs_write_audio_raw_frame(context,
|
||||
sound_format, sound_rate, sound_size, sound_type,
|
||||
1, frame, frame_size, timestamp);
|
||||
codec->aac_packet_type = 1;
|
||||
return __srs_write_audio_raw_frame(context, frame, frame_size, codec, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1180,126 +1119,32 @@ int __srs_write_aac_adts_frame(Context* context,
|
|||
*/
|
||||
int __srs_write_aac_adts_frames(Context* context,
|
||||
char sound_format, char sound_rate, char sound_size, char sound_type,
|
||||
char* frame, int frame_size, u_int32_t timestamp
|
||||
char* frames, int frames_size, u_int32_t timestamp
|
||||
) {
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
SrsStream* stream = &context->aac_raw_stream;
|
||||
if ((ret = stream->initialize(frame, frame_size)) != ERROR_SUCCESS) {
|
||||
if ((ret = stream->initialize(frames, frames_size)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (!stream->empty()) {
|
||||
int adts_header_start = stream->pos();
|
||||
|
||||
// decode the ADTS.
|
||||
// @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75,
|
||||
// 1.A.2.2 Audio_Data_Transport_Stream frame, ADTS
|
||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64145885
|
||||
// byte_alignment()
|
||||
|
||||
// adts_fixed_header:
|
||||
// 12bits syncword,
|
||||
// 16bits left.
|
||||
// adts_variable_header:
|
||||
// 28bits
|
||||
// 12+16+28=56bits
|
||||
// adts_error_check:
|
||||
// 16bits if protection_absent
|
||||
// 56+16=72bits
|
||||
// if protection_absent:
|
||||
// require(7bytes)=56bits
|
||||
// else
|
||||
// require(9bytes)=72bits
|
||||
if (!stream->require(7)) {
|
||||
return ERROR_AAC_ADTS_HEADER;
|
||||
}
|
||||
|
||||
// for aac, the frame must be ADTS format.
|
||||
if (!srs_aac_startswith_adts(stream)) {
|
||||
return ERROR_AAC_REQUIRED_ADTS;
|
||||
}
|
||||
|
||||
// Syncword 12 bslbf
|
||||
stream->read_1bytes();
|
||||
// 4bits left.
|
||||
// adts_fixed_header(), 1.A.2.2.1 Fixed Header of ADTS
|
||||
// ID 1 bslbf
|
||||
// Layer 2 uimsbf
|
||||
// protection_absent 1 bslbf
|
||||
int8_t fh0 = (stream->read_1bytes() & 0x0f);
|
||||
/*int8_t fh_id = (fh0 >> 3) & 0x01;*/
|
||||
/*int8_t fh_layer = (fh0 >> 1) & 0x03;*/
|
||||
int8_t fh_protection_absent = fh0 & 0x01;
|
||||
|
||||
int16_t fh1 = stream->read_2bytes();
|
||||
// Profile_ObjectType 2 uimsbf
|
||||
// sampling_frequency_index 4 uimsbf
|
||||
// private_bit 1 bslbf
|
||||
// channel_configuration 3 uimsbf
|
||||
// original/copy 1 bslbf
|
||||
// home 1 bslbf
|
||||
int8_t fh_Profile_ObjectType = (fh1 >> 14) & 0x03;
|
||||
int8_t fh_sampling_frequency_index = (fh1 >> 10) & 0x0f;
|
||||
/*int8_t fh_private_bit = (fh1 >> 9) & 0x01;*/
|
||||
int8_t fh_channel_configuration = (fh1 >> 6) & 0x07;
|
||||
/*int8_t fh_original = (fh1 >> 5) & 0x01;*/
|
||||
/*int8_t fh_home = (fh1 >> 4) & 0x01;*/
|
||||
// @remark, Emphasis is removed,
|
||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64154736
|
||||
//int8_t fh_Emphasis = (fh1 >> 2) & 0x03;
|
||||
// 4bits left.
|
||||
// adts_variable_header(), 1.A.2.2.2 Variable Header of ADTS
|
||||
// copyright_identification_bit 1 bslbf
|
||||
// copyright_identification_start 1 bslbf
|
||||
/*int8_t fh_copyright_identification_bit = (fh1 >> 3) & 0x01;*/
|
||||
/*int8_t fh_copyright_identification_start = (fh1 >> 2) & 0x01;*/
|
||||
// aac_frame_length 13 bslbf: Length of the frame including headers and error_check in bytes.
|
||||
// use the left 2bits as the 13 and 12 bit,
|
||||
// the aac_frame_length is 13bits, so we move 13-2=11.
|
||||
int16_t fh_aac_frame_length = (fh1 << 11) & 0x0800;
|
||||
|
||||
int32_t fh2 = stream->read_3bytes();
|
||||
// aac_frame_length 13 bslbf: consume the first 13-2=11bits
|
||||
// the fh2 is 24bits, so we move right 24-11=13.
|
||||
fh_aac_frame_length |= (fh2 >> 13) & 0x07ff;
|
||||
// adts_buffer_fullness 11 bslbf
|
||||
/*int16_t fh_adts_buffer_fullness = (fh2 >> 2) & 0x7ff;*/
|
||||
// no_raw_data_blocks_in_frame 2 uimsbf
|
||||
/*int16_t fh_no_raw_data_blocks_in_frame = fh2 & 0x03;*/
|
||||
// adts_error_check(), 1.A.2.2.3 Error detection
|
||||
if (!fh_protection_absent) {
|
||||
if (!stream->require(2)) {
|
||||
return ERROR_AAC_ADTS_HEADER;
|
||||
}
|
||||
// crc_check 16 Rpchof
|
||||
/*int16_t crc_check = */stream->read_2bytes();
|
||||
}
|
||||
|
||||
// TODO: check the fh_sampling_frequency_index
|
||||
// TODO: check the fh_channel_configuration
|
||||
|
||||
// raw_data_blocks
|
||||
int adts_header_size = stream->pos() - adts_header_start;
|
||||
int raw_data_size = fh_aac_frame_length - adts_header_size;
|
||||
if (!stream->require(raw_data_size)) {
|
||||
return ERROR_AAC_ADTS_HEADER;
|
||||
}
|
||||
|
||||
// the profile = object_id + 1
|
||||
// @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 78,
|
||||
// Table 1. A.9 – MPEG-2 Audio profiles and MPEG-4 Audio object types
|
||||
char aac_profile = fh_Profile_ObjectType + 1;
|
||||
|
||||
char* raw_data = stream->data() + stream->pos();
|
||||
if ((ret = __srs_write_aac_adts_frame(context,
|
||||
sound_format, sound_rate, sound_size, sound_type,
|
||||
aac_profile, fh_sampling_frequency_index, fh_channel_configuration,
|
||||
raw_data, raw_data_size, timestamp)) != ERROR_SUCCESS
|
||||
) {
|
||||
char* frame = NULL;
|
||||
int frame_size = 0;
|
||||
SrsRawAacStreamCodec codec;
|
||||
if ((ret = context->aac_raw.adts_demux(stream, &frame, &frame_size, codec)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// override by user specified.
|
||||
codec.sound_format = sound_format;
|
||||
codec.sound_rate = sound_rate;
|
||||
codec.sound_size = sound_size;
|
||||
codec.sound_type = sound_type;
|
||||
|
||||
if ((ret = __srs_write_aac_adts_frame(context, &codec, frame, frame_size, timestamp)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
stream->skip(raw_data_size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1328,10 +1173,16 @@ int srs_audio_write_raw_frame(srs_rtmp_t rtmp,
|
|||
sound_format, sound_rate, sound_size, sound_type,
|
||||
frame, frame_size, timestamp);
|
||||
} else {
|
||||
// use codec info for aac.
|
||||
SrsRawAacStreamCodec codec;
|
||||
codec.sound_format = sound_format;
|
||||
codec.sound_rate = sound_rate;
|
||||
codec.sound_size = sound_size;
|
||||
codec.sound_type = sound_type;
|
||||
codec.aac_packet_type = 0;
|
||||
|
||||
// for other data, directly write frame.
|
||||
return __srs_write_audio_raw_frame(context,
|
||||
sound_format, sound_rate, sound_size, sound_type,
|
||||
0, frame, frame_size, timestamp);
|
||||
return __srs_write_audio_raw_frame(context, frame, frame_size, &codec, timestamp);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
using namespace std;
|
||||
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_kernel_stream.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
|
@ -312,3 +313,254 @@ int SrsRawH264Stream::mux_avc2flv(string video, int8_t frame_type, int8_t avc_pa
|
|||
return ret;
|
||||
}
|
||||
|
||||
SrsRawAacStream::SrsRawAacStream()
|
||||
{
|
||||
}
|
||||
|
||||
SrsRawAacStream::~SrsRawAacStream()
|
||||
{
|
||||
}
|
||||
|
||||
int SrsRawAacStream::adts_demux(SrsStream* stream, char** pframe, int* pnb_frame, SrsRawAacStreamCodec& codec)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
while (!stream->empty()) {
|
||||
int adts_header_start = stream->pos();
|
||||
|
||||
// decode the ADTS.
|
||||
// @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75,
|
||||
// 1.A.2.2 Audio_Data_Transport_Stream frame, ADTS
|
||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64145885
|
||||
// byte_alignment()
|
||||
|
||||
// adts_fixed_header:
|
||||
// 12bits syncword,
|
||||
// 16bits left.
|
||||
// adts_variable_header:
|
||||
// 28bits
|
||||
// 12+16+28=56bits
|
||||
// adts_error_check:
|
||||
// 16bits if protection_absent
|
||||
// 56+16=72bits
|
||||
// if protection_absent:
|
||||
// require(7bytes)=56bits
|
||||
// else
|
||||
// require(9bytes)=72bits
|
||||
if (!stream->require(7)) {
|
||||
return ERROR_AAC_ADTS_HEADER;
|
||||
}
|
||||
|
||||
// for aac, the frame must be ADTS format.
|
||||
if (!srs_aac_startswith_adts(stream)) {
|
||||
return ERROR_AAC_REQUIRED_ADTS;
|
||||
}
|
||||
|
||||
// Syncword 12 bslbf
|
||||
stream->read_1bytes();
|
||||
// 4bits left.
|
||||
// adts_fixed_header(), 1.A.2.2.1 Fixed Header of ADTS
|
||||
// ID 1 bslbf
|
||||
// Layer 2 uimsbf
|
||||
// protection_absent 1 bslbf
|
||||
int8_t fh0 = (stream->read_1bytes() & 0x0f);
|
||||
/*int8_t fh_id = (fh0 >> 3) & 0x01;*/
|
||||
/*int8_t fh_layer = (fh0 >> 1) & 0x03;*/
|
||||
int8_t fh_protection_absent = fh0 & 0x01;
|
||||
|
||||
int16_t fh1 = stream->read_2bytes();
|
||||
// Profile_ObjectType 2 uimsbf
|
||||
// sampling_frequency_index 4 uimsbf
|
||||
// private_bit 1 bslbf
|
||||
// channel_configuration 3 uimsbf
|
||||
// original/copy 1 bslbf
|
||||
// home 1 bslbf
|
||||
int8_t fh_Profile_ObjectType = (fh1 >> 14) & 0x03;
|
||||
int8_t fh_sampling_frequency_index = (fh1 >> 10) & 0x0f;
|
||||
/*int8_t fh_private_bit = (fh1 >> 9) & 0x01;*/
|
||||
int8_t fh_channel_configuration = (fh1 >> 6) & 0x07;
|
||||
/*int8_t fh_original = (fh1 >> 5) & 0x01;*/
|
||||
/*int8_t fh_home = (fh1 >> 4) & 0x01;*/
|
||||
// @remark, Emphasis is removed,
|
||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64154736
|
||||
//int8_t fh_Emphasis = (fh1 >> 2) & 0x03;
|
||||
// 4bits left.
|
||||
// adts_variable_header(), 1.A.2.2.2 Variable Header of ADTS
|
||||
// copyright_identification_bit 1 bslbf
|
||||
// copyright_identification_start 1 bslbf
|
||||
/*int8_t fh_copyright_identification_bit = (fh1 >> 3) & 0x01;*/
|
||||
/*int8_t fh_copyright_identification_start = (fh1 >> 2) & 0x01;*/
|
||||
// aac_frame_length 13 bslbf: Length of the frame including headers and error_check in bytes.
|
||||
// use the left 2bits as the 13 and 12 bit,
|
||||
// the aac_frame_length is 13bits, so we move 13-2=11.
|
||||
int16_t fh_aac_frame_length = (fh1 << 11) & 0x0800;
|
||||
|
||||
int32_t fh2 = stream->read_3bytes();
|
||||
// aac_frame_length 13 bslbf: consume the first 13-2=11bits
|
||||
// the fh2 is 24bits, so we move right 24-11=13.
|
||||
fh_aac_frame_length |= (fh2 >> 13) & 0x07ff;
|
||||
// adts_buffer_fullness 11 bslbf
|
||||
/*int16_t fh_adts_buffer_fullness = (fh2 >> 2) & 0x7ff;*/
|
||||
// no_raw_data_blocks_in_frame 2 uimsbf
|
||||
/*int16_t fh_no_raw_data_blocks_in_frame = fh2 & 0x03;*/
|
||||
// adts_error_check(), 1.A.2.2.3 Error detection
|
||||
if (!fh_protection_absent) {
|
||||
if (!stream->require(2)) {
|
||||
return ERROR_AAC_ADTS_HEADER;
|
||||
}
|
||||
// crc_check 16 Rpchof
|
||||
/*int16_t crc_check = */stream->read_2bytes();
|
||||
}
|
||||
|
||||
// TODO: check the fh_sampling_frequency_index
|
||||
// TODO: check the fh_channel_configuration
|
||||
|
||||
// raw_data_blocks
|
||||
int adts_header_size = stream->pos() - adts_header_start;
|
||||
int raw_data_size = fh_aac_frame_length - adts_header_size;
|
||||
if (!stream->require(raw_data_size)) {
|
||||
return ERROR_AAC_ADTS_HEADER;
|
||||
}
|
||||
|
||||
// the profile = object_id + 1
|
||||
// @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 78,
|
||||
// Table 1. A.9 ¨C MPEG-2 Audio profiles and MPEG-4 Audio object types
|
||||
char aac_profile = fh_Profile_ObjectType + 1;
|
||||
|
||||
// the codec info.
|
||||
codec.protection_absent = fh_protection_absent;
|
||||
codec.Profile_ObjectType = fh_Profile_ObjectType;
|
||||
codec.sampling_frequency_index = fh_sampling_frequency_index;
|
||||
codec.channel_configuration = fh_channel_configuration;
|
||||
codec.aac_frame_length = fh_aac_frame_length;
|
||||
|
||||
codec.aac_profile = aac_profile;
|
||||
codec.aac_samplerate = fh_sampling_frequency_index;
|
||||
codec.aac_channel = fh_channel_configuration;
|
||||
|
||||
// @see srs_audio_write_raw_frame().
|
||||
codec.sound_format = 10; // AAC
|
||||
if (fh_sampling_frequency_index <= 0x0c && fh_sampling_frequency_index > 0x0a) {
|
||||
codec.sound_rate = SrsCodecAudioSampleRate5512;
|
||||
} else if (fh_sampling_frequency_index <= 0x0a && fh_sampling_frequency_index > 0x07) {
|
||||
codec.sound_rate = SrsCodecAudioSampleRate11025;
|
||||
} else if (fh_sampling_frequency_index <= 0x07 && fh_sampling_frequency_index > 0x04) {
|
||||
codec.sound_rate = SrsCodecAudioSampleRate22050;
|
||||
} else if (fh_sampling_frequency_index <= 0x04) {
|
||||
codec.sound_rate = SrsCodecAudioSampleRate44100;
|
||||
} else {
|
||||
codec.sound_rate = SrsCodecAudioSampleRate44100;
|
||||
srs_warn("adts invalid sample rate for flv, rate=%#x", fh_sampling_frequency_index);
|
||||
}
|
||||
codec.sound_size = srs_max(0, srs_min(1, fh_channel_configuration - 1));
|
||||
// TODO: FIXME: finger it out the sound size by adts.
|
||||
codec.sound_size = 1; // 0(8bits) or 1(16bits).
|
||||
|
||||
// frame data.
|
||||
*pframe = stream->data() + stream->pos();
|
||||
*pnb_frame = raw_data_size;
|
||||
stream->skip(raw_data_size);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsRawAacStream::mux_sequence_header(SrsRawAacStreamCodec* codec, string& sh)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
char aac_channel = codec->aac_channel;
|
||||
char aac_profile = codec->aac_profile;
|
||||
char aac_samplerate = codec->aac_samplerate;
|
||||
|
||||
// override the aac samplerate by user specified.
|
||||
// @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64146899
|
||||
switch (codec->sound_rate) {
|
||||
case SrsCodecAudioSampleRate11025:
|
||||
aac_samplerate = 0x0a; break;
|
||||
case SrsCodecAudioSampleRate22050:
|
||||
aac_samplerate = 0x07; break;
|
||||
case SrsCodecAudioSampleRate44100:
|
||||
aac_samplerate = 0x04; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sh = "";
|
||||
|
||||
char ch = 0;
|
||||
// @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf
|
||||
// AudioSpecificConfig (), page 33
|
||||
// 1.6.2.1 AudioSpecificConfig
|
||||
// audioObjectType; 5 bslbf
|
||||
ch = (aac_profile << 3) & 0xf8;
|
||||
// 3bits left.
|
||||
|
||||
// samplingFrequencyIndex; 4 bslbf
|
||||
ch |= (aac_samplerate >> 1) & 0x07;
|
||||
sh += ch;
|
||||
ch = (aac_samplerate << 7) & 0x80;
|
||||
if (aac_samplerate == 0x0f) {
|
||||
return ERROR_AAC_DATA_INVALID;
|
||||
}
|
||||
// 7bits left.
|
||||
|
||||
// channelConfiguration; 4 bslbf
|
||||
ch |= (aac_channel << 3) & 0x78;
|
||||
// 3bits left.
|
||||
|
||||
// only support aac profile 1-4.
|
||||
if (aac_profile < 1 || aac_profile > 4) {
|
||||
return ERROR_AAC_DATA_INVALID;
|
||||
}
|
||||
// GASpecificConfig(), page 451
|
||||
// 4.4.1 Decoder configuration (GASpecificConfig)
|
||||
// frameLengthFlag; 1 bslbf
|
||||
// dependsOnCoreCoder; 1 bslbf
|
||||
// extensionFlag; 1 bslbf
|
||||
sh += ch;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsRawAacStream::mux_aac2flv(char* frame, int nb_frame, SrsRawAacStreamCodec* codec, u_int32_t dts, char** flv, int* nb_flv)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
char sound_format = codec->sound_format;
|
||||
char sound_type = codec->sound_type;
|
||||
char sound_size = codec->sound_size;
|
||||
char sound_rate = codec->sound_rate;
|
||||
char aac_packet_type = codec->aac_packet_type;
|
||||
|
||||
// for audio frame, there is 1 or 2 bytes header:
|
||||
// 1bytes, SoundFormat|SoundRate|SoundSize|SoundType
|
||||
// 1bytes, AACPacketType for SoundFormat == 10, 0 is sequence header.
|
||||
int size = nb_frame + 1;
|
||||
if (sound_format == 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 (sound_format == SrsCodecAudioAAC) {
|
||||
*p++ = aac_packet_type;
|
||||
}
|
||||
|
||||
memcpy(p, frame, nb_frame);
|
||||
|
||||
*flv = data;
|
||||
*nb_flv = size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,4 +86,62 @@ public:
|
|||
virtual int mux_avc2flv(std::string video, int8_t frame_type, int8_t avc_packet_type, u_int32_t dts, u_int32_t pts, char** flv, int* nb_flv);
|
||||
};
|
||||
|
||||
/**
|
||||
* the header of adts sample.
|
||||
*/
|
||||
struct SrsRawAacStreamCodec
|
||||
{
|
||||
int8_t protection_absent;
|
||||
int8_t Profile_ObjectType;
|
||||
int8_t sampling_frequency_index;
|
||||
int8_t channel_configuration;
|
||||
int16_t aac_frame_length;
|
||||
|
||||
// calc by Profile_ObjectType+1
|
||||
char aac_profile;
|
||||
char aac_samplerate;
|
||||
char aac_channel;
|
||||
|
||||
char sound_format;
|
||||
char sound_rate;
|
||||
char sound_size;
|
||||
char sound_type;
|
||||
// 0 for sh; 1 for raw data.
|
||||
int8_t aac_packet_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* the raw aac stream, in adts.
|
||||
*/
|
||||
class SrsRawAacStream
|
||||
{
|
||||
public:
|
||||
SrsRawAacStream();
|
||||
virtual ~SrsRawAacStream();
|
||||
public:
|
||||
/**
|
||||
* demux the stream in adts format.
|
||||
* @param stream the input stream bytes.
|
||||
* @param pframe the output aac frame in stream. user should never free it.
|
||||
* @param pnb_frame the output aac frame size.
|
||||
* @param codec the output codec info.
|
||||
*/
|
||||
virtual int adts_demux(SrsStream* stream, char** pframe, int* pnb_frame, SrsRawAacStreamCodec& codec);
|
||||
/**
|
||||
* aac raw data to aac packet, without flv payload header.
|
||||
* mux the aac specific config to flv sequence header packet.
|
||||
* @param sh output the sequence header.
|
||||
*/
|
||||
virtual int mux_sequence_header(SrsRawAacStreamCodec* codec, std::string& sh);
|
||||
/**
|
||||
* mux the aac audio packet to flv audio packet.
|
||||
* @param frame the aac raw data.
|
||||
* @param nb_frame the count of aac frame.
|
||||
* @param codec the codec info of aac.
|
||||
* @param flv output the muxed flv packet.
|
||||
* @param nb_flv output the muxed flv size.
|
||||
*/
|
||||
virtual int mux_aac2flv(char* frame, int nb_frame, SrsRawAacStreamCodec* codec, u_int32_t dts, char** flv, int* nb_flv);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue