Commit 1f43359b by xiongziliang

Merge branch 'master' of https://github.com/baiyfcu/ZLMediaKit

parents 1a97c2e3 40afa204
include_directories(include source) include_directories(include source)
file(GLOB api_src_list include/*.h source/*.cpp source/*.h source/*.c) file(GLOB api_src_list include/*.h source/*.cpp source/*.h source/*.c)
set(ENABLE_API_STATIC false)
if (IOS) if (IOS)
add_library(mk_api STATIC ${api_src_list}) add_library(mk_api STATIC ${api_src_list})
target_link_libraries(mk_api ${LINK_LIB_LIST}) target_link_libraries(mk_api ${LINK_LIB_LIST})
else () else ()
add_library(mk_api SHARED ${api_src_list}) if(ENABLE_API_STATIC)
if (WIN32) add_library(mk_api STATIC ${api_src_list})
add_definitions(-DMediaKitApi_EXPORTS) if (WIN32)
endif () 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}) target_link_libraries(mk_api ${LINK_LIB_LIST})
add_subdirectory(tests) add_subdirectory(tests)
......
...@@ -14,13 +14,20 @@ ...@@ -14,13 +14,20 @@
#include <stdint.h> #include <stdint.h>
#if defined(_WIN32) #if defined(_WIN32)
#ifndef MediaKitApi_STATIC
#if defined(MediaKitApi_EXPORTS) #if defined(MediaKitApi_EXPORTS)
#define API_EXPORT __declspec(dllexport) #define API_EXPORT __declspec(dllexport)
#else #else
#define API_EXPORT __declspec(dllimport) #define API_EXPORT __declspec(dllimport)
#endif #endif
#define API_CALL __cdecl
#else
#define API_EXPORT
#define API_CALL
#endif
#define API_CALL __cdecl
#else #else
#define API_EXPORT #define API_EXPORT
#define API_CALL #define API_CALL
......
...@@ -66,6 +66,16 @@ API_EXPORT void API_CALL mk_media_init_h265(mk_media ctx, int width, int height, ...@@ -66,6 +66,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); 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完毕后调用此函数, * 初始化h264/h265/aac完毕后调用此函数,
* 在单track(只有音频或视频)时,因为ZLMediaKit不知道后续是否还要添加track,所以会多等待3秒钟 * 在单track(只有音频或视频)时,因为ZLMediaKit不知道后续是否还要添加track,所以会多等待3秒钟
* 如果产生的流是单Track类型,请调用此函数以便加快流生成速度,当然不调用该函数,影响也不大(会多等待3秒) * 如果产生的流是单Track类型,请调用此函数以便加快流生成速度,当然不调用该函数,影响也不大(会多等待3秒)
...@@ -114,6 +124,15 @@ API_EXPORT void API_CALL mk_media_input_aac(mk_media ctx, void *data, int len, u ...@@ -114,6 +124,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); 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.close()回调事件
* 在选择关闭一个关联的MediaSource时,将会最终触发到该回调 * 在选择关闭一个关联的MediaSource时,将会最终触发到该回调
* 你应该通过该事件调用mk_media_release函数并且释放其他资源 * 你应该通过该事件调用mk_media_release函数并且释放其他资源
......
...@@ -106,6 +106,12 @@ API_EXPORT void API_CALL mk_player_set_on_shutdown(mk_player ctx, on_mk_play_eve ...@@ -106,6 +106,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); 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);
/**
* 获取视频宽度 * 获取视频宽度
*/ */
API_EXPORT int API_CALL mk_player_video_width(mk_player ctx); API_EXPORT int API_CALL mk_player_video_width(mk_player ctx);
...@@ -121,6 +127,12 @@ API_EXPORT int API_CALL mk_player_video_height(mk_player ctx); ...@@ -121,6 +127,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); 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);
/**
* 获取音频采样率 * 获取音频采样率
*/ */
API_EXPORT int API_CALL mk_player_audio_samplerate(mk_player ctx); API_EXPORT int API_CALL mk_player_audio_samplerate(mk_player ctx);
......
...@@ -140,6 +140,20 @@ API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample ...@@ -140,6 +140,20 @@ API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample
(*obj)->getChannel()->initAudio(info); (*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){ API_EXPORT void API_CALL mk_media_init_complete(mk_media ctx){
assert(ctx); assert(ctx);
MediaHelper::Ptr *obj = (MediaHelper::Ptr *) 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, ...@@ -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); (*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);
}
...@@ -101,6 +101,15 @@ API_EXPORT void API_CALL mk_player_set_on_data(mk_player ctx, on_mk_play_data cb ...@@ -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<VideoTrack>(player->getTrack(TrackVideo));
return track ? track->getCodecId() : CodecInvalid;
}
API_EXPORT int API_CALL mk_player_video_width(mk_player ctx) { API_EXPORT int API_CALL mk_player_video_width(mk_player ctx) {
assert(ctx); assert(ctx);
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
...@@ -122,6 +131,15 @@ API_EXPORT int API_CALL mk_player_video_fps(mk_player ctx) { ...@@ -122,6 +131,15 @@ API_EXPORT int API_CALL mk_player_video_fps(mk_player ctx) {
return track ? track->getVideoFps() : 0; 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<AudioTrack>(player->getTrack(TrackAudio));
return track ? track->getCodecId() : CodecInvalid;
}
API_EXPORT int API_CALL mk_player_audio_samplerate(mk_player ctx) { API_EXPORT int API_CALL mk_player_audio_samplerate(mk_player ctx) {
assert(ctx); assert(ctx);
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "Util/base64.h" #include "Util/base64.h"
#include "Util/TimeTicker.h" #include "Util/TimeTicker.h"
#include "Extension/AAC.h" #include "Extension/AAC.h"
#include "Extension/G711.h"
#include "Extension/H264.h" #include "Extension/H264.h"
#include "Extension/H265.h" #include "Extension/H265.h"
...@@ -148,6 +149,14 @@ void DevChannel::inputAAC(const char *pcDataWithoutAdts,int iDataLen, uint32_t u ...@@ -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<G711FrameNoCacheAble>(_audio->codecId, (char*)pcData, iDataLen, uiStamp, 0));
}
void DevChannel::initVideo(const VideoInfo& info) { void DevChannel::initVideo(const VideoInfo& info) {
_video = std::make_shared<VideoInfo>(info); _video = std::make_shared<VideoInfo>(info);
addTrack(std::make_shared<H264Track>()); addTrack(std::make_shared<H264Track>());
...@@ -159,32 +168,39 @@ void DevChannel::initH265Video(const VideoInfo &info){ ...@@ -159,32 +168,39 @@ void DevChannel::initH265Video(const VideoInfo &info){
} }
void DevChannel::initAudio(const AudioInfo& info) { void DevChannel::initAudio(const AudioInfo& info) {
_audio = std::make_shared<AudioInfo>(info); _audio = std::make_shared<AudioInfo>(info);
addTrack(std::make_shared<AACTrack>()); if (info.codecId == CodecAAC)
{
AACFrame adtsHeader; addTrack(std::make_shared<AACTrack>());
adtsHeader.syncword = 0x0FFF;
adtsHeader.id = 0; AACFrame adtsHeader;
adtsHeader.layer = 0; adtsHeader.syncword = 0x0FFF;
adtsHeader.protection_absent = 1; adtsHeader.id = 0;
adtsHeader.profile = info.iProfile;//audioObjectType - 1; adtsHeader.layer = 0;
int i = 0; adtsHeader.protection_absent = 1;
for(auto rate : samplingFrequencyTable){ adtsHeader.profile = info.iProfile;//audioObjectType - 1;
if(rate == info.iSampleRate){ int i = 0;
adtsHeader.sf_index = i; for (auto rate : samplingFrequencyTable) {
}; if (rate == info.iSampleRate) {
++i; adtsHeader.sf_index = i;
} };
adtsHeader.private_bit = 0; ++i;
adtsHeader.channel_configuration = info.iChannel; }
adtsHeader.original = 0; adtsHeader.private_bit = 0;
adtsHeader.home = 0; adtsHeader.channel_configuration = info.iChannel;
adtsHeader.copyright_identification_bit = 0; adtsHeader.original = 0;
adtsHeader.copyright_identification_start = 0; adtsHeader.home = 0;
adtsHeader.aac_frame_length = 7; adtsHeader.copyright_identification_bit = 0;
adtsHeader.adts_buffer_fullness = 2047; adtsHeader.copyright_identification_start = 0;
adtsHeader.no_raw_data_blocks_in_frame = 0; adtsHeader.aac_frame_length = 7;
writeAdtsHeader(adtsHeader,_adtsHeader); 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<G711Track>(info.codecId, info.iSampleBit, info.iSampleRate));
}
} }
} /* namespace mediakit */ } /* namespace mediakit */
......
...@@ -41,10 +41,11 @@ public: ...@@ -41,10 +41,11 @@ public:
}; };
class AudioInfo { class AudioInfo {
public: public:
int iChannel; CodecId codecId;
int iSampleBit; int iChannel;
int iSampleRate; int iSampleBit;
int iProfile; int iSampleRate;
int iProfile;
}; };
/** /**
...@@ -121,6 +122,13 @@ public: ...@@ -121,6 +122,13 @@ public:
*/ */
void inputAAC(const char *pcDataWithoutAdts,int iDataLen, uint32_t uiStamp,const char *pcAdtsHeader); 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 #ifdef ENABLE_X264
/** /**
* 输入yuv420p视频帧,内部会完成编码并调用inputH264方法 * 输入yuv420p视频帧,内部会完成编码并调用inputH264方法
......
...@@ -35,6 +35,23 @@ void MediaSink::addTrack(const Track::Ptr &track_in) { ...@@ -35,6 +35,23 @@ void MediaSink::addTrack(const Track::Ptr &track_in) {
if (_allTrackReady) { if (_allTrackReady) {
onTrackFrame(frame); 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() { ...@@ -116,6 +133,7 @@ void MediaSink::emitAllTrackReady() {
return; return;
} }
DebugL << "all track ready use " << _ticker.elapsedTime() << "ms";
if (!_trackReadyCallback.empty()) { if (!_trackReadyCallback.empty()) {
//这是超时强制忽略未准备好的Track //这是超时强制忽略未准备好的Track
_trackReadyCallback.clear(); _trackReadyCallback.clear();
......
...@@ -13,8 +13,10 @@ ...@@ -13,8 +13,10 @@
#include "H264Rtmp.h" #include "H264Rtmp.h"
#include "H265Rtmp.h" #include "H265Rtmp.h"
#include "AACRtmp.h" #include "AACRtmp.h"
#include "G711Rtmp.h"
#include "H264Rtp.h" #include "H264Rtp.h"
#include "AACRtp.h" #include "AACRtp.h"
#include "G711Rtp.h"
#include "H265Rtp.h" #include "H265Rtp.h"
#include "Common/Parser.h" #include "Common/Parser.h"
...@@ -45,6 +47,14 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) { ...@@ -45,6 +47,14 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) {
return std::make_shared<AACTrack>(aac_cfg); return std::make_shared<AACTrack>(aac_cfg);
} }
if (strcasecmp(track->_codec.data(), "PCMA") == 0) {
return std::make_shared<G711Track>(CodecG711A);
}
if (strcasecmp(track->_codec.data(), "PCMU") == 0) {
return std::make_shared<G711Track>(CodecG711U);
}
if (strcasecmp(track->_codec.data(), "h264") == 0) { if (strcasecmp(track->_codec.data(), "h264") == 0) {
//a=fmtp:96 packetization-mode=1;profile-level-id=42C01F;sprop-parameter-sets=Z0LAH9oBQBboQAAAAwBAAAAPI8YMqA==,aM48gA== //a=fmtp:96 packetization-mode=1;profile-level-id=42C01F;sprop-parameter-sets=Z0LAH9oBQBboQAAAAwBAAAAPI8YMqA==,aM48gA==
auto map = Parser::parseArgs(FindField(track->_fmtp.data()," ", nullptr),";","="); auto map = Parser::parseArgs(FindField(track->_fmtp.data()," ", nullptr),";","=");
...@@ -90,6 +100,12 @@ Track::Ptr Factory::getTrackByCodecId(CodecId codecId) { ...@@ -90,6 +100,12 @@ Track::Ptr Factory::getTrackByCodecId(CodecId codecId) {
case CodecAAC:{ case CodecAAC:{
return std::make_shared<AACTrack>(); return std::make_shared<AACTrack>();
} }
case CodecG711A: {
return std::make_shared<G711Track>(CodecG711A);
}
case CodecG711U: {
return std::make_shared<G711Track>(CodecG711U);
}
default: default:
WarnL << "暂不支持该CodecId:" << codecId; WarnL << "暂不支持该CodecId:" << codecId;
return nullptr; return nullptr;
...@@ -125,6 +141,9 @@ RtpCodec::Ptr Factory::getRtpEncoderBySdp(const Sdp::Ptr &sdp) { ...@@ -125,6 +141,9 @@ RtpCodec::Ptr Factory::getRtpEncoderBySdp(const Sdp::Ptr &sdp) {
return std::make_shared<H265RtpEncoder>(ssrc,mtu,sample_rate,pt,interleaved); return std::make_shared<H265RtpEncoder>(ssrc,mtu,sample_rate,pt,interleaved);
case CodecAAC: case CodecAAC:
return std::make_shared<AACRtpEncoder>(ssrc,mtu,sample_rate,pt,interleaved); return std::make_shared<AACRtpEncoder>(ssrc,mtu,sample_rate,pt,interleaved);
case CodecG711A:
case CodecG711U:
return std::make_shared<G711RtpEncoder>(ssrc, mtu, sample_rate, pt, interleaved);
default: default:
WarnL << "暂不支持该CodecId:" << codec_id; WarnL << "暂不支持该CodecId:" << codec_id;
return nullptr; return nullptr;
...@@ -139,6 +158,9 @@ RtpCodec::Ptr Factory::getRtpDecoderByTrack(const Track::Ptr &track) { ...@@ -139,6 +158,9 @@ RtpCodec::Ptr Factory::getRtpDecoderByTrack(const Track::Ptr &track) {
return std::make_shared<H265RtpDecoder>(); return std::make_shared<H265RtpDecoder>();
case CodecAAC: case CodecAAC:
return std::make_shared<AACRtpDecoder>(track->clone()); return std::make_shared<AACRtpDecoder>(track->clone());
case CodecG711A:
case CodecG711U:
return std::make_shared<G711RtpDecoder>(track->clone());
default: default:
WarnL << "暂不支持该CodecId:" << track->getCodecName(); WarnL << "暂不支持该CodecId:" << track->getCodecName();
return nullptr; return nullptr;
...@@ -147,7 +169,7 @@ RtpCodec::Ptr Factory::getRtpDecoderByTrack(const Track::Ptr &track) { ...@@ -147,7 +169,7 @@ RtpCodec::Ptr Factory::getRtpDecoderByTrack(const Track::Ptr &track) {
/////////////////////////////rtmp相关/////////////////////////////////////////// /////////////////////////////rtmp相关///////////////////////////////////////////
Track::Ptr Factory::getTrackByAmf(const AMFValue &amf) { Track::Ptr Factory::getVideoTrackByAmf(const AMFValue &amf) {
CodecId codecId = getCodecIdByAmf(amf); CodecId codecId = getCodecIdByAmf(amf);
if(codecId == CodecInvalid){ if(codecId == CodecInvalid){
return nullptr; return nullptr;
...@@ -156,6 +178,15 @@ Track::Ptr Factory::getTrackByAmf(const AMFValue &amf) { ...@@ -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){ CodecId Factory::getCodecIdByAmf(const AMFValue &val){
if (val.type() == AMF_STRING){ if (val.type() == AMF_STRING){
auto str = val.as_string(); auto str = val.as_string();
...@@ -190,6 +221,36 @@ CodecId Factory::getCodecIdByAmf(const AMFValue &val){ ...@@ -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) { RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track) {
switch (track->getCodecId()){ switch (track->getCodecId()){
case CodecH264: case CodecH264:
...@@ -198,6 +259,9 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track) { ...@@ -198,6 +259,9 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track) {
return std::make_shared<AACRtmpEncoder>(track); return std::make_shared<AACRtmpEncoder>(track);
case CodecH265: case CodecH265:
return std::make_shared<H265RtmpEncoder>(track); return std::make_shared<H265RtmpEncoder>(track);
case CodecG711A:
case CodecG711U:
return std::make_shared<G711RtmpEncoder>(track);
default: default:
WarnL << "暂不支持该CodecId:" << track->getCodecName(); WarnL << "暂不支持该CodecId:" << track->getCodecName();
return nullptr; return nullptr;
...@@ -209,6 +273,8 @@ AMFValue Factory::getAmfByCodecId(CodecId codecId) { ...@@ -209,6 +273,8 @@ AMFValue Factory::getAmfByCodecId(CodecId codecId) {
case CodecAAC: return AMFValue("mp4a"); case CodecAAC: return AMFValue("mp4a");
case CodecH264: return AMFValue("avc1"); case CodecH264: return AMFValue("avc1");
case CodecH265: return AMFValue(FLV_CODEC_H265); 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); default: return AMFValue(AMF_NULL);
} }
} }
......
...@@ -56,11 +56,18 @@ public: ...@@ -56,11 +56,18 @@ public:
////////////////////////////////rtmp相关////////////////////////////////// ////////////////////////////////rtmp相关//////////////////////////////////
/** /**
* 根据amf对象获取应的Track * 根据amf对象获取视频相应的Track
* @param amf rtmp metadata中的videocodecid或audiocodecid的值 * @param amf rtmp metadata中的videocodecid的值
* @return * @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 * 根据amf对象获取相应的CodecId
...@@ -70,6 +77,13 @@ public: ...@@ -70,6 +77,13 @@ public:
static CodecId getCodecIdByAmf(const AMFValue &val); static CodecId getCodecIdByAmf(const AMFValue &val);
/** /**
* 根据amf对象获取音频相应的CodecId
* @param val rtmp metadata中的audiocodecid的值
* @return
*/
static CodecId getAudioCodecIdByAmf(const AMFValue& val);
/**
* 根据Track获取Rtmp的编解码器 * 根据Track获取Rtmp的编解码器
* @param track 媒体描述对象 * @param track 媒体描述对象
* @return * @return
......
...@@ -28,6 +28,8 @@ const char *CodecInfo::getCodecName() { ...@@ -28,6 +28,8 @@ const char *CodecInfo::getCodecName() {
SWITCH_CASE(CodecH264); SWITCH_CASE(CodecH264);
SWITCH_CASE(CodecH265); SWITCH_CASE(CodecH265);
SWITCH_CASE(CodecAAC); SWITCH_CASE(CodecAAC);
SWITCH_CASE(CodecG711A);
SWITCH_CASE(CodecG711U);
default: default:
return "unknown codec"; return "unknown codec";
} }
......
...@@ -26,6 +26,8 @@ typedef enum { ...@@ -26,6 +26,8 @@ typedef enum {
CodecH264 = 0, CodecH264 = 0,
CodecH265, CodecH265,
CodecAAC, CodecAAC,
CodecG711A,
CodecG711U,
CodecMax = 0x7FFF CodecMax = 0x7FFF
} CodecId; } CodecId;
......
/*
* 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<G711Sdp>(getCodecId(), getAudioSampleRate(), getCodecId() == CodecG711A ? 8 : 0, getAudioSampleBit());
}
}//namespace mediakit
/*
* 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<G711Frame> 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<G711FrameNoCacheAble> 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<G711Track> 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<std::remove_reference<decltype(*this)>::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
/*
* 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<G711Frame>::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<G711Track>(track);
}
void G711RtmpEncoder::inputFrame(const Frame::Ptr& frame) {
RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper<RtmpPacket>::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
/*
* 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<G711Frame> {
public:
typedef std::shared_ptr<G711RtmpDecoder> 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<RtmpPacket> {
public:
typedef std::shared_ptr<G711RtmpEncoder> 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
/*
* 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<G711Track>(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<G711Frame>::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
/*
* 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<G711Frame> {
public:
typedef std::shared_ptr<G711RtpDecoder> 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<G711RtpEncoder> 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
...@@ -75,6 +75,9 @@ using namespace toolkit; ...@@ -75,6 +75,9 @@ using namespace toolkit;
#define FLV_CODEC_AAC 10 #define FLV_CODEC_AAC 10
#define FLV_CODEC_H264 7 #define FLV_CODEC_H264 7
#define FLV_CODEC_H265 12 #define FLV_CODEC_H265 12
#define FLV_CODEC_G711A 7
#define FLV_CODEC_G711U 8
namespace mediakit { namespace mediakit {
......
...@@ -61,7 +61,7 @@ bool RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) { ...@@ -61,7 +61,7 @@ bool RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) {
void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) { void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) {
//生成Track对象 //生成Track对象
_videoTrack = dynamic_pointer_cast<VideoTrack>(Factory::getTrackByAmf(videoCodec)); _videoTrack = dynamic_pointer_cast<VideoTrack>(Factory::getVideoTrackByAmf(videoCodec));
if (_videoTrack) { if (_videoTrack) {
//生成rtmpCodec对象以便解码rtmp //生成rtmpCodec对象以便解码rtmp
_videoRtmpDecoder = Factory::getRtmpCodecByTrack(_videoTrack); _videoRtmpDecoder = Factory::getRtmpCodecByTrack(_videoTrack);
...@@ -78,7 +78,7 @@ void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) { ...@@ -78,7 +78,7 @@ void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) {
void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec) { void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec) {
//生成Track对象 //生成Track对象
_audioTrack = dynamic_pointer_cast<AudioTrack>(Factory::getTrackByAmf(audioCodec)); _audioTrack = dynamic_pointer_cast<AudioTrack>(Factory::getAudioTrackByAmf(audioCodec));
if (_audioTrack) { if (_audioTrack) {
//生成rtmpCodec对象以便解码rtmp //生成rtmpCodec对象以便解码rtmp
_audioRtmpDecoder = Factory::getRtmpCodecByTrack(_audioTrack); _audioRtmpDecoder = Factory::getRtmpCodecByTrack(_audioTrack);
......
...@@ -743,22 +743,22 @@ void RtspSession::handleReq_Play(const Parser &parser) { ...@@ -743,22 +743,22 @@ void RtspSession::handleReq_Play(const Parser &parser) {
} }
bool useBuf = true; bool useBuf = true;
_enableSendRtp = false; _enableSendRtp = false;
float iStartTime = 0;
if (strRange.size() && !_bFirstPlay) { if (strRange.size() && !_bFirstPlay) {
//这个是seek操作 //这个是seek操作
auto strStart = FindField(strRange.data(), "npt=", "-"); auto strStart = FindField(strRange.data(), "npt=", "-");
if (strStart == "now") { if (strStart == "now") {
strStart = "0"; strStart = "0";
} }
auto iStartTime = 1000 * atof(strStart.data()); iStartTime = 1000 * atof(strStart.data());
InfoP(this) << "rtsp seekTo(ms):" << iStartTime; InfoP(this) << "rtsp seekTo(ms):" << iStartTime;
useBuf = !pMediaSrc->seekTo(iStartTime); useBuf = !pMediaSrc->seekTo(iStartTime);
}else if(pMediaSrc->totalReaderCount() == 0){ }else if(pMediaSrc->totalReaderCount() == 0){
//第一个消费者 //第一个消费者
pMediaSrc->seekTo(0); pMediaSrc->seekTo(0);
} }
_bFirstPlay = false; _bFirstPlay = false;
_StrPrinter rtp_info; _StrPrinter rtp_info;
for(auto &track : _aTrackInfo){ for(auto &track : _aTrackInfo){
...@@ -778,10 +778,10 @@ void RtspSession::handleReq_Play(const Parser &parser) { ...@@ -778,10 +778,10 @@ void RtspSession::handleReq_Play(const Parser &parser) {
rtp_info.pop_back(); rtp_info.pop_back();
sendRtspResponse("200 OK", sendRtspResponse("200 OK",
{"Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << pMediaSrc->getTimeStamp(TrackInvalid) / 1000.0 << "-", {"Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << (useBuf? pMediaSrc->getTimeStamp(TrackInvalid) / 1000.0 : iStartTime / 1000),
"RTP-Info",rtp_info "RTP-Info",rtp_info
}); });
_enableSendRtp = true; _enableSendRtp = true;
setSocketFlags(); setSocketFlags();
......
...@@ -85,7 +85,10 @@ int main(int argc,char *argv[]) { ...@@ -85,7 +85,10 @@ int main(int argc,char *argv[]) {
//此处选择是否导出调试文件 //此处选择是否导出调试文件
// mINI::Instance()[RtpProxy::kDumpDir] = "/Users/xzl/Desktop/"; // mINI::Instance()[RtpProxy::kDumpDir] = "/Users/xzl/Desktop/";
loadFile(argv[1]); if (argc == 2)
loadFile(argv[1]);
else
ErrorL << "parameter error.";
#else #else
ErrorL << "please ENABLE_RTPPROXY and then test"; ErrorL << "please ENABLE_RTPPROXY and then test";
#endif//#if defined(ENABLE_RTPPROXY) #endif//#if defined(ENABLE_RTPPROXY)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论