├── video_file_format_spec_v10_1.pdf ├── README.md ├── FLVWriter.h └── FLVWriter.c /video_file_format_spec_v10_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tvirus/flv-writer/HEAD/video_file_format_spec_v10_1.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flv-writer 2 | 3 | FLV_GetStreamHeader() 返回 FLV 文件的 FileTag、ScriptTag 和 AacConfigTag 4 | 5 | FLV_Get264Tag_SPS() 返回 264SpsTag, 6 | 7 | FLV_Get264Tag() 和 FLV_GetAACTag() 返回一般的 264Tag 和 AACTag 8 | 9 | 10 | **注意:** 11 | 12 | 1. 在推 HTTP-FLV 流的开始,需要先把 FLV 文件的 FileTag、ScriptTag、AacConfigTag 和 264SpsTag 发出去(存 FLV 文件时也是需要先把这些写进去),其中前三个在 FLV_GetStreamHeader() 函数中一并返回了 13 | 14 | 2. HTTP 的 chunk 应该需要和 Tag 对齐,一个 chunk 发送半个 Tag 时好像解析不出来(web用的flv.js) 15 | 16 | 3. FLV_Get264Tag_SPS() 的入参是 SPS 和 PPS 帧,并且需要第一个是 SPS,第二个是 PPS,否则要自己修改一下代码 17 | 18 | 4. FLV_Get264Tag() 在找到入参中第一个 I 帧或 P 帧后就结束,如果多个 I 帧 或 P 帧一起传入,需要自己修改一下代码 19 | 20 | 5. 每个264帧的开始需要有四个字节 0x00000001 的 start code,否则要自己改一下分割函数 Find264Nalu() 21 | 22 | 6. 一般编码器生成的 SPS、PPS 和 I 帧都是在一个包里的,所以在开始时需要以这个包作为入参调用 FLV_Get264Tag_SPS() 和 FLV_Get264Tag() 两个函数 23 | 24 | 7. 传入的 AAC 数据需要去掉前7个或9个字节的 adts 头 25 | 26 | 8. 把生成的数据存到文件后就是 FLV 视频文件,不过主要针对实时视频,所以ScriptTag中未写文件大小和持续时间 27 | 28 | 9. 时间戳要从0开始 29 | -------------------------------------------------------------------------------- /FLVWriter.h: -------------------------------------------------------------------------------- 1 | #ifndef _FLVWRITER_H_ 2 | #define _FLVWRITER_H_ 3 | 4 | 5 | typedef unsigned char u8; 6 | typedef signed char s8; 7 | typedef unsigned short u16; 8 | typedef signed short s16; 9 | typedef unsigned int u32; 10 | typedef signed int s32; 11 | typedef unsigned long long u64; 12 | typedef signed long long s64; 13 | 14 | 15 | 16 | /* MPEG-4 Audio Object Types */ 17 | /* http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio */ 18 | #define MP4_AUDIO_TYPE_INVALID 0 19 | #define MP4_AUDIO_TYPE_AAC_MAIN 1 20 | #define MP4_AUDIO_TYPE_AAC_LC 2 21 | #define MP4_AUDIO_TYPE_AAC_SSR 3 22 | #define MP4_AUDIO_TYPE_AAC_LD 23 23 | 24 | 25 | typedef struct 26 | { 27 | u8 VideoFlag; /* 是否有视频数据 */ 28 | u8 AudioFlag; /* 是否有音频数据 */ 29 | 30 | /* video */ 31 | u16 Width; 32 | u16 Height; 33 | u8 Framerate; 34 | //u8 Videocodecid; 35 | 36 | 37 | /* audio */ 38 | //u8 SampleSize; /* 16 or 8 */ 39 | u16 SampleRate; /* Hz */ 40 | u8 Stereo; /* 0:mono 1:stereo */ 41 | u8 AudioType; /* aac 格式的具体类型, MPEG-4 Audio Object Types */ 42 | u8 Channel; /* 通道个数 */ 43 | 44 | }T_FLVConfig; 45 | 46 | 47 | 48 | s32 FLV_GetStreamHeader(T_FLVConfig *pConf, u8 *pBuf, u32 Size); 49 | s32 FLV_Get264Tag_SPS(u8 *pBuf, u32 BufSize, u8 *pH264Data, u32 DataSize); 50 | s32 FLV_Get264Tag(u8 *pBuf, u32 BufSize, u8 *pH264Data, u32 DataSize, u32 TimeStamp); 51 | s32 FLV_GetAACTag(u8 *pBuf, u32 BufSize, u8 *pAACData, u32 DataSize, u32 TimeStamp); 52 | 53 | 54 | void* FLV_CreateFile(const char *pFileName, T_FLVConfig *pConf); 55 | s32 FLV_Write264(void *pFileInfo, const u8 *pH264Data, u32 Size, u32 TimeStamp); 56 | s32 FLV_WriteAAC(void *pFileInfo, const u8 *pAACData, u32 Size, u32 TimeStamp); 57 | void FLV_CloseFile(void *pFileInfo); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /FLVWriter.c: -------------------------------------------------------------------------------- 1 | #include "FLVWriter.h" 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | /* file */ 8 | #define FLV_TAG_TYPE_AUDIO 8 9 | #define FLV_TAG_TYPE_VIDEO 9 10 | #define FLV_TAG_TYPE_SCRIPT 18 11 | 12 | 13 | /* video */ 14 | #define FLV_V_FRAME_TYPE_I 1 15 | #define FLV_V_FRAME_TYPE_P 2 16 | 17 | #define FLV_V_CODECID_AVC 7 18 | 19 | #define FLV_AVCPACKET_TYPE_SPS 0 20 | #define FLV_AVCPACKET_TYPE_NALU 1 21 | 22 | 23 | 24 | /* audio */ 25 | #define FLV_A_FORMATE_AAC 10 26 | 27 | #define FLV_A_SAMPLE_RATE_5K 0 28 | #define FLV_A_SAMPLE_RATE_11K 1 29 | #define FLV_A_SAMPLE_RATE_22K 2 30 | #define FLV_A_SAMPLE_RATE_44K 3 31 | 32 | #define FLV_A_SAMPLE_SIZE_8b 0 33 | #define FLV_A_SAMPLE_SIZE_16b 1 34 | 35 | #define FLV_A_SOUND_TYPE_MONO 0 36 | #define FLV_A_SOUND_TYPE_STEREO 1 37 | 38 | #define FLV_AACPACKET_TYPE_HEADER 0 39 | #define FLV_AACPACKET_TYPE_RAW 1 40 | 41 | 42 | /* script */ 43 | #define FLV_STRING_METADATA "onMetaData" 44 | #define FLV_STRING_METADATA_LEN (sizeof(FLV_STRING_METADATA) - 1) 45 | #define FLV_STRING_DURATION "duration" 46 | #define FLV_STRING_DURATION_LEN (sizeof(FLV_STRING_DURATION) - 1) 47 | #define FLV_STRING_WIDTH "width" 48 | #define FLV_STRING_WIDTH_LEN (sizeof(FLV_STRING_WIDTH) - 1) 49 | #define FLV_STRING_HEIGHT "height" 50 | #define FLV_STRING_HEIGHT_LEN (sizeof(FLV_STRING_HEIGHT) - 1) 51 | #define FLV_STRING_FRAMERATE "framerate" 52 | #define FLV_STRING_FRAMERATE_LEN (sizeof(FLV_STRING_FRAMERATE) - 1) 53 | #define FLV_STRING_VCODECID "videocodecid" 54 | #define FLV_STRING_VCODECID_LEN (sizeof(FLV_STRING_VCODECID) - 1) 55 | #define FLV_STRING_VDATARATE "videodatarate" 56 | #define FLV_STRING_VDATARATE_LEN (sizeof(FLV_STRING_VDATARATE) - 1) 57 | #define FLV_STRING_ADATARATE "audiodatarate" 58 | #define FLV_STRING_ADATARATE_LEN (sizeof(FLV_STRING_ADATARATE) - 1) 59 | #define FLV_STRING_ASAMPLERATE "audiosamplerate" 60 | #define FLV_STRING_ASAMPLERATE_LEN (sizeof(FLV_STRING_ASAMPLERATE) - 1) 61 | #define FLV_STRING_ASAMPLESIZE "audiosamplesize" 62 | #define FLV_STRING_ASAMPLESIZE_LEN (sizeof(FLV_STRING_ASAMPLESIZE) - 1) 63 | #define FLV_STRING_STEREO "stereo" 64 | #define FLV_STRING_STEREO_LEN (sizeof(FLV_STRING_STEREO) - 1) 65 | #define FLV_STRING_ACODECID "audiocodecid" 66 | #define FLV_STRING_ACODECID_LEN (sizeof(FLV_STRING_ACODECID) - 1) 67 | #define FLV_STRING_DATE "creationdate" 68 | #define FLV_STRING_DATE_LEN (sizeof(FLV_STRING_DATE) - 1) 69 | #define FLV_STRING_AUTHOR "author" 70 | #define FLV_STRING_AUTHOR_LEN (sizeof(FLV_STRING_AUTHOR) - 1) 71 | 72 | #define FLV_SCRIPT_VALUE_TYPE_NUM 0 73 | #define FLV_SCRIPT_VALUE_TYPE_BOOL 1 74 | #define FLV_SCRIPT_VALUE_TYPE_STRING 2 75 | #define FLV_SCRIPT_VALUE_TYPE_OBJECT 3 76 | 77 | 78 | 79 | 80 | 81 | #define WRITE_BUF(pDest, DestSize, pSrc, SrcSize) \ 82 | do \ 83 | { \ 84 | if ((SrcSize) > DestSize) \ 85 | return -1; \ 86 | memcpy((void*)pDest, (void*)(pSrc), (size_t)(SrcSize)); \ 87 | pDest = ((u8*)pDest) + (SrcSize); \ 88 | DestSize -= (SrcSize); \ 89 | }while(0) 90 | 91 | #define WRITE_STRING(pDest, DestSize, pSrc, SrcSize) \ 92 | do \ 93 | { \ 94 | if ((SrcSize) + 2 > DestSize) \ 95 | return -1; \ 96 | ((u8*)pDest)[0] = 0xff & (((u16)(SrcSize)) >> 8); \ 97 | ((u8*)pDest)[1] = 0xff & ((u16)(SrcSize)); \ 98 | pDest = ((u8*)pDest) + 2; \ 99 | DestSize -= 2; \ 100 | memcpy((void*)pDest, (void*)(pSrc), (size_t)(SrcSize)); \ 101 | pDest = ((u8*)pDest) + (SrcSize); \ 102 | DestSize -= (SrcSize); \ 103 | }while(0) 104 | 105 | /* 自动转成大端 */ 106 | #define WRITE_U32(pDest, Size, Value) \ 107 | do \ 108 | { \ 109 | if (4 > Size) \ 110 | return -1; \ 111 | ((u8*)pDest)[0] = 0xff & (((u32)(Value)) >> 24); \ 112 | ((u8*)pDest)[1] = 0xff & (((u32)(Value)) >> 16); \ 113 | ((u8*)pDest)[2] = 0xff & (((u32)(Value)) >> 8); \ 114 | ((u8*)pDest)[3] = 0xff & ((u32)(Value)); \ 115 | pDest = ((u8*)pDest) + 4; \ 116 | Size -= 4; \ 117 | }while(0) 118 | 119 | #define WRITE_U24(pDest, Size, Value) \ 120 | do \ 121 | { \ 122 | if (3 > Size) \ 123 | return -1; \ 124 | ((u8*)(pDest))[0] = 0xff & (((u32)(Value)) >> 16); \ 125 | ((u8*)(pDest))[1] = 0xff & (((u32)(Value)) >> 8); \ 126 | ((u8*)(pDest))[2] = 0xff & ((u32)(Value)); \ 127 | pDest = ((u8*)pDest) + 3; \ 128 | Size -= 3; \ 129 | }while(0) 130 | 131 | #define WRITE_U16(pDest, Size, Value) \ 132 | do \ 133 | { \ 134 | if (2 > Size) \ 135 | return -1; \ 136 | ((u8*)(pDest))[0] = 0xff & (((u16)(Value)) >> 8); \ 137 | ((u8*)(pDest))[1] = 0xff & ((u16)(Value)); \ 138 | pDest = ((u8*)pDest) + 2; \ 139 | Size -= 2; \ 140 | }while(0) 141 | 142 | #define WRITE_U8(pDest, Size, Value) \ 143 | do \ 144 | { \ 145 | if (1 > Size) \ 146 | return -1; \ 147 | ((u8*)pDest)[0] = (u8)(Value); \ 148 | pDest = ((u8*)pDest) + 1; \ 149 | Size -= 1; \ 150 | }while(0) 151 | 152 | #define WRITE_DOUBLE(pDest, Size, Var) \ 153 | do \ 154 | { \ 155 | if (8 > Size) \ 156 | return -1; \ 157 | ((u8*)(pDest))[0] = 0xff & ((*(u64*)(&(Var))) >> 56); \ 158 | ((u8*)(pDest))[1] = 0xff & ((*(u64*)(&(Var))) >> 48); \ 159 | ((u8*)(pDest))[2] = 0xff & ((*(u64*)(&(Var))) >> 40); \ 160 | ((u8*)(pDest))[3] = 0xff & ((*(u64*)(&(Var))) >> 32); \ 161 | ((u8*)(pDest))[4] = 0xff & ((*(u64*)(&(Var))) >> 24); \ 162 | ((u8*)(pDest))[5] = 0xff & ((*(u64*)(&(Var))) >> 16); \ 163 | ((u8*)(pDest))[6] = 0xff & ((*(u64*)(&(Var))) >> 8); \ 164 | ((u8*)(pDest))[7] = 0xff & (*(u64*)(&(Var))); \ 165 | pDest = ((u8*)pDest) + 8; \ 166 | Size -= 8; \ 167 | }while(0) 168 | 169 | 170 | #pragma pack (1) 171 | 172 | #if __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ 173 | typedef struct 174 | { 175 | u8 aSignature[3]; /* FLV */ 176 | u8 Version; /* 1 */ 177 | 178 | u8 rsv1:5; /* 0 */ 179 | u8 TypeFlagAudio:1; /* 是否有音频数据 */ 180 | u8 rsv2:1; /* 0 */ 181 | u8 TypeFlagVideo:1; /* 是否有视频数据 */ 182 | 183 | u8 aDataOffset[4]; /* 这个头的长度, 9 */ 184 | 185 | }T_FileHeader; 186 | #elif __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ 187 | typedef struct 188 | { 189 | u8 aSignature[3]; /* FLV */ 190 | u8 Version; /* 1 */ 191 | 192 | u8 TypeFlagVideo:1; /* 是否有视频数据 */ 193 | u8 rsv2:1; /* 0 */ 194 | u8 TypeFlagAudio:1; /* 是否有音频数据 */ 195 | u8 rsv1:5; /* 0 */ 196 | 197 | u8 aDataOffset[4]; /* 这个头的长度, 9 */ 198 | 199 | }T_FileHeader; 200 | #else 201 | #error need define __BYTE_ORDER__ 202 | #endif 203 | 204 | 205 | #if __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ 206 | typedef struct 207 | { 208 | u8 rsv:2; /* 0 */ 209 | u8 Filter:1; /* 是否需要解密等预处理 */ 210 | u8 TagType:5; /* 8:audio, 9:video, 18:script */ 211 | 212 | u8 aDateSize[3]; /* 从StreamID后面开始,到tag结束的长度,即tag总长度-11 */ 213 | u8 aTimestamp[3]; /* 时间戳低3字节,毫秒 */ 214 | u8 TimeStampEx; /* 时间戳高1字节,总共可以扩展成4字节 */ 215 | u8 aStreamID[3]; /* 0 */ 216 | 217 | }T_TagHeader; 218 | #elif __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ 219 | typedef struct 220 | { 221 | u8 TagType:5; /* 8:audio, 9:video, 18:script */ 222 | u8 Filter:1; /* 是否需要解密等预处理 */ 223 | u8 rsv:2; /* 0 */ 224 | 225 | u8 aDateSize[3]; /* 从StreamID后面开始,到tag结束的长度,即tag总长度-11 */ 226 | u8 aTimestamp[3]; /* 时间戳低3字节,毫秒 */ 227 | u8 TimeStampEx; /* 时间戳高1字节,总共可以扩展成4字节 */ 228 | u8 aStreamID[3]; /* 0 */ 229 | 230 | }T_TagHeader; 231 | #else 232 | #error need define __BYTE_ORDER__ 233 | #endif 234 | 235 | 236 | /* 237 | FrameType. The following values are defined: 238 | 1 = key frame (for AVC, a seekable frame) 239 | 2 = inter frame (for AVC, a non-seekable frame) 240 | 3 = disposable inter frame (H.263 only) 241 | 4 = generated key frame (reserved for server use only) 242 | 5 = video info/command frame 243 | 244 | CodecID. The following values are defined: 245 | 2 = Sorenson H.263 246 | 3 = Screen video 247 | 4 = On2 VP6 248 | 5 = On2 VP6 with alpha channel 249 | 6 = Screen video version 2 250 | 7 = AVC 251 | */ 252 | #if __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ 253 | typedef struct 254 | { 255 | u8 FrameType:4; 256 | u8 CodecID:4; 257 | 258 | }T_VideoHeader; 259 | #elif __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ 260 | typedef struct 261 | { 262 | u8 CodecID:4; 263 | u8 FrameType:4; 264 | 265 | }T_VideoHeader; 266 | #else 267 | #error need define __BYTE_ORDER__ 268 | #endif 269 | /* 270 | 0 = AVC sequence header 271 | 1 = AVC NALU 272 | 2 = AVC end of sequence (lower level NALU sequence ender is not required or supported) 273 | */ 274 | typedef struct 275 | { 276 | u8 PacketType; 277 | u8 aCompostionTime[3]; 278 | 279 | }T_AVCPacketHeader; 280 | 281 | 282 | #if __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ 283 | typedef struct 284 | { 285 | u8 Version; /* 1 */ 286 | u8 ProfileIndication; 287 | u8 ProfileCompatibility; /* sps中Profile和Level字段中间的那个字节 */ 288 | u8 AVCLevelIndication; 289 | 290 | u8 rsv:6; /* 111111 */ 291 | u8 LengthSizeMinusOne:2; 292 | 293 | /* 294 | u8 rsv2:3; // 111 295 | u8 SPSNum:5; 296 | u16 SPSLen; 297 | SPSData; 298 | 299 | u8 PPSNum; 300 | u16 PPSLen; 301 | PPSData; 302 | ... 303 | */ 304 | 305 | }T_AVCDecoderConfig; 306 | #elif __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ 307 | typedef struct 308 | { 309 | u8 Version; /* 1 */ 310 | u8 ProfileIndication; 311 | u8 ProfileCompatibility; /* sps中Profile和Level字段中间的那个字节 */ 312 | u8 AVCLevelIndication; 313 | 314 | u8 LengthSizeMinusOne:2; 315 | u8 rsv:6; /* 111111 */ 316 | 317 | }T_AVCDecoderConfig; 318 | #else 319 | #error need define __BYTE_ORDER__ 320 | #endif 321 | 322 | 323 | /* 324 | Format of SoundData. 325 | 0 = Linear PCM, platform endian 326 | 1 = ADPCM 327 | 2 = MP3 328 | 3 = Linear PCM, little endian 329 | 4 = Nellymoser 16 kHz mono 330 | 5 = Nellymoser 8 kHz mono 331 | 6 = Nellymoser 332 | 7 = G.711 A-law logarithmic PCM 333 | 8 = G.711 mu-law logarithmic PCM 334 | 9 = reserved 335 | 10 = AAC 336 | 11 = Speex 337 | 14 = MP3 8 kHz 338 | 15 = Device-specific sound 339 | Formats 7, 8, 14, and 15 are reserved. 340 | 341 | Sampling rate 342 | 0:5.5 kHz 1:11 kHz 2:22 kHz 3:44 kHz 343 | 344 | SoundSize. This parameter only pertains to uncompressed formats. Compressed formats always decode to 16 bits internally. 345 | 0:8-bit samples 1:16-bit samples 346 | 347 | SoundType 348 | 0:Mono sound 1:Stereo sound 349 | */ 350 | #if __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ 351 | typedef struct 352 | { 353 | u8 SoundFormat:4; 354 | u8 SoundRate:2; 355 | u8 SoundSize:1; 356 | u8 SoundType:1; 357 | 358 | }T_AudioHeader; 359 | #elif __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ 360 | typedef struct 361 | { 362 | u8 SoundType:1; 363 | u8 SoundSize:1; 364 | u8 SoundRate:2; 365 | u8 SoundFormat:4; 366 | 367 | }T_AudioHeader; 368 | #else 369 | #error need define __BYTE_ORDER__ 370 | #endif 371 | 372 | 373 | 374 | /* 375 | AACPacketType 376 | 0:AAC sequence header 1:AAC raw 377 | */ 378 | typedef struct 379 | { 380 | u8 AACPacketType; 381 | 382 | }T_AACPacketHeader; 383 | 384 | 385 | 386 | #pragma pack () 387 | 388 | 389 | 390 | 391 | 392 | 393 | typedef struct 394 | { 395 | FILE *fp; 396 | u8 *pBuf; 397 | u32 BufSize; 398 | u32 Duration; /* in ms */ 399 | u8 HaveWritenSPS; 400 | 401 | }T_FileInfo; 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | /* http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio */ 411 | static s8 GetSampleRateID(u32 SamplRate) 412 | { 413 | switch (SamplRate) 414 | { 415 | case 96000: return 0; 416 | case 88200: return 1; 417 | case 64000: return 2; 418 | case 48000: return 3; 419 | case 44100: return 4; 420 | case 32000: return 5; 421 | case 24000: return 6; 422 | case 22050: return 7; 423 | case 16000: return 8; 424 | case 12000: return 9; 425 | case 11025: return 10; 426 | case 8000 : return 11; 427 | case 7350 : return 12; 428 | default: return -1; 429 | } 430 | } 431 | 432 | 433 | static s32 GetAudioSpecificConfig(u8 *pBuf, u32 Size, u8 AudioType, u32 SampleRate, u8 Channel) 434 | { 435 | u16 Config; 436 | s8 SampleRateID; 437 | 438 | SampleRateID = GetSampleRateID(SampleRate); 439 | if (0 > SampleRateID) 440 | return -1; 441 | 442 | Config = (AudioType & 0x1f); 443 | Config <<= 4; 444 | Config |= SampleRateID & 0x0f; 445 | Config <<= 4; 446 | Config |= Channel & 0x0f; 447 | Config <<= 3; 448 | 449 | pBuf[0] = (Config >> 8) & 0xff; 450 | pBuf[1] = Config & 0xff; 451 | 452 | return 2; 453 | } 454 | 455 | 456 | /* 返回的数据包括起始的4个字节0x00000001 */ 457 | static u8* Find264Nalu(const u8 *pData, u32 Size, u8 *pNaluType, u32 *pNaluSize) 458 | { 459 | const u8 *pCur; 460 | const u8 *pEnd; 461 | const u8 *pOut; 462 | u8 NaluType; 463 | 464 | 465 | if (4 >= Size) 466 | return NULL; 467 | 468 | /* 找第一个0x00000001 */ 469 | pCur = pData; 470 | pEnd = pData + Size - 4; 471 | while (pCur < pEnd) 472 | { 473 | if ( (0 == pCur[0]) && (0 == pCur[1]) && (0 == pCur[2]) && (1 == pCur[3]) ) 474 | break; 475 | pCur++; 476 | } 477 | if (pCur >= pEnd) 478 | return NULL; 479 | 480 | NaluType = pCur[4] & 0x1f; 481 | *pNaluType = NaluType; 482 | 483 | if (1 == NaluType || 5 == NaluType) /* P帧、I帧, 假设每一包里P帧I帧都是最后一个 */ 484 | { 485 | *pNaluSize = Size - (pCur - pData); 486 | return (u8*)pCur; 487 | } 488 | 489 | pOut = pCur; 490 | 491 | /* 找第二个0x00000001 */ 492 | pCur += 5; 493 | while (pCur <= pEnd) 494 | { 495 | if ( (0 == pCur[0]) && (0 == pCur[1]) && (0 == pCur[2]) && (1 == pCur[3]) ) 496 | break; 497 | pCur++; 498 | } 499 | if (pCur <= pEnd) 500 | { 501 | *pNaluSize = pCur - pOut; 502 | return (u8*)pOut; 503 | } 504 | 505 | *pNaluSize = Size - (pOut - pData); 506 | return (u8*)pOut; 507 | } 508 | 509 | static s32 GetFileHeader(T_FLVConfig *pConf, u8 *pBuf, u32 Size) 510 | { 511 | T_FileHeader *pHeader = (T_FileHeader *)pBuf; 512 | 513 | 514 | if (sizeof(T_FileHeader) > Size) 515 | return -1; 516 | 517 | pHeader->aSignature[0] = 'F'; 518 | pHeader->aSignature[1] = 'L'; 519 | pHeader->aSignature[2] = 'V'; 520 | pHeader->Version = 1; 521 | pHeader->rsv1 = 0; 522 | pHeader->rsv2 = 0; 523 | pHeader->TypeFlagAudio = !!(pConf->AudioFlag); 524 | pHeader->TypeFlagVideo = !!(pConf->VideoFlag); 525 | pHeader->aDataOffset[0] = 0; 526 | pHeader->aDataOffset[1] = 0; 527 | pHeader->aDataOffset[2] = 0; 528 | pHeader->aDataOffset[3] = 9; 529 | 530 | pBuf += sizeof(T_FileHeader); 531 | Size -= sizeof(T_FileHeader); 532 | WRITE_U32(pBuf, Size, 0); 533 | 534 | return sizeof(T_FileHeader) + 4; 535 | } 536 | 537 | static s32 GetTagHeader(u8 *pBuf, u32 BufSize, u8 Type, u32 TimeStamp) 538 | { 539 | T_TagHeader *pHeader = (T_TagHeader *)pBuf; 540 | 541 | 542 | if (sizeof(T_TagHeader) > BufSize) 543 | return -1; 544 | 545 | pHeader->rsv = 0; 546 | pHeader->Filter = 0; 547 | pHeader->TagType = 0x1f & Type; 548 | pHeader->aDateSize[0] = 0; 549 | pHeader->aDateSize[1] = 0; 550 | pHeader->aDateSize[2] = 0; 551 | pHeader->aTimestamp[0] = 0xff & (TimeStamp >> 16); 552 | pHeader->aTimestamp[1] = 0xff & (TimeStamp >> 8); 553 | pHeader->aTimestamp[2] = 0xff & TimeStamp; 554 | pHeader->TimeStampEx = 0xff & (TimeStamp >> 24); 555 | pHeader->aStreamID[0] = 0; 556 | pHeader->aStreamID[1] = 0; 557 | pHeader->aStreamID[2] = 0; 558 | 559 | return sizeof(T_TagHeader); 560 | } 561 | static void SetTagHeaderSize(u8 *pBuf, u32 TagSize) 562 | { 563 | T_TagHeader *pHeader = (T_TagHeader *)pBuf; 564 | 565 | pHeader->aDateSize[0] = 0xff & (TagSize >> 16); 566 | pHeader->aDateSize[1] = 0xff & (TagSize >> 8); 567 | pHeader->aDateSize[2] = 0xff & TagSize; 568 | 569 | return; 570 | } 571 | 572 | 573 | /* 574 | FrameType 575 | 1:key frame 2:inter frame 576 | 577 | CodecID 578 | 7:AVC 579 | 580 | PacketType 581 | 0:AVC sequence header 1:AVC NALU 582 | */ 583 | static s32 GetVideoHeader(u8 *pBuf, u32 Size, u8 FrameType, u8 CodecID, u8 PacketType) 584 | { 585 | T_VideoHeader *pVideoHeader = NULL; 586 | T_AVCPacketHeader *pAVCHeader = NULL; 587 | 588 | 589 | if (sizeof(*pVideoHeader) > Size) 590 | return -1; 591 | pVideoHeader = (T_VideoHeader *)pBuf; 592 | pVideoHeader->FrameType = 0xf & FrameType; 593 | pVideoHeader->CodecID = 0xf & CodecID; 594 | 595 | if (FLV_V_CODECID_AVC != CodecID) 596 | { 597 | return sizeof(*pVideoHeader); 598 | } 599 | pBuf += sizeof(*pVideoHeader); 600 | Size -= sizeof(*pVideoHeader); 601 | 602 | if (sizeof(*pAVCHeader) > Size) 603 | return -1; 604 | pAVCHeader = (T_AVCPacketHeader *)pBuf; 605 | pAVCHeader->PacketType = PacketType; 606 | pAVCHeader->aCompostionTime[0] = 0; 607 | pAVCHeader->aCompostionTime[1] = 0; 608 | pAVCHeader->aCompostionTime[2] = 0; 609 | 610 | return sizeof(*pVideoHeader) + sizeof(*pAVCHeader); 611 | } 612 | 613 | static s32 GetAVCDecoderConfig(u8 *pBuf, u32 BufSize, const u8 *pH264Data, u32 DataSize) 614 | { 615 | u8 *pCur = pBuf; 616 | u8 *pNalu; 617 | u8 NaluType; 618 | u32 NaluSize; 619 | T_AVCDecoderConfig *pDecoderConf = NULL; 620 | 621 | 622 | /* 找到sps */ 623 | pNalu = Find264Nalu(pH264Data, DataSize, &NaluType, &NaluSize); 624 | if ((NULL == pNalu) || (7 != NaluType) || (NaluSize < 8)) 625 | return -1; 626 | pNalu += 4; 627 | NaluSize -= 4; 628 | 629 | /* 写AVCDecoderConf */ 630 | if (sizeof(*pDecoderConf) > BufSize) 631 | return -1; 632 | pDecoderConf = (T_AVCDecoderConfig *)pCur; 633 | pDecoderConf->Version = 1; 634 | pDecoderConf->ProfileIndication = pNalu[1]; 635 | pDecoderConf->ProfileCompatibility = pNalu[2]; 636 | pDecoderConf->AVCLevelIndication = pNalu[3]; 637 | pDecoderConf->rsv = 0x3f; 638 | pDecoderConf->LengthSizeMinusOne = 3; 639 | pCur += sizeof(*pDecoderConf); 640 | BufSize -= sizeof(*pDecoderConf); 641 | 642 | WRITE_U8 (pCur, BufSize, 0xe1); 643 | WRITE_U16(pCur, BufSize, (u16)NaluSize); 644 | WRITE_BUF(pCur, BufSize, pNalu, NaluSize); 645 | 646 | /* 找到pps */ 647 | pNalu = Find264Nalu(pNalu + NaluSize, DataSize - (pNalu-pH264Data) - NaluSize, &NaluType, &NaluSize); 648 | if (NULL == pNalu || 8 != NaluType) 649 | return -1; 650 | pNalu += 4; 651 | NaluSize -= 4; 652 | 653 | WRITE_U8 (pCur, BufSize, 1); 654 | WRITE_U16(pCur, BufSize, (u16)NaluSize); 655 | WRITE_BUF(pCur, BufSize, pNalu, NaluSize); 656 | 657 | return pCur - pBuf; 658 | } 659 | 660 | /* 661 | ScriptTagHeader 662 | ScriptDataBody 663 | Name 664 | StringType 665 | 666 | StringLen 667 | String 668 | Value 669 | ObjectType 670 | 671 | PropName 672 | StringLen 673 | String 674 | PropData 675 | Type 676 | ... 677 | 678 | ObjectEnd 679 | */ 680 | static s32 GetScriptTag(T_FLVConfig *pConf, u8 *pBuf, u32 Size) 681 | { 682 | u8 *pCur = pBuf; 683 | s32 HeaderSize; 684 | u32 TagSize; 685 | double Value; 686 | u32 rst; 687 | time_t Time; 688 | struct tm Tm; 689 | char strDate[30]; 690 | int DateLen; 691 | 692 | 693 | HeaderSize = GetTagHeader(pCur, Size, FLV_TAG_TYPE_SCRIPT, 0); 694 | if (0 > HeaderSize) 695 | return -1; 696 | pCur += HeaderSize; 697 | Size -= HeaderSize; 698 | 699 | 700 | /* 写ScriptTagBody */ 701 | WRITE_U8 (pCur, Size, FLV_SCRIPT_VALUE_TYPE_STRING); /* body name */ 702 | WRITE_STRING(pCur, Size, FLV_STRING_METADATA, FLV_STRING_METADATA_LEN); 703 | 704 | 705 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_OBJECT); /* body value */ 706 | 707 | /* video */ 708 | if (pConf->VideoFlag) 709 | { 710 | WRITE_STRING(pCur, Size, FLV_STRING_WIDTH, FLV_STRING_WIDTH_LEN); /* width */ 711 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_NUM); 712 | Value = (double)(pConf->Width); 713 | WRITE_DOUBLE(pCur, Size, Value); 714 | 715 | WRITE_STRING(pCur, Size, FLV_STRING_HEIGHT, FLV_STRING_HEIGHT_LEN); /* height */ 716 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_NUM); 717 | Value = (double)(pConf->Height); 718 | WRITE_DOUBLE(pCur, Size, Value); 719 | 720 | WRITE_STRING(pCur, Size, FLV_STRING_FRAMERATE, FLV_STRING_FRAMERATE_LEN); /* framerate */ 721 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_NUM); 722 | Value = (double)(pConf->Framerate); 723 | WRITE_DOUBLE(pCur, Size, Value); 724 | 725 | WRITE_STRING(pCur, Size, FLV_STRING_VCODECID, FLV_STRING_VCODECID_LEN); /* videocodecid */ 726 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_NUM); 727 | Value = (double)FLV_V_CODECID_AVC; 728 | WRITE_DOUBLE(pCur, Size, Value); 729 | } 730 | 731 | /* audio */ 732 | if (pConf->AudioFlag) 733 | { 734 | WRITE_STRING(pCur, Size, FLV_STRING_ASAMPLERATE, FLV_STRING_ASAMPLERATE_LEN); /* audiosamplerate */ 735 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_NUM); 736 | Value = (double)(pConf->SampleRate); 737 | WRITE_DOUBLE(pCur, Size, Value); 738 | 739 | WRITE_STRING(pCur, Size, FLV_STRING_ASAMPLESIZE, FLV_STRING_ASAMPLESIZE_LEN); /* audiosamplesize */ 740 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_NUM); 741 | Value = (double)(16);//pConf->SampleSize); 742 | WRITE_DOUBLE(pCur, Size, Value); 743 | 744 | WRITE_STRING(pCur, Size, FLV_STRING_STEREO, FLV_STRING_STEREO_LEN); /* stereo */ 745 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_BOOL); 746 | WRITE_U8(pCur, Size, !!(pConf->Stereo)); 747 | 748 | WRITE_STRING(pCur, Size, FLV_STRING_ACODECID, FLV_STRING_ACODECID_LEN); /* audiocodecid */ 749 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_NUM); 750 | Value = (double)FLV_A_FORMATE_AAC; 751 | WRITE_DOUBLE(pCur, Size, Value); 752 | } 753 | 754 | time(&Time); 755 | localtime_r(&Time, &Tm); 756 | if (119 <= Tm.tm_year) 757 | { 758 | DateLen = snprintf(strDate, 30, "%d-%02d-%02d %02d:%02d:%02d %s", Tm.tm_year + 1900, Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_hour, Tm.tm_min, Tm.tm_sec, Tm.tm_zone); 759 | WRITE_STRING(pCur, Size, FLV_STRING_DATE, FLV_STRING_DATE_LEN); 760 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_STRING); 761 | WRITE_STRING(pCur, Size, strDate, DateLen); 762 | } 763 | 764 | WRITE_STRING(pCur, Size, FLV_STRING_AUTHOR, FLV_STRING_AUTHOR_LEN); 765 | WRITE_U8(pCur, Size, FLV_SCRIPT_VALUE_TYPE_STRING); 766 | WRITE_STRING(pCur, Size, "LLL", 3); 767 | 768 | /* body end */ 769 | WRITE_U24(pCur, Size, 0x000009); 770 | 771 | 772 | TagSize = (u32)(pCur - pBuf); 773 | SetTagHeaderSize(pBuf, TagSize - HeaderSize); 774 | WRITE_U32(pCur, Size, TagSize); 775 | 776 | return TagSize + 4; 777 | } 778 | 779 | s32 FLV_Get264Tag_SPS(u8 *pBuf, u32 BufSize, const u8 *pH264Data, u32 DataSize) 780 | { 781 | u8 *pCur = pBuf; 782 | u32 TagSize; 783 | s32 HeaderSize; 784 | s32 rst; 785 | 786 | 787 | if (NULL == pBuf || NULL == pH264Data) 788 | return -1; 789 | 790 | /* 写TagHeader */ 791 | HeaderSize = GetTagHeader(pCur, BufSize, FLV_TAG_TYPE_VIDEO, 0); 792 | if (0 > HeaderSize) 793 | return -1; 794 | pCur += HeaderSize; 795 | BufSize -= HeaderSize; 796 | 797 | 798 | /* 写VideoHeader */ 799 | rst = GetVideoHeader(pCur, BufSize, FLV_V_FRAME_TYPE_I, FLV_V_CODECID_AVC, FLV_AVCPACKET_TYPE_SPS); 800 | if (0 > rst) 801 | return -1; 802 | pCur += rst; 803 | BufSize -= rst; 804 | 805 | 806 | /* 写AVCDecoderConfigurationRecord */ 807 | rst = GetAVCDecoderConfig(pCur, BufSize, pH264Data, DataSize); 808 | if (0 > rst) 809 | return -1; 810 | pCur += rst; 811 | BufSize -= rst; 812 | 813 | 814 | TagSize = (u32)(pCur - pBuf); 815 | SetTagHeaderSize(pBuf, TagSize - HeaderSize); 816 | WRITE_U32(pCur, BufSize, TagSize); 817 | 818 | return TagSize + 4; 819 | } 820 | 821 | 822 | s32 FLV_Get264Tag(u8 *pBuf, u32 BufSize, const u8 *pH264Data, u32 DataSize, u32 TimeStamp) 823 | { 824 | const u8 *pNalu; 825 | u8 *pCur = pBuf; 826 | u8 NaluType; 827 | u32 NaluSize = 0; 828 | u32 TagSize; 829 | s32 HeaderSize; 830 | s32 rst; 831 | 832 | 833 | if (NULL == pBuf || NULL == pH264Data) 834 | return -1; 835 | 836 | /* 写TagHeader */ 837 | HeaderSize = GetTagHeader(pCur, BufSize, FLV_TAG_TYPE_VIDEO, TimeStamp); 838 | if (0 > HeaderSize) 839 | return -1; 840 | pCur += HeaderSize; 841 | BufSize -= HeaderSize; 842 | 843 | 844 | /* 找到I帧或P帧 找到一个就退出 */ 845 | pNalu = pH264Data; 846 | while (1) 847 | { 848 | pNalu = Find264Nalu(pNalu + NaluSize, DataSize - (pNalu-pH264Data) - NaluSize, &NaluType, &NaluSize); 849 | if (NULL == pNalu) 850 | return -1; 851 | if ((1 == NaluType) || (5 == NaluType)) 852 | break; 853 | } 854 | pNalu += 4; 855 | NaluSize -= 4; 856 | NaluType = (1 == NaluType)? FLV_V_FRAME_TYPE_P: FLV_V_FRAME_TYPE_I; 857 | 858 | 859 | /* 写VideoHeader */ 860 | rst = GetVideoHeader(pCur, BufSize, NaluType, FLV_V_CODECID_AVC, FLV_AVCPACKET_TYPE_NALU); 861 | if (0 > rst) 862 | return -1; 863 | pCur += rst; 864 | BufSize -= rst; 865 | 866 | 867 | /* 写264帧 */ 868 | WRITE_U32(pCur, BufSize, NaluSize); 869 | WRITE_BUF(pCur, BufSize, pNalu, NaluSize); 870 | 871 | TagSize = pCur - pBuf; 872 | SetTagHeaderSize(pBuf, TagSize - HeaderSize); 873 | WRITE_U32(pCur, BufSize, TagSize); 874 | 875 | return TagSize + 4; 876 | } 877 | 878 | 879 | // 默认AAC 880 | static s32 GetAudioHeader(u8 *pBuf, u32 Size, u8 AACPacketType) 881 | { 882 | T_AudioHeader *pAudioHeader; 883 | T_AACPacketHeader *pAACHeader; 884 | 885 | 886 | if (sizeof(*pAudioHeader) > Size) 887 | return -1; 888 | pAudioHeader = (T_AudioHeader *)pBuf; 889 | pAudioHeader->SoundFormat = FLV_A_FORMATE_AAC; 890 | pAudioHeader->SoundRate = FLV_A_SAMPLE_RATE_44K; /* AAC时固定为44K */ 891 | pAudioHeader->SoundSize = FLV_A_SAMPLE_SIZE_16b; /* AAC时固定为16b */ 892 | pAudioHeader->SoundType = FLV_A_SOUND_TYPE_STEREO; /* AAC时固定为stereo */ 893 | pBuf += sizeof(*pAudioHeader); 894 | Size -= sizeof(*pAudioHeader); 895 | 896 | /* 写AACPacketHeader */ 897 | if (sizeof(*pAACHeader) > Size) 898 | return -1; 899 | pAACHeader = (T_AACPacketHeader *)pBuf; 900 | pAACHeader->AACPacketType = AACPacketType; 901 | 902 | return sizeof(*pAudioHeader) + sizeof(*pAACHeader); 903 | } 904 | 905 | static s32 GetAACTag_First(T_FLVConfig *pConf, u8 *pBuf, u32 Size) 906 | { 907 | u8 *pCur = pBuf; 908 | s32 HeaderSize; 909 | s32 rst; 910 | u32 TagSize; 911 | 912 | 913 | if (NULL == pConf || NULL == pBuf) 914 | return -1; 915 | 916 | /* 写TagHeader */ 917 | HeaderSize = GetTagHeader(pCur, Size, FLV_TAG_TYPE_AUDIO, 0); 918 | if (0 > HeaderSize) 919 | return -1; 920 | pCur += HeaderSize; 921 | Size -= HeaderSize; 922 | 923 | /* 写AudioHeader */ 924 | rst = GetAudioHeader(pCur, Size, FLV_AACPACKET_TYPE_HEADER); 925 | if (0 > rst) 926 | return -1; 927 | pCur += rst; 928 | Size -= rst; 929 | 930 | /* 写SpecificConfig */ 931 | rst = GetAudioSpecificConfig(pCur, Size, pConf->AudioType, pConf->SampleRate, pConf->Channel); 932 | if (0 > rst) 933 | return -1; 934 | pCur += rst; 935 | Size -= rst; 936 | 937 | 938 | TagSize = (u32)(pCur - pBuf); 939 | SetTagHeaderSize(pBuf, TagSize - HeaderSize); 940 | WRITE_U32(pCur, Size, TagSize); 941 | 942 | return TagSize + 4; 943 | } 944 | 945 | 946 | s32 FLV_GetAACTag(u8 *pBuf, u32 BufSize, const u8 *pAACData, u32 DataSize, u32 TimeStamp) 947 | { 948 | u8 *pCur = pBuf; 949 | s32 HeaderSize; 950 | s32 rst; 951 | u32 TagSize; 952 | 953 | 954 | if (NULL == pBuf || NULL == pAACData) 955 | return -1; 956 | 957 | /* 写TagHeader */ 958 | HeaderSize = GetTagHeader(pCur, BufSize, FLV_TAG_TYPE_AUDIO, TimeStamp); 959 | if (0 > HeaderSize) 960 | return -1; 961 | pCur += HeaderSize; 962 | BufSize -= HeaderSize; 963 | 964 | /* 写AudioHeader */ 965 | rst = GetAudioHeader(pCur, BufSize, FLV_AACPACKET_TYPE_RAW); 966 | if (0 > rst) 967 | return -1; 968 | pCur += rst; 969 | BufSize -= rst; 970 | 971 | 972 | /* 写AAC data */ 973 | WRITE_BUF(pCur, BufSize, pAACData, DataSize); 974 | 975 | 976 | TagSize = (u32)(pCur - pBuf); 977 | SetTagHeaderSize(pBuf, TagSize - HeaderSize); 978 | WRITE_U32(pCur, BufSize, TagSize); 979 | 980 | return TagSize + 4; 981 | } 982 | 983 | 984 | 985 | 986 | s32 FLV_GetStreamHeader(T_FLVConfig *pConf, u8 *pBuf, u32 Size) 987 | { 988 | u8 *pCur = pBuf; 989 | s32 rst; 990 | 991 | 992 | if (NULL == pConf || NULL == pBuf) 993 | return -1; 994 | 995 | if ((0 == pConf->VideoFlag) && (0 == pConf->AudioFlag)) 996 | return -1; 997 | 998 | 999 | /* 写FileHeader */ 1000 | rst = GetFileHeader(pConf, pCur, Size); 1001 | if (0 > rst) 1002 | return -1; 1003 | pCur += rst; 1004 | Size -= rst; 1005 | 1006 | 1007 | /* 写ScriptTag */ 1008 | rst = GetScriptTag(pConf, pCur, Size); 1009 | if (0 > rst) 1010 | return -1; 1011 | pCur += rst; 1012 | Size -= rst; 1013 | 1014 | 1015 | /* 写起始的AudioTag */ 1016 | if (pConf->AudioFlag) 1017 | { 1018 | rst = GetAACTag_First(pConf, pCur, Size); 1019 | if (0 > rst) 1020 | return -1; 1021 | pCur += rst; 1022 | Size -= rst; 1023 | } 1024 | 1025 | return pCur - pBuf; 1026 | } 1027 | 1028 | 1029 | void* FLV_CreateFile(const char *pFileName, T_FLVConfig *pConf) 1030 | { 1031 | T_FileInfo *pFile; 1032 | s32 rst; 1033 | size_t size; 1034 | 1035 | 1036 | if (NULL == pFileName || NULL == pConf) 1037 | return NULL; 1038 | 1039 | pFile = malloc(sizeof(*pFile)); 1040 | if (NULL == pFile) 1041 | return NULL; 1042 | 1043 | pFile->fp = fopen(pFileName, "wb"); 1044 | if (NULL == pFile->fp) 1045 | goto ERR_1; 1046 | 1047 | pFile->BufSize = 400*1024; 1048 | pFile->pBuf = malloc(pFile->BufSize); 1049 | if (NULL == pFile->pBuf) 1050 | goto ERR_2; 1051 | 1052 | pFile->HaveWritenSPS = 0; 1053 | pFile->Duration = 0; 1054 | 1055 | rst = FLV_GetStreamHeader(pConf, pFile->pBuf, pFile->BufSize); 1056 | if (0 > rst) 1057 | goto ERR_3; 1058 | 1059 | size = fwrite(pFile->pBuf, 1, (size_t)rst, pFile->fp); 1060 | if (size != (size_t)rst) 1061 | goto ERR_3; 1062 | 1063 | return (void *)pFile; 1064 | 1065 | 1066 | ERR_3: 1067 | free(pFile->pBuf); 1068 | 1069 | ERR_2: 1070 | fclose(pFile->fp); 1071 | 1072 | ERR_1: 1073 | free(pFile); 1074 | 1075 | return NULL; 1076 | } 1077 | 1078 | s32 FLV_Write264(void *pFileInfo, const u8 *pH264Data, u32 Size, u32 TimeStamp) 1079 | { 1080 | T_FileInfo *pFile; 1081 | s32 rst; 1082 | size_t size; 1083 | 1084 | if (NULL == pFileInfo || NULL == pH264Data) 1085 | return -1; 1086 | 1087 | pFile = (T_FileInfo *)pFileInfo; 1088 | 1089 | if (0 == pFile->HaveWritenSPS) 1090 | { 1091 | rst = FLV_Get264Tag_SPS(pFile->pBuf, pFile->BufSize, pH264Data, Size); 1092 | if (0 > rst) 1093 | return -1; 1094 | 1095 | size = fwrite(pFile->pBuf, 1, (size_t)rst, pFile->fp); 1096 | if (size != (size_t)rst) 1097 | return -1; 1098 | 1099 | pFile->HaveWritenSPS = 1; 1100 | } 1101 | 1102 | 1103 | rst = FLV_Get264Tag(pFile->pBuf, pFile->BufSize, pH264Data, Size, TimeStamp); 1104 | if (0 > rst) 1105 | return -1; 1106 | 1107 | size = fwrite(pFile->pBuf, 1, (size_t)rst, pFile->fp); 1108 | if (size != (size_t)rst) 1109 | return -1; 1110 | 1111 | if (TimeStamp > pFile->Duration) 1112 | { 1113 | pFile->Duration = TimeStamp; 1114 | } 1115 | 1116 | return 0; 1117 | } 1118 | 1119 | s32 FLV_WriteAAC(void *pFileInfo, const u8 *pAACData, u32 Size, u32 TimeStamp) 1120 | { 1121 | T_FileInfo *pFile; 1122 | s32 rst; 1123 | size_t size; 1124 | 1125 | if (NULL == pFileInfo || NULL == pAACData) 1126 | return -1; 1127 | 1128 | pFile = (T_FileInfo *)pFileInfo; 1129 | 1130 | rst = FLV_GetAACTag(pFile->pBuf, pFile->BufSize, pAACData, Size, TimeStamp); 1131 | if (0 > rst) 1132 | return -1; 1133 | 1134 | size = fwrite(pFile->pBuf, 1, (size_t)rst, pFile->fp); 1135 | if (size != (size_t)rst) 1136 | return -1; 1137 | 1138 | return 0; 1139 | } 1140 | 1141 | void FLV_CloseFile(void *pFileInfo) 1142 | { 1143 | T_FileInfo *pFile; 1144 | 1145 | if (NULL == pFileInfo) 1146 | return; 1147 | 1148 | pFile = (T_FileInfo *)pFileInfo; 1149 | 1150 | if (NULL != pFile->fp) 1151 | fclose(pFile->fp); 1152 | pFile->fp = NULL; 1153 | 1154 | if (NULL != pFile->pBuf) 1155 | free(pFile->pBuf); 1156 | pFile->pBuf = NULL; 1157 | 1158 | free(pFileInfo); 1159 | } 1160 | --------------------------------------------------------------------------------