当前位置:   article > 正文

【Android音视频开发】【031】RTMP写入FLV,H264,AAC文件_android h264 写入文件

android h264 写入文件

通过librtmp将RTMP流转换为FLV文件,H264文件,AAC文件

网上很多RTMP写FLV,都是通过RTMP_Read方法循环读来完成的

但这样得到的FLV文件实际是不规范的,因为它没有FLV Tag Header,只是依靠播放器的自动识别功能来完成播放的

如果把这样的数据,交给一些不够强大的播放器,或者交给解析工具,或程序员编写的代码去使用,大概率是会报错的

传统的FLV大多使用AMFArray来表示AMF2中的属性,但现在也有一些服务器,会使用AMFObject来表示多个属性

一些FLV分析工具,只支持AMFArray格式,用这些工具来解析AMFObject格式的Script Packet就会报错,这是工具问题

接下来我们来讲解正规的处理办法

引用的第三方库

librtmp,用于读取和解析RTMP流数据

RTMP流结构分析

服务器先发过来一个ScriptPacket,然后再发送AudioPacket和VideoPacket,ScriptPacket一般只有一个,用于存储音视频参数

在所有的AudioPacket当中,首个Packet是AudioConfigPacket,用于存储音频参数,AudioConfigPacket一般只有一个,剩下的都是AudioRawDataPacket

在所有的VideoPacket当中,首个Packet是VideoConfigPacket,用于存储视频参数,VideoConfigPacket一般只有一个,剩下的都是VideoRawDataPacket

RTMPPacket.Body中的数据,和FLVTag.Body中的数据是完全一样的,可以直接写入FLV文件,如果想要写入H264和AAC,则需要进一步拆解

FLV文件写入流程

FLV文件是由FLVHeader + TagSize0 + Tag1 + TagSize1 + … + TagN + TagSizeN这些部分组成的,我们逐个写入这些部分,就能生成FLV文件

FLVHeader由Signature+Version+DataTye+HeaderLength等字节组成,可以手动逐个字节拼接

TagSize0占4个字节,所有字节都是0

TagN就是第N个RTMPPacket的Body部分,直接拷贝即可

TagSizeN是第N个RTMPPacket的BodySize,占4个字节

以上所有部分拼接完成,直接写入文件,即可得到一个可播放的FLV文件

H264文件写入流程

H264文件的格式一般为SPS帧 + PPS帧 + I帧 + 若干P帧 + … + I帧 + 若干P帧,SPS+PPS一般出现在I帧前面,可能一次,也可能多次

VideoConfigPacket的Body格式一般为CodecId + FrameType + DataType + 若干SPS + 若干PPS

VideoRawDataPacket的Body格式一般为CodecId + FrameType + DataType + H264RawData(IFrame/PFrame)

SPS部分的格式为SpsNum + SpsLength + SpsData + SpsLength + SpsData + …,PPS同理

VideoPacket的第1个字节,可以判断出编码类型(PCM/H264等)和帧类型(关键帧/参照帧),通过第2个字节,可以判断出数据类型(视频参数/视频裸数据)

如果是Packet中存储的是VideoConfig,则按照VideoConfigPacket的格式,解析出SPSData和PPSData数据,写入H264文件

在H264文件中,SPS帧+PPS帧的格式为,StartCode + SPS + StartCode + PPS
如果是Packet中存储的是VideoRawData,则按照VideoRawDataPacket的格式,解析出IFrame/PFrame数据,写入H264文件

在H264文件中,IFrame/PFrame的格式为,StartCode + H264RawData

以上字节按顺序写入文件,就能得到一个可播放的H264文件

注意,H264存的只是视频裸数据,不是用于发布的文件格式,它是没有时间戳的,只有像FLV这样容器类的文件,才会有时间戳

AAC文件写入流程

AAC文件的格式一般为AAC头 + AAC裸数据 + AAC头 + AAC裸数据 + …

AAC头有两种格式,一种是ADTS(每个裸数据前都有Header,适合流传输),一种是ADIF(只有一个统一的AAC头,在文件最前面,适合文件传输),我们这里用的是ADTS

AudioPacket的Body格式为AudioInfo + PacketType + ConfigData/RawData

通过AudioConfigPacket中的ConfigData,就可以计算出ADTS,具体请看代码,字段较多,但逻辑很简单

AudioRawDataPacket中的RawData,就是AAC文件中的裸数据,直接拷贝即可

所有裸数据单元的AAC头都是一样的,按顺序写入文件,就能得到一个可播放的FLV文件

