diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index 9cdb0d83..6169e0f5 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -1,15 +1,24 @@ include_directories(include source) file(GLOB api_src_list include/*.h source/*.cpp source/*.h source/*.c) + +set(ENABLE_API_STATIC false) + if (IOS) add_library(mk_api STATIC ${api_src_list}) target_link_libraries(mk_api ${LINK_LIB_LIST}) else () - add_library(mk_api SHARED ${api_src_list}) - if (WIN32) - add_definitions(-DMediaKitApi_EXPORTS) - endif () - + if(ENABLE_API_STATIC) + add_library(mk_api STATIC ${api_src_list}) + if (WIN32) + add_definitions(-DMediaKitApi_STATIC) + endif () + else () + add_library(mk_api SHARED ${api_src_list}) + if (WIN32) + add_definitions(-DMediaKitApi_EXPORTS) + endif () + endif() target_link_libraries(mk_api ${LINK_LIB_LIST}) add_subdirectory(tests) diff --git a/api/include/mk_common.h b/api/include/mk_common.h index 623b8d98..119f3da9 100755 --- a/api/include/mk_common.h +++ b/api/include/mk_common.h @@ -14,13 +14,20 @@ #include #if defined(_WIN32) -#if defined(MediaKitApi_EXPORTS) - #define API_EXPORT __declspec(dllexport) - #else - #define API_EXPORT __declspec(dllimport) - #endif - #define API_CALL __cdecl +#ifndef MediaKitApi_STATIC +#if defined(MediaKitApi_EXPORTS) + #define API_EXPORT __declspec(dllexport) + #else + #define API_EXPORT __declspec(dllimport) + #endif + + #define API_CALL __cdecl +#else +#define API_EXPORT +#define API_CALL +#endif + #else #define API_EXPORT #define API_CALL diff --git a/api/include/mk_media.h b/api/include/mk_media.h index 02332892..3cbee8a8 100755 --- a/api/include/mk_media.h +++ b/api/include/mk_media.h @@ -65,6 +65,16 @@ API_EXPORT void API_CALL mk_media_init_h265(mk_media ctx, int width, int height, */ API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample_bit, int sample_rate, int profile); +/** + * 添加g711音频轨道 + * @param ctx 对象指针 + * @param au 1.G711A 2.G711U + * @param channel 通道数 + * @param sample_bit 采样位数,只支持16 + * @param sample_rate 采样率 + */ +API_EXPORT void API_CALL mk_media_init_g711(mk_media ctx, int au, int sample_bit, int sample_rate); + /** * 初始化h264/h265/aac完毕后调用此函数, * 在单track(只有音频或视频)时,因为ZLMediaKit不知道后续是否还要添加track,所以会多等待3秒钟 @@ -113,6 +123,15 @@ API_EXPORT void API_CALL mk_media_input_aac(mk_media ctx, void *data, int len, u */ API_EXPORT void API_CALL mk_media_input_aac1(mk_media ctx, void *data, int len, uint32_t dts, void *adts); +/** + * 输入单帧G711音频 + * @param ctx 对象指针 + * @param data 单帧G711数据 + * @param len 单帧G711数据字节数 + * @param dts 时间戳,毫秒 + */ +API_EXPORT void API_CALL mk_media_input_g711(mk_media ctx, void* data, int len, uint32_t dts); + /** * MediaSource.close()回调事件 * 在选择关闭一个关联的MediaSource时,将会最终触发到该回调 diff --git a/api/include/mk_player.h b/api/include/mk_player.h index 16eae126..f228c845 100755 --- a/api/include/mk_player.h +++ b/api/include/mk_player.h @@ -105,6 +105,12 @@ API_EXPORT void API_CALL mk_player_set_on_shutdown(mk_player ctx, on_mk_play_eve */ API_EXPORT void API_CALL mk_player_set_on_data(mk_player ctx, on_mk_play_data cb, void *user_data); +/** + * 获取视频codec_id -1:不存在 0:H264,1:H265,2:AAC 3.G711A 4.G711U + * @param ctx 播放器指针 + */ +API_EXPORT int API_CALL mk_player_video_codecId(mk_player ctx); + /** * 获取视频宽度 */ @@ -120,6 +126,12 @@ API_EXPORT int API_CALL mk_player_video_height(mk_player ctx); */ API_EXPORT int API_CALL mk_player_video_fps(mk_player ctx); +/** + * 获取音频codec_id -1:不存在 0:H264,1:H265,2:AAC 3.G711A 4.G711U + * @param ctx 播放器指针 + */ +API_EXPORT int API_CALL mk_player_audio_codecId(mk_player ctx); + /** * 获取音频采样率 */ diff --git a/api/source/mk_media.cpp b/api/source/mk_media.cpp index e6bb8481..3b345164 100755 --- a/api/source/mk_media.cpp +++ b/api/source/mk_media.cpp @@ -140,6 +140,20 @@ API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample (*obj)->getChannel()->initAudio(info); } + +API_EXPORT void API_CALL mk_media_init_g711(mk_media ctx, int au, int sample_bit, int sample_rate) +{ + assert(ctx); + MediaHelper::Ptr* obj = (MediaHelper::Ptr*) ctx; + AudioInfo info; + info.iSampleRate = sample_rate; + info.iChannel = 1; + info.iSampleBit = sample_bit; + info.iProfile = 0; + info.codecId = (CodecId)au; + (*obj)->getChannel()->initAudio(info); +} + API_EXPORT void API_CALL mk_media_init_complete(mk_media ctx){ assert(ctx); MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; @@ -170,6 +184,12 @@ API_EXPORT void API_CALL mk_media_input_aac1(mk_media ctx, void *data, int len, (*obj)->getChannel()->inputAAC((char *) data, len, dts, (char *) adts); } +API_EXPORT void API_CALL mk_media_input_g711(mk_media ctx, void* data, int len, uint32_t dts) +{ + assert(ctx && data && len > 0); + MediaHelper::Ptr* obj = (MediaHelper::Ptr*) ctx; + (*obj)->getChannel()->inputG711((char*)data, len, dts); +} diff --git a/api/source/mk_player.cpp b/api/source/mk_player.cpp index d9ba3fab..5f7a681b 100755 --- a/api/source/mk_player.cpp +++ b/api/source/mk_player.cpp @@ -101,6 +101,15 @@ API_EXPORT void API_CALL mk_player_set_on_data(mk_player ctx, on_mk_play_data cb }); } + +API_EXPORT int API_CALL mk_player_video_codecId(mk_player ctx) +{ + assert(ctx); + MediaPlayer::Ptr& player = *((MediaPlayer::Ptr*)ctx); + auto track = dynamic_pointer_cast(player->getTrack(TrackVideo)); + return track ? track->getCodecId() : CodecInvalid; +} + API_EXPORT int API_CALL mk_player_video_width(mk_player ctx) { assert(ctx); MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); @@ -122,6 +131,15 @@ API_EXPORT int API_CALL mk_player_video_fps(mk_player ctx) { return track ? track->getVideoFps() : 0; } + +API_EXPORT int API_CALL mk_player_audio_codecId(mk_player ctx) +{ + assert(ctx); + MediaPlayer::Ptr& player = *((MediaPlayer::Ptr*)ctx); + auto track = dynamic_pointer_cast(player->getTrack(TrackAudio)); + return track ? track->getCodecId() : CodecInvalid; +} + API_EXPORT int API_CALL mk_player_audio_samplerate(mk_player ctx) { assert(ctx); MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); diff --git a/src/Common/Device.cpp b/src/Common/Device.cpp index 310db429..668d06ee 100644 --- a/src/Common/Device.cpp +++ b/src/Common/Device.cpp @@ -16,6 +16,7 @@ #include "Util/base64.h" #include "Util/TimeTicker.h" #include "Extension/AAC.h" +#include "Extension/G711.h" #include "Extension/H264.h" #include "Extension/H265.h" @@ -148,6 +149,14 @@ void DevChannel::inputAAC(const char *pcDataWithoutAdts,int iDataLen, uint32_t u } +void DevChannel::inputG711(const char* pcData, int iDataLen, uint32_t uiStamp) +{ + if (uiStamp == 0) { + uiStamp = (uint32_t)_aTicker[1].elapsedTime(); + } + inputFrame(std::make_shared(_audio->codecId, (char*)pcData, iDataLen, uiStamp, 0)); +} + void DevChannel::initVideo(const VideoInfo& info) { _video = std::make_shared(info); addTrack(std::make_shared()); @@ -159,32 +168,39 @@ void DevChannel::initH265Video(const VideoInfo &info){ } void DevChannel::initAudio(const AudioInfo& info) { - _audio = std::make_shared(info); - addTrack(std::make_shared()); + _audio = std::make_shared(info); + if (info.codecId == CodecAAC) + { + addTrack(std::make_shared()); - AACFrame adtsHeader; - adtsHeader.syncword = 0x0FFF; - adtsHeader.id = 0; - adtsHeader.layer = 0; - adtsHeader.protection_absent = 1; - adtsHeader.profile = info.iProfile;//audioObjectType - 1; - int i = 0; - for(auto rate : samplingFrequencyTable){ - if(rate == info.iSampleRate){ - adtsHeader.sf_index = i; - }; - ++i; + AACFrame adtsHeader; + adtsHeader.syncword = 0x0FFF; + adtsHeader.id = 0; + adtsHeader.layer = 0; + adtsHeader.protection_absent = 1; + adtsHeader.profile = info.iProfile;//audioObjectType - 1; + int i = 0; + for (auto rate : samplingFrequencyTable) { + if (rate == info.iSampleRate) { + adtsHeader.sf_index = i; + }; + ++i; + } + adtsHeader.private_bit = 0; + adtsHeader.channel_configuration = info.iChannel; + adtsHeader.original = 0; + adtsHeader.home = 0; + adtsHeader.copyright_identification_bit = 0; + adtsHeader.copyright_identification_start = 0; + adtsHeader.aac_frame_length = 7; + adtsHeader.adts_buffer_fullness = 2047; + adtsHeader.no_raw_data_blocks_in_frame = 0; + writeAdtsHeader(adtsHeader, _adtsHeader); + } + else if (info.codecId == CodecG711A || info.codecId == CodecG711U) + { + addTrack(std::make_shared(info.codecId, info.iSampleBit, info.iSampleRate)); } - adtsHeader.private_bit = 0; - adtsHeader.channel_configuration = info.iChannel; - adtsHeader.original = 0; - adtsHeader.home = 0; - adtsHeader.copyright_identification_bit = 0; - adtsHeader.copyright_identification_start = 0; - adtsHeader.aac_frame_length = 7; - adtsHeader.adts_buffer_fullness = 2047; - adtsHeader.no_raw_data_blocks_in_frame = 0; - writeAdtsHeader(adtsHeader,_adtsHeader); } } /* namespace mediakit */ diff --git a/src/Common/Device.h b/src/Common/Device.h index 4b13ee02..175a5ee2 100644 --- a/src/Common/Device.h +++ b/src/Common/Device.h @@ -41,10 +41,11 @@ public: }; class AudioInfo { public: - int iChannel; - int iSampleBit; - int iSampleRate; - int iProfile; + CodecId codecId; + int iChannel; + int iSampleBit; + int iSampleRate; + int iProfile; }; /** @@ -121,6 +122,13 @@ public: */ void inputAAC(const char *pcDataWithoutAdts,int iDataLen, uint32_t uiStamp,const char *pcAdtsHeader); + /** + * G711音频帧 + * @param pcData 音频帧 + * @param iDataLen 帧数据长度 + * @param uiStamp 时间戳,单位毫秒 + */ + void inputG711(const char* pcData, int iDataLen, uint32_t uiStamp); #ifdef ENABLE_X264 /** * 输入yuv420p视频帧,内部会完成编码并调用inputH264方法 diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index dba041dd..6cc173f4 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -35,6 +35,23 @@ void MediaSink::addTrack(const Track::Ptr &track_in) { if (_allTrackReady) { onTrackFrame(frame); } + else + { + if (frame->getTrackType() == TrackVideo) + { + checkTrackIfReady(nullptr); + + if (_allTrackReady) { + onTrackFrame(frame); + } + else + { + ErrorL << " 还有track未准备好,丢帧 codecName: " << frame->getCodecName(); + } + + }else + ErrorL << " 还有track未准备好,丢帧 codecName: " << frame->getCodecName(); + } })); } @@ -116,6 +133,7 @@ void MediaSink::emitAllTrackReady() { return; } + DebugL << "all track ready use " << _ticker.elapsedTime() << "ms"; if (!_trackReadyCallback.empty()) { //这是超时强制忽略未准备好的Track _trackReadyCallback.clear(); diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index e0a10d2f..1fd6c754 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -13,8 +13,10 @@ #include "H264Rtmp.h" #include "H265Rtmp.h" #include "AACRtmp.h" +#include "G711Rtmp.h" #include "H264Rtp.h" #include "AACRtp.h" +#include "G711Rtp.h" #include "H265Rtp.h" #include "Common/Parser.h" @@ -45,6 +47,14 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) { return std::make_shared(aac_cfg); } + if (strcasecmp(track->_codec.data(), "PCMA") == 0) { + return std::make_shared(CodecG711A); + } + + if (strcasecmp(track->_codec.data(), "PCMU") == 0) { + return std::make_shared(CodecG711U); + } + if (strcasecmp(track->_codec.data(), "h264") == 0) { //a=fmtp:96 packetization-mode=1;profile-level-id=42C01F;sprop-parameter-sets=Z0LAH9oBQBboQAAAAwBAAAAPI8YMqA==,aM48gA== auto map = Parser::parseArgs(FindField(track->_fmtp.data()," ", nullptr),";","="); @@ -90,6 +100,12 @@ Track::Ptr Factory::getTrackByCodecId(CodecId codecId) { case CodecAAC:{ return std::make_shared(); } + case CodecG711A: { + return std::make_shared(CodecG711A); + } + case CodecG711U: { + return std::make_shared(CodecG711U); + } default: WarnL << "暂不支持该CodecId:" << codecId; return nullptr; @@ -125,6 +141,9 @@ RtpCodec::Ptr Factory::getRtpEncoderBySdp(const Sdp::Ptr &sdp) { return std::make_shared(ssrc,mtu,sample_rate,pt,interleaved); case CodecAAC: return std::make_shared(ssrc,mtu,sample_rate,pt,interleaved); + case CodecG711A: + case CodecG711U: + return std::make_shared(ssrc, mtu, sample_rate, pt, interleaved); default: WarnL << "暂不支持该CodecId:" << codec_id; return nullptr; @@ -139,6 +158,9 @@ RtpCodec::Ptr Factory::getRtpDecoderByTrack(const Track::Ptr &track) { return std::make_shared(); case CodecAAC: return std::make_shared(track->clone()); + case CodecG711A: + case CodecG711U: + return std::make_shared(track->clone()); default: WarnL << "暂不支持该CodecId:" << track->getCodecName(); return nullptr; @@ -147,7 +169,7 @@ RtpCodec::Ptr Factory::getRtpDecoderByTrack(const Track::Ptr &track) { /////////////////////////////rtmp相关/////////////////////////////////////////// -Track::Ptr Factory::getTrackByAmf(const AMFValue &amf) { +Track::Ptr Factory::getVideoTrackByAmf(const AMFValue &amf) { CodecId codecId = getCodecIdByAmf(amf); if(codecId == CodecInvalid){ return nullptr; @@ -156,6 +178,15 @@ Track::Ptr Factory::getTrackByAmf(const AMFValue &amf) { } +mediakit::Track::Ptr Factory::getAudioTrackByAmf(const AMFValue& amf) +{ + CodecId codecId = getAudioCodecIdByAmf(amf); + if (codecId == CodecInvalid) { + return nullptr; + } + return getTrackByCodecId(codecId); +} + CodecId Factory::getCodecIdByAmf(const AMFValue &val){ if (val.type() == AMF_STRING){ auto str = val.as_string(); @@ -190,6 +221,36 @@ CodecId Factory::getCodecIdByAmf(const AMFValue &val){ } +CodecId Factory::getAudioCodecIdByAmf(const AMFValue& val) +{ + if (val.type() == AMF_STRING) { + auto str = val.as_string(); + if (str == "mp4a") { + return CodecAAC; + } + WarnL << "暂不支持该Amf:" << str; + return CodecInvalid; + } + + if (val.type() != AMF_NULL) { + auto type_id = val.as_integer(); + switch (type_id) { + case FLV_CODEC_AAC: return CodecAAC; + case FLV_CODEC_G711A: return CodecG711A; + case FLV_CODEC_G711U: return CodecG711U; + + default: + WarnL << "暂不支持该Amf:" << type_id; + return CodecInvalid; + } + } + else { + WarnL << "Metadata不存在相应的Track"; + } + + return CodecInvalid; +} + RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track) { switch (track->getCodecId()){ case CodecH264: @@ -198,6 +259,9 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track) { return std::make_shared(track); case CodecH265: return std::make_shared(track); + case CodecG711A: + case CodecG711U: + return std::make_shared(track); default: WarnL << "暂不支持该CodecId:" << track->getCodecName(); return nullptr; @@ -209,6 +273,8 @@ AMFValue Factory::getAmfByCodecId(CodecId codecId) { case CodecAAC: return AMFValue("mp4a"); case CodecH264: return AMFValue("avc1"); case CodecH265: return AMFValue(FLV_CODEC_H265); + case CodecG711A: return AMFValue(FLV_CODEC_G711A); + case CodecG711U: return AMFValue(FLV_CODEC_G711U); default: return AMFValue(AMF_NULL); } } diff --git a/src/Extension/Factory.h b/src/Extension/Factory.h index fd53a5b7..c8ad09a3 100644 --- a/src/Extension/Factory.h +++ b/src/Extension/Factory.h @@ -56,11 +56,18 @@ public: ////////////////////////////////rtmp相关////////////////////////////////// /** - * 根据amf对象获取响应的Track - * @param amf rtmp metadata中的videocodecid或audiocodecid的值 + * 根据amf对象获取视频相应的Track + * @param amf rtmp metadata中的videocodecid的值 * @return */ - static Track::Ptr getTrackByAmf(const AMFValue &amf); + static Track::Ptr getVideoTrackByAmf(const AMFValue &amf); + + /** + * 根据amf对象获取音频相应的Track + * @param amf rtmp metadata中的audiocodecid的值 + * @return + */ + static Track::Ptr getAudioTrackByAmf(const AMFValue& amf); /** * 根据amf对象获取相应的CodecId @@ -69,6 +76,13 @@ public: */ static CodecId getCodecIdByAmf(const AMFValue &val); + /** + * 根据amf对象获取音频相应的CodecId + * @param val rtmp metadata中的audiocodecid的值 + * @return + */ + static CodecId getAudioCodecIdByAmf(const AMFValue& val); + /** * 根据Track获取Rtmp的编解码器 * @param track 媒体描述对象 diff --git a/src/Extension/Frame.cpp b/src/Extension/Frame.cpp index 9244ca7c..9d543706 100644 --- a/src/Extension/Frame.cpp +++ b/src/Extension/Frame.cpp @@ -28,6 +28,8 @@ const char *CodecInfo::getCodecName() { SWITCH_CASE(CodecH264); SWITCH_CASE(CodecH265); SWITCH_CASE(CodecAAC); + SWITCH_CASE(CodecG711A); + SWITCH_CASE(CodecG711U); default: return "unknown codec"; } diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 1cffb7bc..32df0e3d 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -26,6 +26,8 @@ typedef enum { CodecH264 = 0, CodecH265, CodecAAC, + CodecG711A, + CodecG711U, CodecMax = 0x7FFF } CodecId; diff --git a/src/Extension/G711.cpp b/src/Extension/G711.cpp new file mode 100644 index 00000000..ee4211a8 --- /dev/null +++ b/src/Extension/G711.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "G711.h" + +namespace mediakit{ + + +Sdp::Ptr G711Track::getSdp() { + if(!ready()){ + WarnL << getCodecName() << " Track未准备好"; + return nullptr; + } + return std::make_shared(getCodecId(), getAudioSampleRate(), getCodecId() == CodecG711A ? 8 : 0, getAudioSampleBit()); +} + +}//namespace mediakit + + diff --git a/src/Extension/G711.h b/src/Extension/G711.h new file mode 100644 index 00000000..94c66975 --- /dev/null +++ b/src/Extension/G711.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_G711_H +#define ZLMEDIAKIT_G711_H + +#include "Frame.h" +#include "Track.h" + +namespace mediakit{ + +class G711Frame; + +unsigned const samplingFrequencyTableG711[16] = { 96000, 88200, + 64000, 48000, + 44100, 32000, + 24000, 22050, + 16000, 12000, + 11025, 8000, + 7350, 0, 0, 0 }; + +void makeAdtsHeader(const string &strAudioCfg,G711Frame &adts); +void writeAdtsHeader(const G711Frame &adts, uint8_t *pcAdts) ; +string makeG711AdtsConfig(const uint8_t *pcAdts); +void getAACInfo(const G711Frame &adts,int &iSampleRate,int &iChannel); + + +/** + * aac帧,包含adts头 + */ +class G711Frame : public Frame { +public: + typedef std::shared_ptr Ptr; + + char *data() const override{ + return (char *)buffer; + } + uint32_t size() const override { + return frameLength; + } + uint32_t dts() const override { + return timeStamp; + } + uint32_t prefixSize() const override{ + return iPrefixSize; + } + + TrackType getTrackType() const override{ + return TrackAudio; + } + + CodecId getCodecId() const override{ + return _codecId; + } + + bool keyFrame() const override { + return false; + } + + bool configFrame() const override{ + return false; + } +public: + CodecId _codecId = CodecG711A; + unsigned int frameLength; // 一个帧的长度包括 raw data block + unsigned char buffer[2 * 1024 + 7]; + uint32_t timeStamp; + uint32_t iPrefixSize = 0; +} ; + +class G711FrameNoCacheAble : public FrameNoCacheAble { +public: + typedef std::shared_ptr Ptr; + + G711FrameNoCacheAble(CodecId codecId, char *ptr,uint32_t size,uint32_t dts,int prefixeSize = 7){ + _codecId = codecId; + _ptr = ptr; + _size = size; + _dts = dts; + _prefixSize = prefixeSize; + } + + TrackType getTrackType() const override{ + return TrackAudio; + } + + CodecId getCodecId() const override{ + return _codecId; + } + + bool keyFrame() const override { + return false; + } + + bool configFrame() const override{ + return false; + } + +private: + CodecId _codecId; +} ; + + +/** + * g711音频通道 + */ +class G711Track : public AudioTrack{ +public: + typedef std::shared_ptr Ptr; + + /** + * 延后获取adts头信息 + * 在随后的inputFrame中获取adts头信息 + */ + G711Track(){} + + /** + * G711A G711U + */ + G711Track(CodecId codecId, int sampleBit = 16, int sampleRate = 8000){ + _codecid = codecId; + _sampleBit = sampleBit; + _sampleRate = sampleRate; + onReady(); + } + + /** + * 返回编码类型 + * @return + */ + CodecId getCodecId() const override{ + return _codecid; + } + + /** + * 在获取aac_cfg前是无效的Track + * @return + */ + bool ready() override { + return true; + } + + + /** + * 返回音频采样率 + * @return + */ + int getAudioSampleRate() const override{ + return _sampleRate; + } + /** + * 返回音频采样位数,一般为16或8 + * @return + */ + int getAudioSampleBit() const override{ + return _sampleBit; + } + /** + * 返回音频通道数 + * @return + */ + int getAudioChannel() const override{ + return _channel; + } + + /** + * 输入数据帧,并获取aac_cfg + * @param frame 数据帧 + */ + void inputFrame(const Frame::Ptr &frame) override{ + AudioTrack::inputFrame(frame); + } +private: + /** + * + */ + void onReady(){ +/* + if(_cfg.size() < 2){ + return; + } + G711Frame aacFrame; + makeAdtsHeader(_cfg,aacFrame); + getAACInfo(aacFrame,_sampleRate,_channel);*/ + } + Track::Ptr clone() override { + return std::make_shared::type >(*this); + } + + //生成sdp + Sdp::Ptr getSdp() override ; +private: + string _cfg; + CodecId _codecid = CodecG711A; + int _sampleRate = 8000; + int _sampleBit = 16; + int _channel = 1; +}; + + +/** +* aac类型SDP +*/ +class G711Sdp : public Sdp { +public: + + /** + * + * @param aac_codecId G711A G711U + * @param sample_rate 音频采样率 + * @param playload_type rtp playload type 默认0为G711U, 8为G711A + * @param bitrate 比特率 + */ + G711Sdp(CodecId codecId, + int sample_rate, + int playload_type = 0, + int bitrate = 128) : Sdp(sample_rate,playload_type), _codecId(codecId){ + _printer << "m=audio 0 RTP/AVP " << playload_type << "\r\n"; + //_printer << "b=AS:" << bitrate << "\r\n"; + _printer << "a=rtpmap:" << playload_type << (codecId == CodecG711A ? " PCMA/" : " PCMU/") << sample_rate << "\r\n"; + _printer << "a=control:trackID=" << getTrackType() << "\r\n"; + } + + string getSdp() const override { + return _printer; + } + + TrackType getTrackType() const override { + return TrackAudio; + } + CodecId getCodecId() const override { + return _codecId; + } +private: + _StrPrinter _printer; + CodecId _codecId = CodecG711A; +}; + +}//namespace mediakit + + +#endif //ZLMEDIAKIT_AAC_H diff --git a/src/Extension/G711Rtmp.cpp b/src/Extension/G711Rtmp.cpp new file mode 100644 index 00000000..2381a8ef --- /dev/null +++ b/src/Extension/G711Rtmp.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "G711Rtmp.h" + +namespace mediakit{ + +G711RtmpDecoder::G711RtmpDecoder() { + _adts = obtainFrame(); +} + +G711Frame::Ptr G711RtmpDecoder::obtainFrame() { + //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 + auto frame = ResourcePoolHelper::obtainObj(); + frame->frameLength = 0; + frame->iPrefixSize = 0; + return frame; +} + +bool G711RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool key_pos) { + onGetG711(pkt->strBuf.data() + 2, pkt->strBuf.size() - 2, pkt->timeStamp); + return false; +} + +void G711RtmpDecoder::onGetG711(const char* pcData, int iLen, uint32_t ui32TimeStamp) { + if(iLen + 7 > sizeof(_adts->buffer)){ + WarnL << "Illegal adts data, exceeding the length limit."; + return; + } + + //拷贝aac负载 + memcpy(_adts->buffer, pcData, iLen); + _adts->frameLength = iLen; + _adts->timeStamp = ui32TimeStamp; + + //写入环形缓存 + RtmpCodec::inputFrame(_adts); + _adts = obtainFrame(); +} +///////////////////////////////////////////////////////////////////////////////////// + +G711RtmpEncoder::G711RtmpEncoder(const Track::Ptr &track) { + _track = dynamic_pointer_cast(track); +} + +void G711RtmpEncoder::inputFrame(const Frame::Ptr& frame) { + + RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper::obtainObj(); + rtmpPkt->strBuf.clear(); + rtmpPkt->strBuf.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); + + rtmpPkt->bodySize = rtmpPkt->strBuf.size(); + rtmpPkt->chunkId = CHUNK_AUDIO; + rtmpPkt->streamId = STREAM_MEDIA; + rtmpPkt->timeStamp = frame->dts(); + rtmpPkt->typeId = MSG_AUDIO; + RtmpCodec::inputRtmp(rtmpPkt, false); + +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/Extension/G711Rtmp.h b/src/Extension/G711Rtmp.h new file mode 100644 index 00000000..3ab29765 --- /dev/null +++ b/src/Extension/G711Rtmp.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_G711RTMPCODEC_H +#define ZLMEDIAKIT_G711RTMPCODEC_H + +#include "Rtmp/RtmpCodec.h" +#include "Extension/Track.h" +#include "Extension/G711.h" + +namespace mediakit{ +/** + * G711 Rtmp转adts类 + */ +class G711RtmpDecoder : public RtmpCodec , public ResourcePoolHelper { +public: + typedef std::shared_ptr Ptr; + + G711RtmpDecoder(); + ~G711RtmpDecoder() {} + + /** + * 输入Rtmp并解码 + * @param Rtmp Rtmp数据包 + * @param key_pos 此参数内部强制转换为false,请忽略之 + */ + bool inputRtmp(const RtmpPacket::Ptr &Rtmp, bool key_pos = false) override; + + TrackType getTrackType() const override{ + return TrackAudio; + } + + void setCodecId(CodecId codecId) + { + _codecid = codecId; + } + + CodecId getCodecId() const override{ + return _codecid; + } + +protected: + void onGetG711(const char* pcData, int iLen, uint32_t ui32TimeStamp); + G711Frame::Ptr obtainFrame(); +protected: + G711Frame::Ptr _adts; + CodecId _codecid = CodecInvalid; + +}; + + +/** + * aac adts转Rtmp类 + */ +class G711RtmpEncoder : public G711RtmpDecoder , public ResourcePoolHelper { +public: + typedef std::shared_ptr Ptr; + + /** + * 构造函数,track可以为空,此时则在inputFrame时输入adts头 + * 如果track不为空且包含adts头相关信息, + * 那么inputFrame时可以不输入adts头 + * @param track + */ + G711RtmpEncoder(const Track::Ptr &track); + ~G711RtmpEncoder() {} + + /** + * 输入aac 数据,可以不带adts头 + * @param frame aac数据 + */ + void inputFrame(const Frame::Ptr &frame) override; +private: + G711Track::Ptr _track; +}; + +}//namespace mediakit + +#endif //ZLMEDIAKIT_G711RTMPCODEC_H diff --git a/src/Extension/G711Rtp.cpp b/src/Extension/G711Rtp.cpp new file mode 100644 index 00000000..c55cd956 --- /dev/null +++ b/src/Extension/G711Rtp.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "G711Rtp.h" + +namespace mediakit{ + +G711RtpEncoder::G711RtpEncoder(uint32_t ui32Ssrc, + uint32_t ui32MtuSize, + uint32_t ui32SampleRate, + uint8_t ui8PlayloadType, + uint8_t ui8Interleaved) : + RtpInfo(ui32Ssrc, + ui32MtuSize, + ui32SampleRate, + ui8PlayloadType, + ui8Interleaved){ +} + +void G711RtpEncoder::inputFrame(const Frame::Ptr &frame) { + GET_CONFIG(uint32_t, cycleMS, Rtp::kCycleMS); + auto uiStamp = frame->dts(); + auto pcData = frame->data() + frame->prefixSize(); + auto iLen = frame->size() - frame->prefixSize(); + + uiStamp %= cycleMS; + char *ptr = (char *) pcData; + int iSize = iLen; + while (iSize > 0) { + if (iSize <= _ui32MtuSize - 20) { + makeG711Rtp(ptr, iSize, true, uiStamp); + break; + } + makeG711Rtp(ptr, _ui32MtuSize - 20, false, uiStamp); + ptr += (_ui32MtuSize - 20); + iSize -= (_ui32MtuSize - 20); + } +} + +void G711RtpEncoder::makeG711Rtp(const void *data, unsigned int len, bool mark, uint32_t uiStamp) { + RtpCodec::inputRtp(makeRtp(getTrackType(),data,len,mark,uiStamp), false); +} + +///////////////////////////////////////////////////////////////////////////////////// + +G711RtpDecoder::G711RtpDecoder(const Track::Ptr &track){ + auto g711Track = dynamic_pointer_cast(track); + _codecid = g711Track->getCodecId(); + if(!g711Track || !g711Track->ready()){ + WarnL << "该g711 track无效!"; + }else{ + } + _adts = obtainFrame(); +} +G711RtpDecoder::G711RtpDecoder() { + _adts = obtainFrame(); +} + +G711Frame::Ptr G711RtpDecoder::obtainFrame() { + //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 + auto frame = ResourcePoolHelper::obtainObj(); + frame->frameLength = 0; + frame->iPrefixSize = 0; + return frame; +} + +bool G711RtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool key_pos) { + // 获取rtp数据长度 + int length = rtppack->size() - rtppack->offset; + + // 获取rtp数据 + const uint8_t *rtp_packet_buf = (uint8_t *)rtppack->data() + rtppack->offset; + + _adts->frameLength = length; + memcpy(_adts->buffer, rtp_packet_buf, length); + _adts->_codecId = _codecid; + if (rtppack->mark == true) { + _adts->timeStamp = rtppack->timeStamp; + onGetG711(_adts); + } + return false; +} + +void G711RtpDecoder::onGetG711(const G711Frame::Ptr &frame) { + //写入环形缓存 + RtpCodec::inputFrame(frame); + _adts = obtainFrame(); +} + + +}//namespace mediakit + + + diff --git a/src/Extension/G711Rtp.h b/src/Extension/G711Rtp.h new file mode 100644 index 00000000..2d052720 --- /dev/null +++ b/src/Extension/G711Rtp.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_G711RTPCODEC_H +#define ZLMEDIAKIT_G711RTPCODEC_H + +#include "Rtsp/RtpCodec.h" +#include "Extension/G711.h" +namespace mediakit{ +/** + * G711 rtp转adts类 + */ +class G711RtpDecoder : public RtpCodec , public ResourcePoolHelper { +public: + typedef std::shared_ptr Ptr; + + G711RtpDecoder(const Track::Ptr &track); + ~G711RtpDecoder() {} + + /** + * 输入rtp并解码 + * @param rtp rtp数据包 + * @param key_pos 此参数内部强制转换为false,请忽略之 + */ + bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override; + + TrackType getTrackType() const override{ + return TrackAudio; + } + CodecId getCodecId() const override{ + return _codecid; + } +protected: + G711RtpDecoder(); +private: + void onGetG711(const G711Frame::Ptr &frame); + G711Frame::Ptr obtainFrame(); +private: + G711Frame::Ptr _adts; + CodecId _codecid = CodecInvalid; +}; + + +/** + * g711 转rtp类 + */ +class G711RtpEncoder : public G711RtpDecoder , public RtpInfo { +public: + typedef std::shared_ptr Ptr; + + /** + * @param ui32Ssrc ssrc + * @param ui32MtuSize mtu 大小 + * @param ui32SampleRate 采样率 + * @param ui8PlayloadType pt类型 + * @param ui8Interleaved rtsp interleaved 值 + */ + G711RtpEncoder(uint32_t ui32Ssrc, + uint32_t ui32MtuSize, + uint32_t ui32SampleRate, + uint8_t ui8PlayloadType = 0, + uint8_t ui8Interleaved = TrackAudio * 2); + ~G711RtpEncoder() {} + + /** + * @param frame g711数据 + */ + void inputFrame(const Frame::Ptr &frame) override; +private: + void makeG711Rtp(const void *pData, unsigned int uiLen, bool bMark, uint32_t uiStamp); +private: + unsigned char _aucSectionBuf[1600]; +}; + +}//namespace mediakit + +#endif //ZLMEDIAKIT_G711RTPCODEC_H diff --git a/src/Rtmp/Rtmp.h b/src/Rtmp/Rtmp.h index a02fc0d8..53df18dd 100644 --- a/src/Rtmp/Rtmp.h +++ b/src/Rtmp/Rtmp.h @@ -75,6 +75,9 @@ using namespace toolkit; #define FLV_CODEC_AAC 10 #define FLV_CODEC_H264 7 #define FLV_CODEC_H265 12 +#define FLV_CODEC_G711A 7 +#define FLV_CODEC_G711U 8 + namespace mediakit { diff --git a/src/Rtmp/RtmpDemuxer.cpp b/src/Rtmp/RtmpDemuxer.cpp index bfdd6795..26a3106f 100644 --- a/src/Rtmp/RtmpDemuxer.cpp +++ b/src/Rtmp/RtmpDemuxer.cpp @@ -61,7 +61,7 @@ bool RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) { void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) { //生成Track对象 - _videoTrack = dynamic_pointer_cast(Factory::getTrackByAmf(videoCodec)); + _videoTrack = dynamic_pointer_cast(Factory::getVideoTrackByAmf(videoCodec)); if (_videoTrack) { //生成rtmpCodec对象以便解码rtmp _videoRtmpDecoder = Factory::getRtmpCodecByTrack(_videoTrack); @@ -78,7 +78,7 @@ void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) { void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec) { //生成Track对象 - _audioTrack = dynamic_pointer_cast(Factory::getTrackByAmf(audioCodec)); + _audioTrack = dynamic_pointer_cast(Factory::getAudioTrackByAmf(audioCodec)); if (_audioTrack) { //生成rtmpCodec对象以便解码rtmp _audioRtmpDecoder = Factory::getRtmpCodecByTrack(_audioTrack); diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index a64a640d..d1e07a82 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -743,22 +743,22 @@ void RtspSession::handleReq_Play(const Parser &parser) { } bool useBuf = true; - _enableSendRtp = false; - - if (strRange.size() && !_bFirstPlay) { + _enableSendRtp = false; + float iStartTime = 0; + if (strRange.size() && !_bFirstPlay) { //这个是seek操作 - auto strStart = FindField(strRange.data(), "npt=", "-"); - if (strStart == "now") { - strStart = "0"; - } - auto iStartTime = 1000 * atof(strStart.data()); - InfoP(this) << "rtsp seekTo(ms):" << iStartTime; - useBuf = !pMediaSrc->seekTo(iStartTime); - }else if(pMediaSrc->totalReaderCount() == 0){ - //第一个消费者 - pMediaSrc->seekTo(0); - } - _bFirstPlay = false; + auto strStart = FindField(strRange.data(), "npt=", "-"); + if (strStart == "now") { + strStart = "0"; + } + iStartTime = 1000 * atof(strStart.data()); + InfoP(this) << "rtsp seekTo(ms):" << iStartTime; + useBuf = !pMediaSrc->seekTo(iStartTime); + }else if(pMediaSrc->totalReaderCount() == 0){ + //第一个消费者 + pMediaSrc->seekTo(0); + } + _bFirstPlay = false; _StrPrinter rtp_info; for(auto &track : _aTrackInfo){ @@ -778,10 +778,10 @@ void RtspSession::handleReq_Play(const Parser &parser) { rtp_info.pop_back(); - sendRtspResponse("200 OK", - {"Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << pMediaSrc->getTimeStamp(TrackInvalid) / 1000.0 << "-", - "RTP-Info",rtp_info - }); + sendRtspResponse("200 OK", + {"Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << (useBuf? pMediaSrc->getTimeStamp(TrackInvalid) / 1000.0 : iStartTime / 1000), + "RTP-Info",rtp_info + }); _enableSendRtp = true; setSocketFlags(); diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp index 9248a24b..4f16899c 100644 --- a/tests/test_rtp.cpp +++ b/tests/test_rtp.cpp @@ -85,7 +85,10 @@ int main(int argc,char *argv[]) { //此处选择是否导出调试文件 // mINI::Instance()[RtpProxy::kDumpDir] = "/Users/xzl/Desktop/"; - loadFile(argv[1]); + if (argc == 2) + loadFile(argv[1]); + else + ErrorL << "parameter error."; #else ErrorL << "please ENABLE_RTPPROXY and then test"; #endif//#if defined(ENABLE_RTPPROXY)