Commit a86398b6 by xia-chu

优化与完善rtmp协议相关代码

rtmp相关常量由宏改为枚举
明确rtmp包一些字段赋值含义
parent 47add544
...@@ -18,7 +18,7 @@ namespace mediakit { ...@@ -18,7 +18,7 @@ namespace mediakit {
static string getAacCfg(const RtmpPacket &thiz) { static string getAacCfg(const RtmpPacket &thiz) {
string ret; string ret;
if (thiz.getMediaType() != FLV_CODEC_AAC) { if ((RtmpAudioCodec)thiz.getRtmpCodecId() != RtmpAudioCodec::aac) {
return ret; return ret;
} }
if (!thiz.isCfgFrame()) { if (!thiz.isCfgFrame()) {
...@@ -93,51 +93,45 @@ void AACRtmpEncoder::makeConfigPacket() { ...@@ -93,51 +93,45 @@ void AACRtmpEncoder::makeConfigPacket() {
bool AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) { bool AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) {
if (_aac_cfg.empty()) { if (_aac_cfg.empty()) {
if (frame->prefixSize()) { if (frame->prefixSize()) {
//包含adts头,从adts头获取aac配置信息 // 包含adts头,从adts头获取aac配置信息
_aac_cfg = makeAacConfig((uint8_t *) (frame->data()), frame->prefixSize()); _aac_cfg = makeAacConfig((uint8_t *)(frame->data()), frame->prefixSize());
} }
makeConfigPacket(); makeConfigPacket();
} }
if(_aac_cfg.empty()){ if (_aac_cfg.empty()) {
return false; return false;
} }
auto rtmpPkt = RtmpPacket::create(); auto pkt = RtmpPacket::create();
//header // header
uint8_t is_config = false; pkt->buffer.push_back(_audio_flv_flags);
rtmpPkt->buffer.push_back(_audio_flv_flags); pkt->buffer.push_back((uint8_t)RtmpAACPacketType::aac_raw);
rtmpPkt->buffer.push_back(!is_config); // aac data
pkt->buffer.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
//aac data pkt->body_size = pkt->buffer.size();
rtmpPkt->buffer.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); pkt->chunk_id = CHUNK_AUDIO;
pkt->stream_index = STREAM_MEDIA;
rtmpPkt->body_size = rtmpPkt->buffer.size(); pkt->time_stamp = frame->dts();
rtmpPkt->chunk_id = CHUNK_AUDIO; pkt->type_id = MSG_AUDIO;
rtmpPkt->stream_index = STREAM_MEDIA; RtmpCodec::inputRtmp(pkt);
rtmpPkt->time_stamp = frame->dts();
rtmpPkt->type_id = MSG_AUDIO;
RtmpCodec::inputRtmp(rtmpPkt);
return true; return true;
} }
void AACRtmpEncoder::makeAudioConfigPkt() { void AACRtmpEncoder::makeAudioConfigPkt() {
_audio_flv_flags = getAudioRtmpFlags(std::make_shared<AACTrack>(_aac_cfg)); _audio_flv_flags = getAudioRtmpFlags(std::make_shared<AACTrack>(_aac_cfg));
auto rtmpPkt = RtmpPacket::create(); auto pkt = RtmpPacket::create();
// header
//header pkt->buffer.push_back(_audio_flv_flags);
uint8_t is_config = true; pkt->buffer.push_back((uint8_t)RtmpAACPacketType::aac_config_header);
rtmpPkt->buffer.push_back(_audio_flv_flags); // aac config
rtmpPkt->buffer.push_back(!is_config); pkt->buffer.append(_aac_cfg);
//aac config pkt->body_size = pkt->buffer.size();
rtmpPkt->buffer.append(_aac_cfg); pkt->chunk_id = CHUNK_AUDIO;
pkt->stream_index = STREAM_MEDIA;
rtmpPkt->body_size = rtmpPkt->buffer.size(); pkt->time_stamp = 0;
rtmpPkt->chunk_id = CHUNK_AUDIO; pkt->type_id = MSG_AUDIO;
rtmpPkt->stream_index = STREAM_MEDIA; RtmpCodec::inputRtmp(pkt);
rtmpPkt->time_stamp = 0;
rtmpPkt->type_id = MSG_AUDIO;
RtmpCodec::inputRtmp(rtmpPkt);
} }
}//namespace mediakit }//namespace mediakit
\ No newline at end of file
...@@ -201,11 +201,11 @@ static CodecId getVideoCodecIdByAmf(const AMFValue &val){ ...@@ -201,11 +201,11 @@ static CodecId getVideoCodecIdByAmf(const AMFValue &val){
} }
if (val.type() != AMF_NULL) { if (val.type() != AMF_NULL) {
auto type_id = val.as_integer(); auto type_id = (RtmpVideoCodec)val.as_integer();
switch (type_id) { switch (type_id) {
case FLV_CODEC_H264 : return CodecH264; case RtmpVideoCodec::h264: return CodecH264;
case FLV_CODEC_H265 : return CodecH265; case RtmpVideoCodec::h265: return CodecH265;
default : WarnL << "暂不支持该视频Amf:" << type_id; return CodecInvalid; default: WarnL << "暂不支持该视频Amf:" << (int)type_id; return CodecInvalid;
} }
} }
return CodecInvalid; return CodecInvalid;
...@@ -243,13 +243,13 @@ static CodecId getAudioCodecIdByAmf(const AMFValue &val) { ...@@ -243,13 +243,13 @@ static CodecId getAudioCodecIdByAmf(const AMFValue &val) {
} }
if (val.type() != AMF_NULL) { if (val.type() != AMF_NULL) {
auto type_id = val.as_integer(); auto type_id = (RtmpAudioCodec)val.as_integer();
switch (type_id) { switch (type_id) {
case FLV_CODEC_AAC : return CodecAAC; case RtmpAudioCodec::aac : return CodecAAC;
case FLV_CODEC_G711A : return CodecG711A; case RtmpAudioCodec::g711a : return CodecG711A;
case FLV_CODEC_G711U : return CodecG711U; case RtmpAudioCodec::g711u : return CodecG711U;
case FLV_CODEC_OPUS : return CodecOpus; case RtmpAudioCodec::opus : return CodecOpus;
default : WarnL << "暂不支持该音频Amf:" << type_id; return CodecInvalid; default : WarnL << "暂不支持该音频Amf:" << (int)type_id; return CodecInvalid;
} }
} }
...@@ -291,13 +291,13 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track, bool is_enc ...@@ -291,13 +291,13 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track, bool is_enc
} }
AMFValue Factory::getAmfByCodecId(CodecId codecId) { AMFValue Factory::getAmfByCodecId(CodecId codecId) {
switch (codecId){ switch (codecId) {
case CodecAAC: return AMFValue(FLV_CODEC_AAC); case CodecAAC: return AMFValue((int)RtmpAudioCodec::aac);
case CodecH264: return AMFValue(FLV_CODEC_H264); case CodecH264: return AMFValue((int)RtmpVideoCodec::h264);
case CodecH265: return AMFValue(FLV_CODEC_H265); case CodecH265: return AMFValue((int)RtmpVideoCodec::h265);
case CodecG711A: return AMFValue(FLV_CODEC_G711A); case CodecG711A: return AMFValue((int)RtmpAudioCodec::g711a);
case CodecG711U: return AMFValue(FLV_CODEC_G711U); case CodecG711U: return AMFValue((int)RtmpAudioCodec::g711u);
case CodecOpus: return AMFValue(FLV_CODEC_OPUS); case CodecOpus: return AMFValue((int)RtmpAudioCodec::opus);
default: return AMFValue(AMF_NULL); default: return AMFValue(AMF_NULL);
} }
} }
......
...@@ -30,7 +30,7 @@ H264Frame::Ptr H264RtmpDecoder::obtainFrame() { ...@@ -30,7 +30,7 @@ H264Frame::Ptr H264RtmpDecoder::obtainFrame() {
* 返回不带0x00 00 00 01头的sps pps * 返回不带0x00 00 00 01头的sps pps
*/ */
static bool getH264Config(const RtmpPacket &thiz, string &sps, string &pps) { static bool getH264Config(const RtmpPacket &thiz, string &sps, string &pps) {
if (thiz.getMediaType() != FLV_CODEC_H264) { if ((RtmpVideoCodec)thiz.getRtmpCodecId() != RtmpVideoCodec::h264) {
return false; return false;
} }
if (!thiz.isCfgFrame()) { if (!thiz.isCfgFrame()) {
...@@ -159,22 +159,21 @@ bool H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { ...@@ -159,22 +159,21 @@ bool H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
} }
return _merger.inputFrame(frame, [this](uint64_t dts, uint64_t pts, const Buffer::Ptr &, bool have_key_frame) { return _merger.inputFrame(frame, [this](uint64_t dts, uint64_t pts, const Buffer::Ptr &, bool have_key_frame) {
//flags // flags
_rtmp_packet->buffer[0] = FLV_CODEC_H264 | ((have_key_frame ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4); _rtmp_packet->buffer[0] = (uint8_t)RtmpVideoCodec::h264 | ((uint8_t)(have_key_frame ? RtmpFrameType::key_frame : RtmpFrameType::inter_frame) << 4);
//not config _rtmp_packet->buffer[1] = (uint8_t)RtmpH264PacketType::h264_nalu;
_rtmp_packet->buffer[1] = true; int32_t cts = pts - dts;
int32_t cts = pts - dts; // cts
//cts set_be24(&_rtmp_packet->buffer[2], cts);
set_be24(&_rtmp_packet->buffer[2], cts); _rtmp_packet->time_stamp = dts;
_rtmp_packet->time_stamp = dts; _rtmp_packet->body_size = _rtmp_packet->buffer.size();
_rtmp_packet->body_size = _rtmp_packet->buffer.size(); _rtmp_packet->chunk_id = CHUNK_VIDEO;
_rtmp_packet->chunk_id = CHUNK_VIDEO; _rtmp_packet->stream_index = STREAM_MEDIA;
_rtmp_packet->stream_index = STREAM_MEDIA; _rtmp_packet->type_id = MSG_VIDEO;
_rtmp_packet->type_id = MSG_VIDEO; // 输出rtmp packet
//输出rtmp packet RtmpCodec::inputRtmp(_rtmp_packet);
RtmpCodec::inputRtmp(_rtmp_packet); _rtmp_packet = nullptr;
_rtmp_packet = nullptr; }, &_rtmp_packet->buffer);
}, &_rtmp_packet->buffer);
} }
void H264RtmpEncoder::makeVideoConfigPkt() { void H264RtmpEncoder::makeVideoConfigPkt() {
...@@ -182,42 +181,39 @@ void H264RtmpEncoder::makeVideoConfigPkt() { ...@@ -182,42 +181,39 @@ void H264RtmpEncoder::makeVideoConfigPkt() {
WarnL << "sps长度不足4字节"; WarnL << "sps长度不足4字节";
return; return;
} }
int8_t flags = FLV_CODEC_H264; auto flags = (uint8_t)RtmpVideoCodec::h264;
flags |= (FLV_KEY_FRAME << 4); flags |= ((uint8_t)RtmpFrameType::key_frame << 4);
bool is_config = true; auto pkt = RtmpPacket::create();
// header
auto rtmpPkt = RtmpPacket::create(); pkt->buffer.push_back(flags);
//header pkt->buffer.push_back((uint8_t)RtmpH264PacketType::h264_config_header);
rtmpPkt->buffer.push_back(flags); // cts
rtmpPkt->buffer.push_back(!is_config); pkt->buffer.append("\x0\x0\x0", 3);
//cts // AVCDecoderConfigurationRecord start
rtmpPkt->buffer.append("\x0\x0\x0", 3); pkt->buffer.push_back(1); // version
pkt->buffer.push_back(_sps[1]); // profile
//AVCDecoderConfigurationRecord start pkt->buffer.push_back(_sps[2]); // compat
rtmpPkt->buffer.push_back(1); // version pkt->buffer.push_back(_sps[3]); // level
rtmpPkt->buffer.push_back(_sps[1]); // profile pkt->buffer.push_back((char)0xff); // 6 bits reserved + 2 bits nal size length - 1 (11)
rtmpPkt->buffer.push_back(_sps[2]); // compat pkt->buffer.push_back((char)0xe1); // 3 bits reserved + 5 bits number of sps (00001)
rtmpPkt->buffer.push_back(_sps[3]); // level // sps
rtmpPkt->buffer.push_back((char)0xff); // 6 bits reserved + 2 bits nal size length - 1 (11)
rtmpPkt->buffer.push_back((char)0xe1); // 3 bits reserved + 5 bits number of sps (00001)
//sps
uint16_t size = (uint16_t)_sps.size(); uint16_t size = (uint16_t)_sps.size();
size = htons(size); size = htons(size);
rtmpPkt->buffer.append((char *) &size, 2); pkt->buffer.append((char *)&size, 2);
rtmpPkt->buffer.append(_sps); pkt->buffer.append(_sps);
//pps // pps
rtmpPkt->buffer.push_back(1); // version pkt->buffer.push_back(1); // version
size = (uint16_t)_pps.size(); size = (uint16_t)_pps.size();
size = htons(size); size = htons(size);
rtmpPkt->buffer.append((char *) &size, 2); pkt->buffer.append((char *)&size, 2);
rtmpPkt->buffer.append(_pps); pkt->buffer.append(_pps);
rtmpPkt->body_size = rtmpPkt->buffer.size(); pkt->body_size = pkt->buffer.size();
rtmpPkt->chunk_id = CHUNK_VIDEO; pkt->chunk_id = CHUNK_VIDEO;
rtmpPkt->stream_index = STREAM_MEDIA; pkt->stream_index = STREAM_MEDIA;
rtmpPkt->time_stamp = 0; pkt->time_stamp = 0;
rtmpPkt->type_id = MSG_VIDEO; pkt->type_id = MSG_VIDEO;
RtmpCodec::inputRtmp(rtmpPkt); RtmpCodec::inputRtmp(pkt);
} }
}//namespace mediakit }//namespace mediakit
...@@ -50,7 +50,7 @@ static bool decode_HEVCDecoderConfigurationRecord(uint8_t *extra, size_t bytes, ...@@ -50,7 +50,7 @@ static bool decode_HEVCDecoderConfigurationRecord(uint8_t *extra, size_t bytes,
* 返回不带0x00 00 00 01头的sps * 返回不带0x00 00 00 01头的sps
*/ */
static bool getH265ConfigFrame(const RtmpPacket &thiz, string &frame) { static bool getH265ConfigFrame(const RtmpPacket &thiz, string &frame) {
if (thiz.getMediaType() != FLV_CODEC_H265) { if ((RtmpVideoCodec)thiz.getRtmpCodecId() != RtmpVideoCodec::h265) {
return false; return false;
} }
if (!thiz.isCfgFrame()) { if (!thiz.isCfgFrame()) {
...@@ -243,34 +243,31 @@ bool H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) { ...@@ -243,34 +243,31 @@ bool H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
} }
return _merger.inputFrame(frame, [this](uint64_t dts, uint64_t pts, const Buffer::Ptr &, bool have_key_frame) { return _merger.inputFrame(frame, [this](uint64_t dts, uint64_t pts, const Buffer::Ptr &, bool have_key_frame) {
// flags // flags
_rtmp_packet->buffer[0] = FLV_CODEC_H265 | ((have_key_frame ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4); _rtmp_packet->buffer[0] = (uint8_t)RtmpVideoCodec::h265 | ((uint8_t)(have_key_frame ? RtmpFrameType::key_frame : RtmpFrameType::inter_frame) << 4);
// not config _rtmp_packet->buffer[1] = (uint8_t)RtmpH264PacketType::h264_nalu;
_rtmp_packet->buffer[1] = true; int32_t cts = pts - dts;
int32_t cts = pts - dts; // cts
// cts set_be24(&_rtmp_packet->buffer[2], cts);
set_be24(&_rtmp_packet->buffer[2], cts); _rtmp_packet->time_stamp = dts;
_rtmp_packet->body_size = _rtmp_packet->buffer.size();
_rtmp_packet->time_stamp = dts; _rtmp_packet->chunk_id = CHUNK_VIDEO;
_rtmp_packet->body_size = _rtmp_packet->buffer.size(); _rtmp_packet->stream_index = STREAM_MEDIA;
_rtmp_packet->chunk_id = CHUNK_VIDEO; _rtmp_packet->type_id = MSG_VIDEO;
_rtmp_packet->stream_index = STREAM_MEDIA; // 输出rtmp packet
_rtmp_packet->type_id = MSG_VIDEO; RtmpCodec::inputRtmp(_rtmp_packet);
// 输出rtmp packet _rtmp_packet = nullptr;
RtmpCodec::inputRtmp(_rtmp_packet); }, &_rtmp_packet->buffer);
_rtmp_packet = nullptr;
}, &_rtmp_packet->buffer);
} }
void H265RtmpEncoder::makeVideoConfigPkt() { void H265RtmpEncoder::makeVideoConfigPkt() {
#ifdef ENABLE_MP4 #ifdef ENABLE_MP4
int8_t flags = FLV_CODEC_H265; auto flags = (uint8_t)RtmpVideoCodec::h265;
flags |= (FLV_KEY_FRAME << 4); flags |= ((uint8_t)RtmpFrameType::key_frame << 4);
bool is_config = true;
auto pkt = RtmpPacket::create(); auto pkt = RtmpPacket::create();
// header // header
pkt->buffer.push_back(flags); pkt->buffer.push_back(flags);
pkt->buffer.push_back(!is_config); pkt->buffer.push_back((uint8_t)RtmpH264PacketType::h264_config_header);
// cts // cts
pkt->buffer.append("\x0\x0\x0", 3); pkt->buffer.append("\x0\x0\x0", 3);
......
...@@ -63,18 +63,6 @@ ...@@ -63,18 +63,6 @@
#define CHUNK_AUDIO 6 /*音频chunkID*/ #define CHUNK_AUDIO 6 /*音频chunkID*/
#define CHUNK_VIDEO 7 /*视频chunkID*/ #define CHUNK_VIDEO 7 /*视频chunkID*/
#define FLV_KEY_FRAME 1
#define FLV_INTER_FRAME 2
#define FLV_CODEC_AAC 10
#define FLV_CODEC_H264 7
//金山扩展: https://github.com/ksvc/FFmpeg/wiki
#define FLV_CODEC_H265 12
#define FLV_CODEC_G711A 7
#define FLV_CODEC_G711U 8
//参考学而思网校: https://github.com/notedit/rtmp/commit/6e314ac5b29611431f8fb5468596b05815743c10
#define FLV_CODEC_OPUS 13
namespace mediakit { namespace mediakit {
#if defined(_WIN32) #if defined(_WIN32)
...@@ -181,12 +169,9 @@ public: ...@@ -181,12 +169,9 @@ public:
} }
void clear(); void clear();
bool isVideoKeyFrame() const; bool isVideoKeyFrame() const;
bool isCfgFrame() const; bool isCfgFrame() const;
int getRtmpCodecId() const;
int getMediaType() const;
int getAudioSampleRate() const; int getAudioSampleRate() const;
int getAudioSampleBit() const; int getAudioSampleBit() const;
int getAudioChannel() const; int getAudioChannel() const;
...@@ -269,6 +254,10 @@ private: ...@@ -269,6 +254,10 @@ private:
//根据音频track获取flags //根据音频track获取flags
uint8_t getAudioRtmpFlags(const Track::Ptr &track); uint8_t getAudioRtmpFlags(const Track::Ptr &track);
////////////////// rtmp video //////////////////////////
//https://rtmp.veriskope.com/pdf/video_file_format_spec_v10_1.pdf
// UB [4]; Type of video frame.
enum class RtmpFrameType : uint8_t { enum class RtmpFrameType : uint8_t {
reserved = 0, reserved = 0,
key_frame = 1, // key frame (for AVC, a seekable frame) key_frame = 1, // key frame (for AVC, a seekable frame)
...@@ -278,6 +267,7 @@ enum class RtmpFrameType : uint8_t { ...@@ -278,6 +267,7 @@ enum class RtmpFrameType : uint8_t {
video_info_frame = 5, // video info/command frame video_info_frame = 5, // video info/command frame
}; };
// UB [4]; Codec Identifier.
enum class RtmpVideoCodec : uint8_t { enum class RtmpVideoCodec : uint8_t {
h263 = 2, // Sorenson H.263 h263 = 2, // Sorenson H.263
screen_video = 3, // Screen video screen_video = 3, // Screen video
...@@ -288,12 +278,15 @@ enum class RtmpVideoCodec : uint8_t { ...@@ -288,12 +278,15 @@ enum class RtmpVideoCodec : uint8_t {
h265 = 12, // 国内扩展 h265 = 12, // 国内扩展
}; };
// UI8;
enum class RtmpH264PacketType : uint8_t { enum class RtmpH264PacketType : uint8_t {
h264_config_header = 0, // AVC sequence header(sps/pps) h264_config_header = 0, // AVC or HEVC sequence header(sps/pps)
h264_nalu = 1, // AVC NALU h264_nalu = 1, // AVC or HEVC NALU
h264_end_seq = 2, // AVC end of sequence (lower level NALU sequence ender is not REQUIRED or supported) h264_end_seq = 2, // AVC or HEVC end of sequence (lower level NALU sequence ender is not REQUIRED or supported)
}; };
// https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp.pdf
// UB[4]
enum class RtmpPacketType : uint8_t { enum class RtmpPacketType : uint8_t {
PacketTypeSequenceStart = 0, PacketTypeSequenceStart = 0,
PacketTypeCodedFrames = 1, PacketTypeCodedFrames = 1,
...@@ -321,15 +314,49 @@ enum class RtmpPacketType : uint8_t { ...@@ -321,15 +314,49 @@ enum class RtmpPacketType : uint8_t {
PacketTypeMPEG2TSSequenceStart = 5, PacketTypeMPEG2TSSequenceStart = 5,
}; };
////////////////// rtmp audio //////////////////////////
//https://rtmp.veriskope.com/pdf/video_file_format_spec_v10_1.pdf
// UB [4]; Format of SoundData
enum class RtmpAudioCodec : uint8_t {
/**
0 = Linear PCM, platform endian
1 = ADPCM
2 = MP3
3 = Linear PCM, little endian
4 = Nellymoser 16 kHz mono
5 = Nellymoser 8 kHz mono
6 = Nellymoser
7 = G.711 A-law logarithmic PCM
8 = G.711 mu-law logarithmic PCM
9 = reserved
10 = AAC
11 = Speex
14 = MP3 8 kHz
15 = Device-specific sound
*/
g711a = 7,
g711u = 8,
aac = 10,
opus = 13 // 国内扩展
};
// UI8;
enum class RtmpAACPacketType : uint8_t {
aac_config_header = 0, // AAC sequence header
aac_raw = 1, // AAC raw
};
////////////////////////////////////////////
struct RtmpPacketInfo { struct RtmpPacketInfo {
CodecId codec = CodecInvalid; CodecId codec = CodecInvalid;
bool is_enhanced; bool is_enhanced;
union { union {
struct { struct {
RtmpFrameType frame_type; RtmpFrameType frame_type;
RtmpVideoCodec rtmp_codec; RtmpPacketType pkt_type; // enhanced = true
RtmpPacketType pkt_type; RtmpH264PacketType h264_pkt_type; // enhanced = false
RtmpH264PacketType h264_pkt_type;
} video; } video;
}; };
}; };
......
...@@ -132,7 +132,7 @@ void RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) { ...@@ -132,7 +132,7 @@ void RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) {
case MSG_AUDIO: { case MSG_AUDIO: {
if (!_try_get_audio_track) { if (!_try_get_audio_track) {
_try_get_audio_track = true; _try_get_audio_track = true;
auto codec = AMFValue(pkt->getMediaType()); auto codec = AMFValue(pkt->getRtmpCodecId());
makeAudioTrack(codec, pkt->getAudioSampleRate(), pkt->getAudioChannel(), pkt->getAudioSampleBit(), 0); makeAudioTrack(codec, pkt->getAudioSampleRate(), pkt->getAudioChannel(), pkt->getAudioSampleBit(), 0);
} }
if (_audio_rtmp_decoder) { if (_audio_rtmp_decoder) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论