实现代码


	//RtmpPlayer.cpp

	#include "base/Bases.h"
	#include "RtmpConfigParser.h"
	
	#include "rtmp.h"
	
	RTMP *rtmp = nullptr;
	
	char *url = nullptr;
	
	pthread_t pidPush = 0;
	
	bool playing = false;
	
	FILE *h264File;
	FILE *aacFile;
	FILE *flvFile;
	
	AudioSpecificConfig audioSpecificConfig = {};
	
	bool _prepare();
	
	bool _read();
	
	//传统方法保存RTMP为FLV文件
	//这种方法是不规范的,本质上是直接保存RTMP流,而不是FLV文件
	//因为一些专业的播放器,能够智能识别格式,所以才能正常播放
	//如果交给分析工具,或者开发者自己写的播放客户端,大概率是会出问题的
	void writeToFlv() {
	    //写FLV
	    char *buffer = (char *) malloc(1024 * 1024);
	    while (playing) {
	        int len = RTMP_Read(rtmp, buffer, 1024 * 1024);
	        if (len > 0)
	            fwrite(buffer, len, 1, flvFile);
	    }
	    fflush(flvFile);
	}
	
	//写FLV-TAG
	void writeFlvTag(uint8_t tagType, uint32_t tagSize, uint32_t bodySize, uint32_t timestamp, char *body) {
	    unsigned char *pBodySize = (unsigned char *) &bodySize;
	    unsigned char *pTimestamp = (unsigned char *) &timestamp;
	    unsigned char *pTagSize = (unsigned char *) &tagSize;
	    const unsigned char tagHeader[] = {
	            tagType, //TAG Type,0x08表示音频,0x09表示视频,0x12表示脚本
	            *(pBodySize + 2), *(pBodySize + 1), *pBodySize, //Body Size,按字节拷贝bodySize
	            *(pTimestamp + 2), *(pTimestamp + 1), *pTimestamp, //Timestamp,按字节拷贝timestamp
	            0x00, //Timestamp Extended,时间戳扩展
	            0x00, 0x00, 0x00 //StreamID,永远为0
	    };
	    const unsigned char tagSizeBytes[] = {
	            *(pTagSize + 3), *(pTagSize + 2), *(pTagSize + 1), *pTagSize
	    };
	    fwrite(tagHeader, 11, 1, flvFile);
	    fwrite(body, bodySize, 1, flvFile);
	    fwrite(tagSizeBytes, 4, 1, flvFile);
	    fflush(flvFile);
	}
	
	//JNI接口加载完毕
	//System.loadLibrary函数被调用时,会触发此方法
	extern "C" JNIEXPORT jint JNICALL
	JNI_OnLoad(JavaVM *vm, void *reserved) {
	    JNI::jvm = vm;
	    //取流的同时,将流媒体数据同时写入三个文件
	    //H264视频文件,AAC音频文件,FLV混合文件
	    h264File = fopen("sdcard/1.h264", "wb");
	    aacFile = fopen("sdcard/1.aac", "wb");
	    flvFile = fopen("sdcard/1.flv", "wb");
	    return JNI_VERSION_1_6;
	}
	
	//JNI接口被JVM回收时,会触发此方法
	extern "C" JNIEXPORT void JNICALL
	JNI_OnUnload(JavaVM *vm, void *reserved) {
	    //删除Java回调
	    delete Pusher::java;
	    Pusher::java = nullptr;
	}
	
	extern "C"
	JNIEXPORT void JNICALL
	Java_easing_android_media_RtmpPlayer_RtmpPlayer_native_1initialize(JNIEnv *env, jobject interface, jstring tag) {
	    //设置日志标签
	    JNIPrivate::TAG = JNI::toConstChar(tag);
	    //记录JNI环境
	    JNI::env = env;
	    JNI::interface = env->NewGlobalRef(interface);
	    //创建JavaCaller,用于回调Java层方法
	    Pusher::java = new JavaCaller();
	    //stdio重定向到logcat
	    JNI::stdioToLogcat();
	    std::cout << "initialized" << std::endl;
	}
	
	extern "C"
	JNIEXPORT void JNICALL
	Java_easing_android_media_RtmpPlayer_RtmpPlayer_native_1prepare(JNIEnv *env, jobject interface, jstring jUrl) {
	    //jstring地址转char*地址
	    const char *constUrl = JNI::toConstChar(jUrl);
	    url = JNI::toChar(constUrl);
	    //准备播放环境
	    playing = false;
	    bool ret = _prepare();
	    if (!ret) {
	        std::cout << "prepare fail" << std::endl;
	        return;
	    }
	    playing = true;
	    //创建推流线程
	    pthread_create(&pidPush, nullptr, [](void *context) -> void * {
	        _read();
	        return nullptr;
	    }, nullptr);
	}
	
	extern "C"
	JNIEXPORT void JNICALL
	Java_easing_android_media_RtmpPlayer_RtmpPlayer_native_1release(JNIEnv *env, jobject interface) {
	    playing = false;
	    //等待推流结束
	    pthread_join(pidPush, nullptr);
	    //销毁RTMP
	    if (rtmp) {
	        RTMP_Close(rtmp);
	        RTMP_Free(rtmp);
	    }
	    //销毁URL
	    delete (url);
	}
	
	//连接服务器
	bool _prepare() {
	
	    //连接RTMP
	    rtmp = RTMP_Alloc();
	    RTMP_Init(rtmp);
	    rtmp->Link.timeout = 30;
	    RTMP_SetupURL(rtmp, url);
	    rtmp->Link.lFlags |= RTMP_LF_LIVE;
	    RTMP_SetBufferMS(rtmp, 1 * 1000);
	    RTMP_Connect(rtmp, nullptr);
	    RTMP_ConnectStream(rtmp, 0);
	
	    return true;
	}
	
	//读取流数据,并保存为H264和AAC
	bool _read() {
	    bool ret = true;
	    RTMPPacket packet = {};
	
	    //写FLV文件头
	    const char flvHeader[] = {
	            'F', 'L', 'V', //固定签名
	            0x01, //FLV版本号
	            0x05, //数据类型,0x01表示视频,0x04表示音频,0x05表示音视频都有
	            0x00, 0x00, 0x00, 0x09, //FLV头长度
	            0x00, 0x00, 0x00, 0x00 //首个TAG前的固定Size,永远为0
	    };
	    fwrite(flvHeader, 13, 1, flvFile);
	    fflush(flvFile);
	
	    //循环读取流数据
	    while (playing) {
	        ret = RTMP_ReadPacket(rtmp, &packet);
	        if (!ret) continue;
	        ret = RTMPPacket_IsReady(&packet);
	        if (!ret) continue;
	        if (!packet.m_nBodySize) continue;
	
	        //读取包信息
	        uint8_t headerType = packet.m_headerType;
	        uint8_t tagType = packet.m_packetType;
	        uint32_t bodySize = packet.m_nBodySize;
	        uint32_t tagSize = bodySize + 11;
	        uint32_t timestamp = packet.m_nTimeStamp;
	        char *body = packet.m_body;
	
	        //处理脚本包
	        if (packet.m_packetType == RTMP_PACKET_TYPE_INFO && RTMP_ClientPacket(rtmp, &packet)) {
	            //写入FLV文件
	            writeFlvTag(0x12, tagSize, bodySize, timestamp, body);
	            //解析媒体参数
	            MediaMetadata metadata = {};
	            parseScriptTag(body, bodySize, metadata);
	            std::string strMetadata;
	            metadata.toString(strMetadata);
	            std::cout << "Media Metadata" << std::endl << strMetadata << std::endl;
	        }
	
	        //处理音频包
	        if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO && RTMP_ClientPacket(rtmp, &packet)) {
	            //写入FLV文件
	            writeFlvTag(0x08, tagSize, bodySize, timestamp, body);
	            //解析参数
	            unsigned short header1 = (body[0] & 0xF0) >> 4; //前4位表示音频格式,1表示PCM,2表示MP3,10表示AAC
	            unsigned short header2 = body[1]; //0x00表示AAC解码配置,0x01表示AAC裸数据
	            bool isAAC = header1 == 0x0A;
	            bool isConfigPacket = header2 == 0x00;
	            //不是AAC
	            if (!isAAC) {
	                std::cout << "audio format is not aac " << header1 << std::endl;
	                continue;
	            }
	            //解包AAC数据,存入AAC文件
	            if (isConfigPacket) {
	                //从RTMPPacketBody中解析出音频参数
	                AudioParam audioParam = {};
	                audioParam.format = (body[0] & 0xF0) >> 4; //音频格式,1表示PCM,2表示MP3,10表示AAC
	                audioParam.sampleRate = (body[0] & 0x0C) >> 2; //音频采样率,1表示11KHz,2表示22kHz,3表示44kHz
	                audioParam.bitDepth = (body[0] & 0x02) >> 1; //音频采样精度,0表示8位,1表示16位
	                audioParam.channels = body[0] & 0x01; //0表示单声道,1表示多声道
	                audioParam.packetType = body[1]; //0表示音频配置包,1表示音频裸数据
	                audioParam.audioSpecificConfig = ((body[2] & 0xFF) << 8) + (0x00FF & body[3]);
	                //解析自定义部分数据
	                int aotSize = packet.m_nBodySize - 4;
	                uint8_t *aotSpecificConfig = (uint8_t *) malloc(aotSize);
	                memcpy(aotSpecificConfig, body + 4, aotSize);
	                audioParam.aotSpecificConfig = aotSpecificConfig;
	                //提取AudioSpecificConfig,用于生成ADTS(AAC头信息)
	                //想要理解此部分代码,可以找个标准的FLV文件
	                //然后用FLVAnalyzer软件解析下包结构,对照解析结果逐个字节理解即可
	                audioSpecificConfig.audioObjectType = (audioParam.audioSpecificConfig & 0xF800) >> 11; //AudioObjectType占5位
	                audioSpecificConfig.sampleFrequencyIndex = (audioParam.audioSpecificConfig & 0x0780) >> 7; //SampleFrequencyIndex占4位
	                audioSpecificConfig.channelConfiguration = (audioParam.audioSpecificConfig & 0x78) >> 3; //ChannelConfiguration占4位
	                audioSpecificConfig.frameLengthFlag = (audioParam.audioSpecificConfig & 0x04) >> 2; //FrameLengthFlag占1位
	                audioSpecificConfig.dependOnCoreCoder = (audioParam.audioSpecificConfig & 0x02) >> 1; //DependOnCoreCoder占1位
	                audioSpecificConfig.extensionFlag = audioParam.audioSpecificConfig & 0x01; //ExtensionFlag占1位
	            } else {
	                //创建ADTS
	                char adts[7] = {};
	                createADTS(audioSpecificConfig, packet.m_nBodySize - 2 + 7, adts); //除掉AudioBody的前两个参数字节,再加上ADTS的7个字节
	                //写入ADTS到AAC文件
	                fwrite(adts, 7, 1, aacFile);
	                fflush(aacFile);
	                //写入AAC裸数据到AAC文件
	                fwrite(packet.m_body + 2, packet.m_nBodySize - 2, 1, aacFile);
	                fflush(aacFile);
	            }
	        }
	
	        //处理视频包
	        if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO && RTMP_ClientPacket(rtmp, &packet)) {
	            //写入FLV文件
	            writeFlvTag(0x09, tagSize, bodySize, timestamp, body);
	            //解析参数
	            unsigned short header1 = body[0]; //高4位表示帧类型,低4位表示解码器,0x10表示关键帧,0x20表示参照帧,0x07表示AVC
	            unsigned short header2 = body[1]; //0x00表示AVC解码配置,0x01表示AVC裸数据
	            bool isAvc = (header1 & 0x0F) == 0x07;
	            bool isConfigPacket = header2 == 0x00;
	            //不是AVC
	            if (!isAvc) {
	                std::cout << "video format is not avc " << header1 << std::endl;
	                continue;
	            }
	            //H264固定起始码
	            const char start_code[4] = {0x00, 0x00, 0x00, 0x01};
	            //解包H264数据,存入H264文件
	            if (isConfigPacket) {
	                //AVC-Header
	                //从RTMPPacket中解析SPS
	                int sps_num = body[10] & 0x1F;
	                int sps_index = 11;
	                while (sps_index <= 10 + sps_num) {
	                    int sps_len = (body[sps_index] & 0xFF) << 8 | (body[sps_index + 1] & 0xFF);
	                    sps_index += 2;
	                    //写入H264文件
	                    fwrite(start_code, 1, 4, h264File);
	                    fwrite(body + sps_index, 1, sps_len, h264File);
	                    fflush(h264File);
	                    sps_index += sps_len;
	                }
	                //从RTMPPacket中解析PPS
	                int pps_num = body[sps_index] & 0x1F;
	                int pps_index = sps_index + 1;
	                while (pps_index <= sps_index + pps_num) {
	                    int pps_len = (body[pps_index] & 0xFF) << 8 | body[pps_index + 1] & 0xFF;
	                    pps_index += 2;
	                    //写入H264文件
	                    fwrite(start_code, 1, 4, h264File);
	                    fwrite(body + pps_index, 1, pps_len, h264File);
	                    fflush(h264File);
	                    pps_index += pps_len;
	                }
	            } else {
	                //AVC-NALU
	                int nalu_index = 5;
	                while (nalu_index < packet.m_nBodySize) {
	                    int nalu_len = (body[nalu_index] & 0x000000FF) << 24 | (body[nalu_index + 1] & 0x000000FF) << 16 | (body[nalu_index + 2] & 0x000000FF) << 8 | body[nalu_index + 3] & 0x000000FF;
	                    nalu_index = nalu_index + 4;
	                    //写入H264文件
	                    fwrite(start_code, 1, 4, h264File);
	                    fwrite(body + nalu_index, 1, nalu_len, h264File);
	                    fflush(h264File);
	                    nalu_index += nalu_len;
	                }
	            }
	        }
	
	        //释放Packet
	        RTMPPacket_Free(&packet);
	    }
	    return ret;
	}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309

	//RtmpConfigParser.h

	#include <iostream>
	#include <string.h>
	#include <stdio.h>
	#include <sstream>
	
	#include "rtmp/rtmp_sys.h"
	#include "rtmp/log.h"
	
	struct AudioParam {
	    uint8_t format;
	    uint8_t sampleRate;
	    uint8_t bitDepth;
	    uint8_t channels;
	    uint8_t packetType;
	    uint16_t audioSpecificConfig;
	    uint8_t * aotSpecificConfig; //可变部分,用于存储自定义数据,长度为FlvTagSize-固定部分长度
	};
	
	struct AudioSpecificConfig {
	    uint8_t audioObjectType;
	    uint8_t sampleFrequencyIndex;
	    uint8_t channelConfiguration;
	    uint8_t frameLengthFlag;
	    uint8_t dependOnCoreCoder;
	    uint8_t extensionFlag;
	    uint8_t *AOTSpecificConfig = nullptr; //长度可变,DataSize-固定部分长度,就是这部分的长度
	};
	
	struct MediaMetadata {
	
	    public:
	
	    int audioCodecId = 0; //音频解码器
	    int audioSampleRate = 44100; //音频采样率
	    int audioSampleSize = 16; //音频采样精度
	    int audioChannelStereo = 1; //音频声道,0单声道,1多声道
	    int videoCodecId = 0; //视频解码器
	    int videoWidth = 800; //视频宽度
	    int videoHeight = 600; //视频高度
	    int videoFrameRate = 30; //视频帧率
	    int fileSize = 0; //文件大小
	
	    void toString(std::string &str) {
	        std::ostringstream os;
	        os << "audioCodecId" << ":" << audioCodecId << std::endl;
	        os << "audioSampleRate" << ":" << audioSampleRate << std::endl;
	        os << "audioSampleSize" << ":" << audioSampleSize << std::endl;
	        os << "audioChannelStereo" << ":" << audioChannelStereo << std::endl;
	        os << "videoCodecId" << ":" << videoCodecId << std::endl;
	        os << "videoWidth" << ":" << videoWidth << std::endl;
	        os << "videoHeight" << ":" << videoHeight << std::endl;
	        os << "videoFrameRate" << ":" << videoFrameRate << std::endl;
	        os << "fileSize" << ":" << fileSize << std::endl;
	        str = os.str();
	    }
	};
	
	void copyProperty(MediaMetadata &metadata, AMFObjectProperty *property);
	
	//根据AudioSpecificConfig生成ADTS
	//FrameSize=ADTS+AACRaw,即Header+Body,整帧长度
	void createADTS(AudioSpecificConfig audioSpecificConfig, unsigned short frameSize, char *dst) {
	    uint8_t adts[7] = {};
	    //Syncword,12位,固定值0xFFF
	    adts[0] = 0xFF;
	    adts[1] = 0xF0;
	    //ID,1位,0表示MPEG4,1表示MPEG2
	    adts[1] |= 0x00;
	    //Layer,2位,永远是0
	    adts[1] |= 0x00;
	    //ProtectionAbsent,1位,是否CRC校验,0表示不校验,1表示校验
	    adts[1] |= 0x01;
	    //Profile,2位,等于audioObjectType-1
	    adts[2] = (audioSpecificConfig.audioObjectType - 1) << 6;
	    //SampleFrequencyIndex,4位,采样率,4表示44kHz
	    adts[2] |= (audioSpecificConfig.sampleFrequencyIndex & 0x0F) << 2;
	    //PrivateBit,1位,私有位
	    adts[2] |= (0 << 1);
	    //ChannelConfiguration,3位,声道数
	    adts[2] |= (audioSpecificConfig.channelConfiguration & 0B0100) >> 2;
	    adts[3] = (audioSpecificConfig.channelConfiguration & 0B0011) << 6;
	    //OriginalCopy,1位
	    adts[3] |= (0 << 5);
	    //Home,1位
	    adts[3] |= (0 << 4);
	    //CopyrightedIdBit,1位,永远是0
	    adts[3] |= (0 << 3);
	    //CopyrightedIdStart,1位,永远是0
	    adts[3] |= (0 << 2);
	    //AACFrameLength,13位,ADTS和裸数据总长度
	    adts[3] |= (frameSize & 0x1800) >> 11;
	    adts[4] = (frameSize & 0x07F8) >> 3;
	    adts[5] = (frameSize & 0x07) << 5;
	    //ADTSBufferFullness,11位,永远是0x7FF,表示码率可变
	    adts[5] |= 0x1F;
	    adts[6] = 0xFC;
	    //RawDataBlockNumber,2位,表示该包中包含多少个原始帧,填0即可
	    adts[6] |= 0B0000;
	    memcpy(dst, adts, 7);
	}
	
	//解析ScriptTag
	void parseScriptTag(char *body, int bodySize, MediaMetadata &metadata) {
	    //解析AMFObject
	    AMFObject obj;
	    AMF_Decode(&obj, body, bodySize, false);
	    AMF_Dump(&obj);
	
	    //遍历所有属性
	    for (int i = 0; i < obj.o_num; i++) {
	        AMFObjectProperty *property = AMF_GetProp(&obj, NULL, i);
	        if (!property)
	            continue;
	        //普通属性
	        if (property->p_type != AMF_OBJECT) {
	            copyProperty(metadata, property);
	            continue;
	        }
	        //嵌套属性
	        AMFObject subObject;
	        AMFProp_GetObject(property, &subObject);
	        for (int m = 0; m < subObject.o_num; m++) {
	            AMFObjectProperty *subProperty = AMF_GetProp(&subObject, NULL, m);
	            copyProperty(metadata, subProperty);
	        }
	    }
	}
	
	//拷贝Script属性到Metadata
	void copyProperty(MediaMetadata &metadata, AMFObjectProperty *property) {
	    if (!property || !property->p_name.av_val)
	        return;
	    //去除属性名后面的特殊字符
	    AMFDataType propertyType = property->p_type;
	    std::string strPropertyName = property->p_name.av_val;
	    strPropertyName.erase(remove_if(strPropertyName.begin(), strPropertyName.end(), [](char c) -> bool {
	        return c == '\U00000002';
	    }), strPropertyName.end());
	    const char *propertyName = strPropertyName.c_str();
	    std::cout << "get property: " << propertyName << std::endl;
	    //获取属性值
	    if (strcmp("stereo", propertyName) == 0) {
	        bool value = AMFProp_GetBoolean(property);
	        metadata.audioChannelStereo = value;
	    } else if (strcmp("width", propertyName) == 0) {
	        double value = AMFProp_GetNumber(property);
	        metadata.videoWidth = value;
	    } else if (strcmp("height", propertyName) == 0) {
	        double value = AMFProp_GetNumber(property);
	        metadata.videoHeight = value;
	    } else if (strcmp("framerate", propertyName) == 0) {
	        double value = AMFProp_GetNumber(property);
	        metadata.videoFrameRate = value;
	    } else if (strcmp("videocodecid", propertyName) == 0) {
	        double value = AMFProp_GetNumber(property);
	        metadata.videoCodecId = value;
	    } else if (strcmp("audiosamplerate", propertyName) == 0) {
	        double value = AMFProp_GetNumber(property);
	        metadata.audioSampleRate = value;
	    } else if (strcmp("audiosamplesize", propertyName) == 0) {
	        double value = AMFProp_GetNumber(property);
	        metadata.audioSampleSize = value;
	    } else if (strcmp("audiocodecid", propertyName) == 0) {
	        double value = AMFProp_GetNumber(property);
	        metadata.audioCodecId = value;
	    } else if (strcmp("filesize", propertyName) == 0) {
	        double value = AMFProp_GetNumber(property);
	        metadata.fileSize = value;
	    }
	}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174

Demo下载

RtmpPlayer.zip

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/392043?site
推荐阅读
相关标签
  

闽ICP备14008679号