├── .gitattributes ├── include ├── BaseWriter.h ├── FlvCommon.h └── FlvEncoder.h ├── libflv.sln ├── libflv.vcxproj └── src ├── BaseWriter.cpp ├── FlvEncoder.cpp ├── flv.c ├── flv_bytestream.c └── flv_bytestream.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /include/BaseWriter.h: -------------------------------------------------------------------------------- 1 | #ifndef _BASE_WRITER_H 2 | #define _BASE_WRITER_H 3 | #include 4 | #include "FlvCommon.h" 5 | 6 | namespace BrightLib{namespace Media{ 7 | 8 | class LIBFLV_API BaseWriter 9 | { 10 | public: 11 | virtual int write(uint8_t *buf, size_t len)=0; 12 | }; 13 | class FileWriter 14 | { 15 | }; 16 | class LIBFLV_API StreamWriter : public BaseWriter 17 | { 18 | private: 19 | std::ostream& m_output; 20 | public: 21 | StreamWriter(std::ostream& st); 22 | virtual int write(uint8_t *buf, size_t len); 23 | }; 24 | 25 | }} 26 | 27 | #endif //_BASE_WRITER_H 28 | -------------------------------------------------------------------------------- /include/FlvCommon.h: -------------------------------------------------------------------------------- 1 | #ifndef _FLV_COMMON_H 2 | #define _FLV_COMMON_H 3 | #include 4 | #ifdef LIBFLV_EXPORTS 5 | #define LIBFLV_API __declspec(dllexport) 6 | #else 7 | #define LIBFLV_API __declspec(dllimport) 8 | #endif 9 | 10 | #endif //_FLV_COMMON_H 11 | -------------------------------------------------------------------------------- /include/FlvEncoder.h: -------------------------------------------------------------------------------- 1 | #ifndef _FLV_MUXER_H_ 2 | #define _FLV_MUXER_H_ 3 | #include "FlvCommon.h" 4 | #include "BaseWriter.h" 5 | 6 | namespace BrightLib{namespace Media{ 7 | 8 | #define CHECK(x)\ 9 | do {\ 10 | if( (x) < 0 )\ 11 | return -1;\ 12 | } while( 0 ) 13 | 14 | 15 | 16 | namespace Flv 17 | { 18 | 19 | class LIBFLV_API FlvEncoder 20 | { 21 | protected: 22 | bool m_bSentHeader; 23 | bool m_bSentParameters; 24 | BaseWriter *m_pWriter; 25 | void *m_pFlvBuffer; 26 | int flush(); 27 | public: 28 | FlvEncoder(BaseWriter *pWriter); 29 | virtual ~FlvEncoder(); 30 | int init(BaseWriter *pWriter); 31 | int write_header(); 32 | int write_video(uint8_t *pBuf, size_t len, int64_t pts, int64_t dts); 33 | int write_audio(); 34 | }; 35 | 36 | class LIBFLV_API H264FlvEncoder : public FlvEncoder 37 | { 38 | private: 39 | uint8_t *m_pSEI; 40 | size_t m_uiSEILen; 41 | uint8_t *m_pSPS; 42 | size_t m_uiSPSLen; 43 | uint8_t *m_pPPS; 44 | size_t m_uiPPSLen; 45 | uint64_t m_uiFrameNum; 46 | uint64_t m_uiDelayTime; 47 | bool m_bCompressDTS; 48 | double m_dTimeBase; 49 | int m_iDelayFrames; 50 | uint64_t m_uiInitDelta; 51 | 52 | int write_sps_pps(); 53 | int write_nalu(uint8_t *pBuf, size_t len, int64_t pts, int64_t dts); 54 | int write_frame(uint8_t *pBuf, size_t len, int64_t pts, int64_t dts, bool bKeyFrame); 55 | public: 56 | H264FlvEncoder(BaseWriter *pWriter); 57 | virtual ~H264FlvEncoder(); 58 | int write_video(uint8_t *pBuf, size_t len, int64_t pts, int64_t dts); 59 | }; 60 | 61 | }; //namespace FLV 62 | 63 | }}; 64 | 65 | #endif //_FLV_MUXER_H_ 66 | 67 | -------------------------------------------------------------------------------- /libflv.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libflv", "libflv.vcxproj", "{1E46322C-0938-45DC-B19F-F72051B52AF8}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {1E46322C-0938-45DC-B19F-F72051B52AF8}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {1E46322C-0938-45DC-B19F-F72051B52AF8}.Debug|Win32.Build.0 = Debug|Win32 14 | {1E46322C-0938-45DC-B19F-F72051B52AF8}.Release|Win32.ActiveCfg = Release|Win32 15 | {1E46322C-0938-45DC-B19F-F72051B52AF8}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /libflv.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {1E46322C-0938-45DC-B19F-F72051B52AF8} 15 | Win32Proj 16 | libflv 17 | 18 | 19 | 20 | DynamicLibrary 21 | true 22 | Unicode 23 | 24 | 25 | DynamicLibrary 26 | false 27 | true 28 | Unicode 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | true 42 | 43 | 44 | false 45 | 46 | 47 | 48 | 49 | 50 | Level3 51 | Disabled 52 | WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBFLV_EXPORTS;%(PreprocessorDefinitions) 53 | include 54 | 55 | 56 | Windows 57 | true 58 | 59 | 60 | 61 | 62 | Level3 63 | 64 | 65 | MaxSpeed 66 | true 67 | true 68 | WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBFLV_EXPORTS;%(PreprocessorDefinitions) 69 | include 70 | 71 | 72 | Windows 73 | true 74 | true 75 | true 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/BaseWriter.cpp: -------------------------------------------------------------------------------- 1 | #include "BaseWriter.h" 2 | 3 | namespace BrightLib{namespace Media{ 4 | 5 | StreamWriter::StreamWriter(std::ostream& st):m_output(st) 6 | { 7 | 8 | } 9 | 10 | int StreamWriter::write(uint8_t *buf, size_t len) 11 | { 12 | m_output.write((char*)buf, len); 13 | return 0; 14 | } 15 | 16 | }} 17 | -------------------------------------------------------------------------------- /src/FlvEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | extern "C" 4 | { 5 | #include "flv_bytestream.h" 6 | } 7 | #include "FlvEncoder.h" 8 | 9 | typedef void* flv_io_handle; 10 | 11 | namespace BrightLib{namespace Media{ 12 | 13 | namespace Flv 14 | { 15 | 16 | FlvEncoder::FlvEncoder(BaseWriter *pWriter) 17 | { 18 | m_pWriter = pWriter; 19 | m_pFlvBuffer = malloc(sizeof(flv_buffer)); 20 | memset(m_pFlvBuffer, 0, sizeof(flv_buffer)); 21 | 22 | m_bSentHeader = false; 23 | m_bSentParameters = false; 24 | } 25 | 26 | FlvEncoder::~FlvEncoder() 27 | { 28 | if(m_pFlvBuffer!=NULL) free(m_pFlvBuffer); 29 | } 30 | 31 | int FlvEncoder::init(BaseWriter *pWriter) 32 | { 33 | m_pWriter = pWriter; 34 | m_pFlvBuffer = malloc(sizeof(flv_buffer)); 35 | memset(m_pFlvBuffer, 0, sizeof(flv_buffer)); 36 | 37 | return 0; 38 | } 39 | 40 | int FlvEncoder::flush() 41 | { 42 | flv_buffer *pBuf = (flv_buffer*)m_pFlvBuffer; 43 | 44 | if( !pBuf->d_cur ) 45 | return 0; 46 | 47 | m_pWriter->write(pBuf->data, pBuf->d_cur); 48 | pBuf->d_total += pBuf->d_cur; 49 | 50 | pBuf->d_cur = 0; 51 | 52 | return 0; 53 | } 54 | 55 | int FlvEncoder::write_header() 56 | { 57 | flv_buffer *pBuf = (flv_buffer*)m_pFlvBuffer; 58 | 59 | flv_put_tag ( pBuf, "FLV" ); // Signature 60 | flv_put_byte( pBuf, 1 ); // Version 61 | flv_put_byte( pBuf, 1 ); // Video Only 62 | flv_put_be32( pBuf, 9 ); // DataOffset 63 | flv_put_be32( pBuf, 0 ); // PreviousTagSize0 64 | 65 | return this->flush(); 66 | } 67 | 68 | 69 | H264FlvEncoder::H264FlvEncoder(BaseWriter *pWriter):FlvEncoder(pWriter) 70 | { 71 | m_pSPS = NULL; 72 | m_pPPS = NULL; 73 | m_pSEI = NULL; 74 | m_uiSEILen = 0; 75 | m_uiFrameNum = 0; 76 | m_uiDelayTime= 0; 77 | m_iDelayFrames = 0; 78 | m_uiInitDelta = 0; 79 | m_dTimeBase = 1.0/1000.0; 80 | 81 | m_bCompressDTS = true; 82 | } 83 | 84 | H264FlvEncoder::~H264FlvEncoder() 85 | { 86 | if(m_pSEI) free(m_pSEI); 87 | if(m_pSPS) free(m_pSPS); 88 | if(m_pPPS) free(m_pPPS); 89 | } 90 | 91 | int H264FlvEncoder::write_video(uint8_t *data, size_t len, int64_t pts, int64_t dts) 92 | { 93 | int rc = 0; 94 | uint8_t *p = data; 95 | uint8_t *q = NULL; 96 | unsigned int n = 0; 97 | unsigned int count = 0; 98 | 99 | if(len<5) return 0; //invalid data: 00 00 00 01 100 | 101 | for(unsigned int i=0; i0) 107 | { 108 | rc = write_nalu(q, n, pts, dts); 109 | if(rc<0) return rc; 110 | count += rc; 111 | } 112 | q += n; 113 | n = 0; 114 | }else if(i==len-5 && q!=NULL){ //the last piece 115 | rc = write_nalu(q, n+4, pts, dts); 116 | if(rc<0) return rc; 117 | count += rc; 118 | break; 119 | } 120 | if(q!=NULL) n++; 121 | } 122 | 123 | return count; 124 | } 125 | int H264FlvEncoder::write_nalu(uint8_t *data, size_t len, int64_t pts, int64_t dts) 126 | { 127 | uint8_t *p = data; 128 | p += 4; //skip start code or AVC length 129 | uint8_t type = p[0] & 0x1F; 130 | 131 | if(this->m_bSentParameters && (type == 0x06 || type==0x07 || type==0x08)) 132 | { 133 | printf("Duplicate SPS/PPS\n"); 134 | return 0; 135 | } 136 | 137 | switch(type) 138 | { 139 | case 0x01: //non-IDR 140 | case 0x02: //slice 141 | case 0x03: 142 | case 0x04: 143 | return write_frame(data, len, pts, dts, false); 144 | 145 | case 0x05: //IDR 146 | return write_frame(data, len, pts, dts, true); 147 | 148 | case 0x06: //SEI 149 | if(m_pSEI!=NULL) { free(m_pSEI); m_pSEI=NULL; } 150 | if(m_pSEI==NULL) m_pSEI = (uint8_t*)malloc(len); 151 | if(m_pSEI==NULL) return -1; 152 | memcpy(m_pSEI, data, len); 153 | m_uiSEILen = len; 154 | break; 155 | 156 | case 0x07: //SPS 157 | if(m_pSPS!=NULL) { free(m_pSPS); m_pSPS=NULL; } 158 | if(m_pSPS==NULL) m_pSPS = (uint8_t*)malloc(len); 159 | if(m_pSPS==NULL) return -1; 160 | memcpy(m_pSPS, p, len-4); 161 | m_uiSPSLen = len-4; 162 | return write_sps_pps(); 163 | 164 | case 0x08: //PPS 165 | if(m_pPPS!=NULL) { free(m_pPPS); m_pPPS=NULL; } 166 | if(m_pPPS==NULL) m_pPPS = (uint8_t*)malloc(len); 167 | if(m_pPPS==NULL) return -1; 168 | memcpy(m_pPPS, p, len-4); 169 | m_uiPPSLen = len-4; 170 | return write_sps_pps(); 171 | } 172 | 173 | return -1; 174 | } 175 | 176 | int H264FlvEncoder::write_sps_pps() 177 | { 178 | uint8_t *sps = m_pSPS; 179 | size_t sps_len = m_uiSPSLen; 180 | uint8_t *pps = m_pPPS; 181 | size_t pps_len = m_uiPPSLen; 182 | 183 | if(sps==NULL || pps==NULL) return 0; //write until both SPS and PPS are ready 184 | if(m_bSentHeader==false) //send header if not yet 185 | { 186 | this->write_header(); 187 | m_bSentHeader = true; 188 | } 189 | if(this->m_bSentParameters) return 0; 190 | 191 | flv_buffer *c = (flv_buffer*)m_pFlvBuffer; 192 | 193 | //SPS 194 | flv_put_byte( c, FLV_TAG_TYPE_VIDEO ); 195 | flv_put_be24( c, 0 ); // rewrite later 196 | flv_put_be24( c, 0 ); // timestamp 197 | flv_put_byte( c, 0 ); // timestamp extended 198 | flv_put_be24( c, 0 ); // StreamID - Always 0 199 | int start = c->d_cur; // needed for overwriting length 200 | 201 | flv_put_byte( c, 7 | FLV_FRAME_KEY ); // Frametype and CodecID 202 | flv_put_byte( c, 0 ); // AVC sequence header 203 | flv_put_be24( c, 0 ); // composition time 204 | 205 | flv_put_byte( c, 1 ); // version 206 | flv_put_byte( c, sps[1] ); // profile 207 | flv_put_byte( c, sps[2] ); // profile 208 | flv_put_byte( c, sps[3] ); // level 209 | flv_put_byte( c, 0xff ); // 6 bits reserved (111111) + 2 bits nal size length - 1 (11) 210 | flv_put_byte( c, 0xe1 ); // 3 bits reserved (111) + 5 bits number of sps (00001) 211 | 212 | flv_put_be16( c, sps_len ); 213 | flv_append_data( c, sps, sps_len ); 214 | 215 | // PPS 216 | flv_put_byte( c, 1 ); // number of pps 217 | flv_put_be16( c, pps_len ); 218 | flv_append_data( c, pps, pps_len ); 219 | 220 | // rewrite data length info 221 | int length = c->d_cur - start; 222 | flv_rewrite_amf_be24( c, length, start - 10 ); 223 | flv_put_be32( c, length + 11 ); // Last tag size 224 | CHECK( this->flush() ); 225 | 226 | this->m_bSentParameters = true; 227 | return sps_len + pps_len; 228 | } 229 | 230 | #define convert_timebase_ms(timestamp, timebase) (int64_t)((timestamp) * (timebase) * 1000 + 0.5) 231 | int H264FlvEncoder::write_frame(uint8_t *data, size_t len, int64_t ipts, int64_t idts, bool bKeyFrame) 232 | { 233 | int64_t dts; 234 | int64_t cts; 235 | int64_t offset; 236 | flv_buffer *c = (flv_buffer*)m_pFlvBuffer; 237 | 238 | if(this->m_bSentParameters==false) 239 | { 240 | return 0; 241 | } 242 | 243 | if( !this->m_uiFrameNum ) 244 | { 245 | this->m_uiDelayTime = idts * -1; 246 | } 247 | 248 | if( this->m_bCompressDTS ) 249 | { 250 | if( this->m_uiFrameNum == 1 ) 251 | this->m_uiInitDelta = convert_timebase_ms( idts + this->m_uiDelayTime, this->m_dTimeBase ); 252 | 253 | dts = this->m_uiFrameNum > this->m_iDelayFrames 254 | ? convert_timebase_ms( idts, this->m_dTimeBase ) 255 | : this->m_uiFrameNum * this->m_uiInitDelta / (this->m_iDelayFrames + 1); 256 | 257 | cts = convert_timebase_ms( ipts, this->m_dTimeBase ); 258 | } 259 | else 260 | { 261 | dts = convert_timebase_ms( idts + this->m_uiDelayTime, this->m_dTimeBase ); 262 | cts = convert_timebase_ms( ipts + this->m_uiDelayTime, this->m_dTimeBase ); 263 | } 264 | offset = cts - dts; 265 | 266 | // A new frame - write packet header 267 | flv_put_byte( c, FLV_TAG_TYPE_VIDEO ); 268 | flv_put_be24( c, 0 ); // calculated later 269 | flv_put_be24( c, dts ); 270 | flv_put_byte( c, dts >> 24 ); 271 | flv_put_be24( c, 0 ); 272 | 273 | int start = c->d_cur; 274 | flv_put_byte( c, (bKeyFrame ? FLV_FRAME_KEY : FLV_FRAME_INTER) | 0x7 ); 275 | flv_put_byte( c, 1 ); // AVC NALU 276 | flv_put_be24( c, offset ); 277 | 278 | if( this->m_pSEI ) 279 | { 280 | flv_append_data( c, this->m_pSEI, this->m_uiSEILen ); 281 | free( this->m_pSEI ); 282 | this->m_pSEI = NULL; 283 | } 284 | flv_append_data( c, data, len ); 285 | 286 | int length = c->d_cur - start; 287 | flv_rewrite_amf_be24( c, length, start - 10 ); 288 | flv_put_be32( c, 11 + length ); // Last tag size 289 | CHECK( this->flush() ); 290 | 291 | this->m_uiFrameNum++; 292 | 293 | return len; 294 | 295 | } 296 | 297 | }}}; //namespace 298 | 299 | -------------------------------------------------------------------------------- /src/flv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flv_bytestream.h" 6 | typedef void *hnd_t; 7 | 8 | typedef struct _flv_hnd_t_ 9 | { 10 | flv_buffer *c; 11 | 12 | uint8_t *sei; 13 | int sei_len; 14 | 15 | int64_t i_fps_num; 16 | int64_t i_fps_den; 17 | int64_t i_framenum; 18 | 19 | uint64_t i_framerate_pos; 20 | uint64_t i_duration_pos; 21 | uint64_t i_filesize_pos; 22 | uint64_t i_bitrate_pos; 23 | 24 | uint8_t b_write_length; 25 | //int64_t i_prev_dts; 26 | //int64_t i_prev_cts; 27 | int64_t i_delay_time; 28 | int64_t i_init_delta; 29 | int i_delay_frames; 30 | 31 | double d_timebase; 32 | int b_vfr_input; 33 | int b_dts_compress; 34 | 35 | //unsigned start; 36 | }flv_hnd_t; 37 | 38 | typedef struct x264_param_t 39 | { 40 | int i_width; 41 | int i_height; 42 | int b_vfr_input; /* VFR input. If 1, use timebase and timestamps for ratecontrol purposes. If 0, use fps only. */ 43 | uint32_t i_fps_num; 44 | uint32_t i_fps_den; 45 | uint32_t i_timebase_num; /* Timebase numerator */ 46 | uint32_t i_timebase_den; /* Timebase denominator */ 47 | 48 | int i_bframe; /* how many b-frame between 2 references pictures */ 49 | int i_bframe_pyramid; /* Keep some B-frames as references: 0=off, 1=strict hierarchical, 2=normal */ 50 | }x264_param_t; 51 | 52 | #if defined(__GNUC__) && (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ > 0) 53 | #define UNUSED __attribute__((unused)) 54 | #define ALWAYS_INLINE __attribute__((always_inline)) inline 55 | #define NOINLINE __attribute__((noinline)) 56 | #define MAY_ALIAS __attribute__((may_alias)) 57 | #define x264_constant_p(x) __builtin_constant_p(x) 58 | #define x264_nonconstant_p(x) (!__builtin_constant_p(x)) 59 | #else 60 | #ifdef _MSC_VER 61 | #define ALWAYS_INLINE __forceinline 62 | #define NOINLINE __declspec(noinline) 63 | #else 64 | #define ALWAYS_INLINE inline 65 | #define NOINLINE 66 | #endif 67 | #define UNUSED 68 | #define MAY_ALIAS 69 | #define x264_constant_p(x) 0 70 | #define x264_nonconstant_p(x) 0 71 | #endif 72 | 73 | #define WORD_SIZE sizeof(void*) 74 | 75 | #define asm __asm__ 76 | 77 | #if WORDS_BIGENDIAN 78 | #define endian_fix(x) (x) 79 | #define endian_fix64(x) (x) 80 | #define endian_fix32(x) (x) 81 | #define endian_fix16(x) (x) 82 | #else 83 | #if HAVE_X86_INLINE_ASM && HAVE_MMX 84 | static ALWAYS_INLINE uint32_t endian_fix32( uint32_t x ) 85 | { 86 | asm("bswap %0":"+r"(x)); 87 | return x; 88 | } 89 | #elif defined(__GNUC__) && HAVE_ARMV6 90 | static ALWAYS_INLINE uint32_t endian_fix32( uint32_t x ) 91 | { 92 | asm("rev %0, %0":"+r"(x)); 93 | return x; 94 | } 95 | #else 96 | static ALWAYS_INLINE uint32_t endian_fix32( uint32_t x ) 97 | { 98 | return (x<<24) + ((x<<8)&0xff0000) + ((x>>8)&0xff00) + (x>>24); 99 | } 100 | #endif 101 | #if HAVE_X86_INLINE_ASM && ARCH_X86_64 102 | static ALWAYS_INLINE uint64_t endian_fix64( uint64_t x ) 103 | { 104 | asm("bswap %0":"+r"(x)); 105 | return x; 106 | } 107 | #else 108 | static ALWAYS_INLINE uint64_t endian_fix64( uint64_t x ) 109 | { 110 | return endian_fix32(x>>32) + ((uint64_t)endian_fix32(x)<<32); 111 | } 112 | #endif 113 | static ALWAYS_INLINE intptr_t endian_fix( intptr_t x ) 114 | { 115 | return WORD_SIZE == 8 ? endian_fix64(x) : endian_fix32(x); 116 | } 117 | static ALWAYS_INLINE uint16_t endian_fix16( uint16_t x ) 118 | { 119 | return (x<<8)|(x>>8); 120 | } 121 | #endif 122 | 123 | 124 | /* 125 | static int write_header( flv_buffer *c ) 126 | { 127 | flv_put_tag( c, "FLV" ); // Signature 128 | flv_put_byte( c, 1 ); // Version 129 | flv_put_byte( c, 1 ); // Video Only 130 | flv_put_be32( c, 9 ); // DataOffset 131 | flv_put_be32( c, 0 ); // PreviousTagSize0 132 | 133 | return flv_flush_data( c ); 134 | } 135 | 136 | static int open_file( char *psz_filename, hnd_t *p_handle, int use_dts_compress ) 137 | { 138 | flv_hnd_t *p_flv = NULL; 139 | *p_handle = NULL; 140 | p_flv = (flv_hnd_t *)calloc( 1, sizeof(flv_hnd_t) ); 141 | if( !p_flv ) 142 | return -1; 143 | 144 | p_flv->b_dts_compress = use_dts_compress; 145 | 146 | p_flv->c = flv_create_writer( psz_filename ); 147 | if( !p_flv->c ) 148 | return -1; 149 | 150 | CHECK( write_header( p_flv->c ) ); 151 | *p_handle = p_flv; 152 | 153 | return 0; 154 | } 155 | */ 156 | #if 0 157 | static int set_param( hnd_t handle, x264_param_t *p_param ) 158 | { 159 | int start = 0; 160 | unsigned length = 0; 161 | flv_hnd_t *p_flv = (flv_hnd_t*)handle; 162 | flv_buffer *c = p_flv->c; 163 | 164 | flv_put_byte( c, FLV_TAG_TYPE_META ); // Tag Type "script data" 165 | 166 | start = c->d_cur; 167 | flv_put_be24( c, 0 ); // data length 168 | flv_put_be24( c, 0 ); // timestamp 169 | flv_put_be32( c, 0 ); // reserved 170 | 171 | flv_put_byte( c, AMF_DATA_TYPE_STRING ); 172 | flv_put_amf_string( c, "onMetaData" ); 173 | 174 | flv_put_byte( c, AMF_DATA_TYPE_MIXEDARRAY ); 175 | flv_put_be32( c, 7 ); 176 | 177 | flv_put_amf_string( c, "width" ); 178 | flv_put_amf_double( c, p_param->i_width ); 179 | 180 | flv_put_amf_string( c, "height" ); 181 | flv_put_amf_double( c, p_param->i_height ); 182 | 183 | flv_put_amf_string( c, "framerate" ); 184 | 185 | if( !p_param->b_vfr_input ) 186 | flv_put_amf_double( c, (double)p_param->i_fps_num / p_param->i_fps_den ); 187 | else 188 | { 189 | p_flv->i_framerate_pos = c->d_cur + c->d_total + 1; 190 | flv_put_amf_double( c, 0 ); // written at end of encoding 191 | } 192 | 193 | flv_put_amf_string( c, "videocodecid" ); 194 | flv_put_amf_double( c, FLV_CODECID_H264 ); 195 | 196 | flv_put_amf_string( c, "duration" ); 197 | p_flv->i_duration_pos = c->d_cur + c->d_total + 1; 198 | flv_put_amf_double( c, 0 ); // written at end of encoding 199 | 200 | flv_put_amf_string( c, "filesize" ); 201 | p_flv->i_filesize_pos = c->d_cur + c->d_total + 1; 202 | flv_put_amf_double( c, 0 ); // written at end of encoding 203 | 204 | flv_put_amf_string( c, "videodatarate" ); 205 | p_flv->i_bitrate_pos = c->d_cur + c->d_total + 1; 206 | flv_put_amf_double( c, 0 ); // written at end of encoding 207 | 208 | flv_put_amf_string( c, "" ); 209 | flv_put_byte( c, AMF_END_OF_OBJECT ); 210 | 211 | length = c->d_cur - start; 212 | flv_rewrite_amf_be24( c, length - 10, start ); 213 | 214 | flv_put_be32( c, length + 1 ); // tag length 215 | 216 | p_flv->i_fps_num = p_param->i_fps_num; 217 | p_flv->i_fps_den = p_param->i_fps_den; 218 | p_flv->d_timebase = (double)p_param->i_timebase_num / p_param->i_timebase_den; 219 | p_flv->b_vfr_input = p_param->b_vfr_input; 220 | p_flv->i_delay_frames = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0; 221 | 222 | return 0; 223 | } 224 | 225 | typedef struct 226 | { 227 | /* Size of payload (including any padding) in bytes. */ 228 | int i_payload; 229 | /* If param->b_annexb is set, Annex-B bytestream with startcode. 230 | * Otherwise, startcode is replaced with a 4-byte size. 231 | * This size is the size used in mp4/similar muxing; it is equal to i_payload-4 */ 232 | uint8_t *p_payload; 233 | 234 | } x264_nal_t; 235 | static int write_headers( hnd_t handle, x264_nal_t *p_nal ) 236 | { 237 | uint8_t *sps = NULL; 238 | unsigned length = 0; 239 | flv_hnd_t *p_flv = (flv_hnd_t *)handle; 240 | flv_buffer *c = p_flv->c; 241 | 242 | int sps_size = p_nal[0].i_payload; 243 | int pps_size = p_nal[1].i_payload; 244 | int sei_size = p_nal[2].i_payload; 245 | 246 | // SEI 247 | /* It is within the spec to write this as-is but for 248 | * mplayer/ffmpeg playback this is deferred until before the first frame */ 249 | 250 | p_flv->sei = (uint8_t*)malloc( sei_size ); 251 | if( !p_flv->sei ) 252 | return -1; 253 | p_flv->sei_len = sei_size; 254 | 255 | memcpy( p_flv->sei, p_nal[2].p_payload, sei_size ); 256 | 257 | // SPS 258 | sps = p_nal[0].p_payload + 4; 259 | 260 | flv_put_byte( c, FLV_TAG_TYPE_VIDEO ); 261 | flv_put_be24( c, 0 ); // rewrite later 262 | flv_put_be24( c, 0 ); // timestamp 263 | flv_put_byte( c, 0 ); // timestamp extended 264 | flv_put_be24( c, 0 ); // StreamID - Always 0 265 | p_flv->start = c->d_cur; // needed for overwriting length 266 | 267 | flv_put_byte( c, 7 | FLV_FRAME_KEY ); // Frametype and CodecID 268 | flv_put_byte( c, 0 ); // AVC sequence header 269 | flv_put_be24( c, 0 ); // composition time 270 | 271 | flv_put_byte( c, 1 ); // version 272 | flv_put_byte( c, sps[1] ); // profile 273 | flv_put_byte( c, sps[2] ); // profile 274 | flv_put_byte( c, sps[3] ); // level 275 | flv_put_byte( c, 0xff ); // 6 bits reserved (111111) + 2 bits nal size length - 1 (11) 276 | flv_put_byte( c, 0xe1 ); // 3 bits reserved (111) + 5 bits number of sps (00001) 277 | 278 | flv_put_be16( c, sps_size - 4 ); 279 | flv_append_data( c, sps, sps_size - 4 ); 280 | 281 | // PPS 282 | flv_put_byte( c, 1 ); // number of pps 283 | flv_put_be16( c, pps_size - 4 ); 284 | flv_append_data( c, p_nal[1].p_payload + 4, pps_size - 4 ); 285 | 286 | // rewrite data length info 287 | length = c->d_cur - p_flv->start; 288 | flv_rewrite_amf_be24( c, length, p_flv->start - 10 ); 289 | flv_put_be32( c, length + 11 ); // Last tag size 290 | CHECK( flv_flush_data( c ) ); 291 | 292 | return sei_size + sps_size + pps_size; 293 | } 294 | 295 | static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, int64_t i_pts, int64_t i_dts, int b_keyframe) 296 | { 297 | int64_t dts; 298 | int64_t cts; 299 | int64_t offset; 300 | unsigned length = 0; 301 | flv_hnd_t *p_flv = (flv_hnd_t *)handle; 302 | flv_buffer *c = p_flv->c; 303 | 304 | #define convert_timebase_ms( timestamp, timebase ) (int64_t)((timestamp) * (timebase) * 1000 + 0.5) 305 | 306 | if( !p_flv->i_framenum ) 307 | { 308 | p_flv->i_delay_time = i_dts * -1; 309 | if( !p_flv->b_dts_compress && p_flv->i_delay_time ); 310 | //x264_cli_log( "flv", X264_LOG_INFO, "initial delay %"PRId64" ms\n", 311 | // convert_timebase_ms( p_picture->i_pts + p_flv->i_delay_time, p_flv->d_timebase ) ); 312 | } 313 | 314 | 315 | if( p_flv->b_dts_compress ) 316 | { 317 | if( p_flv->i_framenum == 1 ) 318 | p_flv->i_init_delta = convert_timebase_ms( i_dts + p_flv->i_delay_time, p_flv->d_timebase ); 319 | dts = p_flv->i_framenum > p_flv->i_delay_frames 320 | ? convert_timebase_ms( i_dts, p_flv->d_timebase ) 321 | : p_flv->i_framenum * p_flv->i_init_delta / (p_flv->i_delay_frames + 1); 322 | cts = convert_timebase_ms( i_pts, p_flv->d_timebase ); 323 | } 324 | else 325 | { 326 | dts = convert_timebase_ms( i_dts + p_flv->i_delay_time, p_flv->d_timebase ); 327 | cts = convert_timebase_ms( i_pts + p_flv->i_delay_time, p_flv->d_timebase ); 328 | } 329 | offset = cts - dts; 330 | 331 | if( p_flv->i_framenum ) 332 | { 333 | //if( p_flv->i_prev_dts == dts ) 334 | // x264_cli_log( "flv", X264_LOG_WARNING, "duplicate DTS %"PRId64" generated by rounding\n" 335 | // " decoding framerate cannot exceed 1000fps\n", dts ); 336 | //if( p_flv->i_prev_cts == cts ) 337 | // x264_cli_log( "flv", X264_LOG_WARNING, "duplicate CTS %"PRId64" generated by rounding\n" 338 | // " composition framerate cannot exceed 1000fps\n", cts ); 339 | } 340 | p_flv->i_prev_dts = dts; 341 | p_flv->i_prev_cts = cts; 342 | 343 | // A new frame - write packet header 344 | flv_put_byte( c, FLV_TAG_TYPE_VIDEO ); 345 | flv_put_be24( c, 0 ); // calculated later 346 | flv_put_be24( c, dts ); 347 | flv_put_byte( c, dts >> 24 ); 348 | flv_put_be24( c, 0 ); 349 | 350 | p_flv->start = c->d_cur; 351 | flv_put_byte( c, b_keyframe ? FLV_FRAME_KEY : FLV_FRAME_INTER); 352 | flv_put_byte( c, 1 ); // AVC NALU 353 | flv_put_be24( c, offset ); 354 | 355 | if( p_flv->sei ) 356 | { 357 | flv_append_data( c, p_flv->sei, p_flv->sei_len ); 358 | free( p_flv->sei ); 359 | p_flv->sei = NULL; 360 | } 361 | flv_append_data( c, p_nalu, i_size ); 362 | 363 | length = c->d_cur - p_flv->start; 364 | flv_rewrite_amf_be24( c, length, p_flv->start - 10 ); 365 | flv_put_be32( c, 11 + length ); // Last tag size 366 | CHECK( flv_flush_data( c ) ); 367 | 368 | p_flv->i_framenum++; 369 | 370 | return i_size; 371 | } 372 | #endif 373 | /* 374 | static void rewrite_amf_double( FILE *fp, uint64_t position, double value ) 375 | { 376 | uint64_t x = endian_fix64( flv_dbl2int( value ) ); 377 | fseek( fp, position, SEEK_SET ); 378 | fwrite( &x, 8, 1, fp ); 379 | } 380 | */ 381 | /* 382 | static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest_pts ) 383 | { 384 | flv_hnd_t *p_flv = (flv_hnd_t *)handle; 385 | flv_buffer *c = p_flv->c; 386 | 387 | CHECK( flv_flush_data( c ) ); 388 | 389 | double total_duration = (2 * largest_pts - second_largest_pts) * p_flv->d_timebase; 390 | 391 | if( x264_is_regular_file( c->fp ) && total_duration > 0 ) 392 | { 393 | double framerate; 394 | uint64_t filesize = ftell( c->fp ); 395 | 396 | if( p_flv->i_framerate_pos ) 397 | { 398 | framerate = (double)p_flv->i_framenum / total_duration; 399 | rewrite_amf_double( c->fp, p_flv->i_framerate_pos, framerate ); 400 | } 401 | 402 | rewrite_amf_double( c->fp, p_flv->i_duration_pos, total_duration ); 403 | rewrite_amf_double( c->fp, p_flv->i_filesize_pos, filesize ); 404 | rewrite_amf_double( c->fp, p_flv->i_bitrate_pos, filesize * 8 / ( total_duration * 1000 ) ); 405 | } 406 | 407 | fclose( c->fp ); 408 | 409 | free( p_flv ); 410 | free( c ); 411 | 412 | return 0; 413 | } 414 | */ 415 | -------------------------------------------------------------------------------- /src/flv_bytestream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "flv_bytestream.h" 5 | 6 | typedef union 7 | { 8 | double f; 9 | uint64_t i; 10 | }dbl2int_type; 11 | uint64_t flv_dbl2int( double value ) 12 | { 13 | return ((dbl2int_type*)(&value))->i; 14 | } 15 | 16 | /* Put functions */ 17 | 18 | void flv_put_byte( flv_buffer *c, uint8_t b ) 19 | { 20 | flv_append_data( c, &b, 1 ); 21 | } 22 | 23 | void flv_put_be32( flv_buffer *c, uint32_t val ) 24 | { 25 | flv_put_byte( c, val >> 24 ); 26 | flv_put_byte( c, val >> 16 ); 27 | flv_put_byte( c, val >> 8 ); 28 | flv_put_byte( c, val ); 29 | } 30 | 31 | void flv_put_be64( flv_buffer *c, uint64_t val ) 32 | { 33 | flv_put_be32( c, val >> 32 ); 34 | flv_put_be32( c, val ); 35 | } 36 | 37 | void flv_put_be16( flv_buffer *c, uint16_t val ) 38 | { 39 | flv_put_byte( c, val >> 8 ); 40 | flv_put_byte( c, val ); 41 | } 42 | 43 | void flv_put_be24( flv_buffer *c, uint32_t val ) 44 | { 45 | flv_put_be16( c, val >> 8 ); 46 | flv_put_byte( c, val ); 47 | } 48 | 49 | void flv_put_tag( flv_buffer *c, const char *tag ) 50 | { 51 | while( *tag ) 52 | flv_put_byte( c, *tag++ ); 53 | } 54 | 55 | void flv_put_amf_string( flv_buffer *c, const char *str ) 56 | { 57 | uint16_t len = strlen( str ); 58 | flv_put_be16( c, len ); 59 | flv_append_data( c, (uint8_t*)str, len ); 60 | } 61 | 62 | void flv_put_amf_double( flv_buffer *c, double d ) 63 | { 64 | flv_put_byte( c, AMF_DATA_TYPE_NUMBER ); 65 | flv_put_be64( c, flv_dbl2int( d ) ); 66 | } 67 | 68 | int flv_append_data( flv_buffer *c, uint8_t *data, unsigned size ) 69 | { 70 | unsigned ns = c->d_cur + size; 71 | 72 | if( ns > c->d_max ) 73 | { 74 | void *dp; 75 | unsigned dn = 16; 76 | while( ns > dn ) 77 | dn <<= 1; 78 | 79 | dp = realloc( c->data, dn ); 80 | if( !dp ) 81 | return -1; 82 | 83 | c->data = (uint8_t*)dp; 84 | c->d_max = dn; 85 | } 86 | 87 | memcpy( c->data + c->d_cur, data, size ); 88 | 89 | c->d_cur = ns; 90 | 91 | return 0; 92 | } 93 | 94 | void flv_rewrite_amf_be24( flv_buffer *c, unsigned length, unsigned start ) 95 | { 96 | *(c->data + start + 0) = length >> 16; 97 | *(c->data + start + 1) = length >> 8; 98 | *(c->data + start + 2) = length >> 0; 99 | } 100 | -------------------------------------------------------------------------------- /src/flv_bytestream.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef X264_FLV_BYTESTREAM_H 3 | #define X264_FLV_BYTESTREAM_H 4 | 5 | /* offsets for packed values */ 6 | #define FLV_AUDIO_SAMPLESSIZE_OFFSET 1 7 | #define FLV_AUDIO_SAMPLERATE_OFFSET 2 8 | #define FLV_AUDIO_CODECID_OFFSET 4 9 | 10 | #define FLV_VIDEO_FRAMETYPE_OFFSET 4 11 | 12 | /* bitmasks to isolate specific values */ 13 | #define FLV_AUDIO_CHANNEL_MASK 0x01 14 | #define FLV_AUDIO_SAMPLESIZE_MASK 0x02 15 | #define FLV_AUDIO_SAMPLERATE_MASK 0x0c 16 | #define FLV_AUDIO_CODECID_MASK 0xf0 17 | 18 | #define FLV_VIDEO_CODECID_MASK 0x0f 19 | #define FLV_VIDEO_FRAMETYPE_MASK 0xf0 20 | 21 | #define AMF_END_OF_OBJECT 0x09 22 | 23 | enum 24 | { 25 | FLV_HEADER_FLAG_HASVIDEO = 1, 26 | FLV_HEADER_FLAG_HASAUDIO = 4, 27 | }; 28 | 29 | enum 30 | { 31 | FLV_TAG_TYPE_AUDIO = 0x08, 32 | FLV_TAG_TYPE_VIDEO = 0x09, 33 | FLV_TAG_TYPE_META = 0x12, 34 | }; 35 | 36 | enum 37 | { 38 | FLV_MONO = 0, 39 | FLV_STEREO = 1, 40 | }; 41 | 42 | enum 43 | { 44 | FLV_SAMPLESSIZE_8BIT = 0, 45 | FLV_SAMPLESSIZE_16BIT = 1 << FLV_AUDIO_SAMPLESSIZE_OFFSET, 46 | }; 47 | 48 | enum 49 | { 50 | FLV_SAMPLERATE_SPECIAL = 0, /**< signifies 5512Hz and 8000Hz in the case of NELLYMOSER */ 51 | FLV_SAMPLERATE_11025HZ = 1 << FLV_AUDIO_SAMPLERATE_OFFSET, 52 | FLV_SAMPLERATE_22050HZ = 2 << FLV_AUDIO_SAMPLERATE_OFFSET, 53 | FLV_SAMPLERATE_44100HZ = 3 << FLV_AUDIO_SAMPLERATE_OFFSET, 54 | }; 55 | 56 | enum 57 | { 58 | FLV_CODECID_MP3 = 2 << FLV_AUDIO_CODECID_OFFSET, 59 | FLV_CODECID_AAC = 10<< FLV_AUDIO_CODECID_OFFSET, 60 | }; 61 | 62 | enum 63 | { 64 | FLV_CODECID_H264 = 7, 65 | }; 66 | 67 | enum 68 | { 69 | FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET | 7, 70 | FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET | 7, 71 | }; 72 | 73 | typedef enum 74 | { 75 | AMF_DATA_TYPE_NUMBER = 0x00, 76 | AMF_DATA_TYPE_BOOL = 0x01, 77 | AMF_DATA_TYPE_STRING = 0x02, 78 | AMF_DATA_TYPE_OBJECT = 0x03, 79 | AMF_DATA_TYPE_NULL = 0x05, 80 | AMF_DATA_TYPE_UNDEFINED = 0x06, 81 | AMF_DATA_TYPE_REFERENCE = 0x07, 82 | AMF_DATA_TYPE_MIXEDARRAY = 0x08, 83 | AMF_DATA_TYPE_OBJECT_END = 0x09, 84 | AMF_DATA_TYPE_ARRAY = 0x0a, 85 | AMF_DATA_TYPE_DATE = 0x0b, 86 | AMF_DATA_TYPE_LONG_STRING = 0x0c, 87 | AMF_DATA_TYPE_UNSUPPORTED = 0x0d, 88 | } AMFDataType; 89 | 90 | typedef struct flv_buffer 91 | { 92 | uint8_t *data; 93 | unsigned d_cur; 94 | unsigned d_max; 95 | //FILE *fp; 96 | uint64_t d_total; 97 | } flv_buffer; 98 | 99 | 100 | flv_buffer *flv_create_writer( const char *filename ); 101 | int flv_append_data( flv_buffer *c, uint8_t *data, unsigned size ); 102 | int flv_write_byte( flv_buffer *c, uint8_t *byte ); 103 | int flv_flush_data( flv_buffer *c ); 104 | void flv_rewrite_amf_be24( flv_buffer *c, unsigned length, unsigned start ); 105 | 106 | uint64_t flv_dbl2int( double value ); 107 | void flv_put_byte( flv_buffer *c, uint8_t b ); 108 | void flv_put_be32( flv_buffer *c, uint32_t val ); 109 | void flv_put_be64( flv_buffer *c, uint64_t val ); 110 | void flv_put_be16( flv_buffer *c, uint16_t val ); 111 | void flv_put_be24( flv_buffer *c, uint32_t val ); 112 | void flv_put_tag( flv_buffer *c, const char *tag ); 113 | void flv_put_amf_string( flv_buffer *c, const char *str ); 114 | void flv_put_amf_double( flv_buffer *c, double d ); 115 | 116 | #endif 117 | --------------------------------------------------------------------------------