avi 打包
1 // avi文件固定大小:1MB = 1048576 Bytes 2 // 不足的部分置空,最后一帧超过的放弃,并关闭文件。 3 // 声道只有一个,如果编码速度来得及,就用aac,否则就用pcm。 4 5 #include "stdafx.h" 6 #include <stdlib.h> 7 #include <stdio.h> 8 #include <string.h> 9 10 #include "DPAVIRecorder.h" 11 12 #define AVI_MAX_LEN 0x400000 13 14 #ifndef MAX_PATH 15 #define MAX_PATH 256 16 #endif 17 18 #define AVI_SEEK_SET 0 19 #define AVI_SEEK_CUR 1 20 #define AVI_SEEK_END 2 21 22 #define PAD_EVEN(x) ( ((x)+1) & ~1 ) 23 24 #define HEADERBYTES 512 25 #define HEADERMOVISTART (HEADERBYTES-4) 26 27 typedef FILE *avi_hfile; 28 29 //".avi", ".hdrl", ".movi", ".idxl" 30 #define AVI_POSTFIX_AVI 0 31 #define AVI_POSTFIX_HDRL 1 32 #define AVI_POSTFIX_MOVI 2 33 #define AVI_POSTFIX_IDX1 3 34 #define AVI_POSTFIX_MAX 4 35 36 37 typedef struct _Chunk 38 { 39 avi_int32 ChunkId; /* Chunk ID marker */ 40 avi_int32 ChunkSize; /* Size of the chunk data in bytes */ 41 avi_byte ChunkData[]; /* The chunk data */ 42 } CHUNK, *LPCHUNK; 43 44 /** < Chunk payload ("avih") */ 45 typedef struct _AVIHeader 46 { 47 avi_int32 TimeBetweenFrames; /* Time delay between frames */ 48 avi_int32 MaximumDataRate; /* Data rate of AVI data */ 49 avi_int32 PaddingGranularity; /* Size of single unit of padding */ 50 avi_int32 Flags; /* Data parameters */ 51 avi_int32 TotalNumberOfFrames; /* Number of video frame stored */ 52 avi_int32 NumberOfInitialFrames; /* Number of preview frames */ 53 avi_int32 NumberOfStreams; /* Number of data streams in chunk*/ 54 avi_int32 SuggestedBufferSize; /* Minimum playback buffer size */ 55 avi_int32 Width; /* Width of video frame in pixels */ 56 avi_int32 Height; /* Height of video frame in pixels*/ 57 avi_int32 TimeScale; /* Unit used to measure time */ 58 avi_int32 DataRate; /* Data rate of playback */ 59 avi_int32 StartTime; /* Starting time of AVI data */ 60 avi_int32 DataLength; /* Size of AVI data chunk */ 61 } AVIHEADER, *LPAVIHEADER; 62 63 /** < Chunk payload ("strh") */ 64 typedef struct _StreamHeader 65 { 66 avi_int32 DataType; /* Chunk identifier ("vids") */ 67 avi_int32 DataHandler; /* Device handler identifier ("H264") */ 68 avi_int32 Flags; /* Data parameters */ 69 avi_int32 Priority; /* Set to 0 */ 70 avi_int32 InitialFrames; /* Number of initial audio frames */ 71 avi_int32 TimeScale; /* Unit used to measure time */ 72 avi_int32 DataRate; /* Data rate of playback */ 73 avi_int32 StartTime; /* Starting time of AVI data */ 74 avi_int32 DataLength; /* Size of AVI data chunk */ 75 avi_int32 SuggestedBufferSize; /* Minimum playback buffer size */ 76 avi_int32 Quality; /* Sample quailty factor */ 77 avi_int32 SampleSize; /* Size of the sample in bytes */ 78 struct { 79 avi_int32 left; 80 avi_int32 top; 81 avi_int32 right; 82 avi_int32 bottom; 83 } rcFrame; 84 } STREAMHEADER, *LPSTREAMHEADER; 85 86 /** < Chunk payload ("strf") */ 87 typedef struct _BitMapInfoHeader { 88 avi_int32 biSize; 89 avi_int32 biWidth; 90 avi_int32 biHeight; 91 avi_int16 biPlanes; 92 avi_int16 biBitCount; 93 avi_int32 biCompression; 94 avi_int32 biSizeImage; 95 avi_int32 biXPelsPerMeter; 96 avi_int32 biYPelsPerMeter; 97 avi_int32 biClrUsed; 98 avi_int32 biClrImportant; 99 } BITMAPINFOHEADER, *LPBITMAPINFOHEADER; 100 101 /** < Chunk payload ("strf") */ 102 typedef struct _WaveFormatHeader 103 { 104 avi_int16 wFormatTag; 105 avi_int16 nChannels; //声道数 106 avi_int32 nSamplesPerSec; //采样率 107 avi_int32 nAvgBytesPerSec; //WAVE声音中每秒的数据量 108 avi_int16 nBlockAlign; //数据块的对齐标志 109 avi_int16 wBitsPerSample; //采样的位宽 110 } WAVEFORMATHEADER, *LPWAVEFORMATHEADER; 111 112 typedef struct _AviIndex 113 { 114 avi_int32 Identifier; /* Chunk identifier reference */ 115 avi_int32 Flags; /* Type of chunk referenced */ 116 avi_int32 Offset; /* Position of chunk in file */ 117 avi_int32 Length; /* Length of chunk in bytes */ 118 } AVIINDEX, *LPAVIINDEX; 119 120 typedef struct _JunkChunk 121 { 122 avi_int32 ChunkId; /* Chunk ID marker (JUNK)*/ 123 avi_int32 PaggingSize; /* Size of the padding in bytes */ 124 avi_byte Padding[]; /* Padding */ 125 } JUNKCHUNK, *LPJUNKCHUNK; 126 127 typedef struct _AVI_FILE_HANDLE 128 { 129 avi_string name; 130 avi_hfile hdrl; 131 avi_hfile movi; 132 avi_hfile idx1; 133 } output_handles; 134 135 #define MAX_OUTPUT_HANDLE_NUM 4 136 137 typedef struct avi_recorder_handle_st{ 138 char dir_path[MAX_PATH]; 139 output_handles ohandles[MAX_OUTPUT_HANDLE_NUM]; 140 //avi_byte hdrl_cache[512]; 141 avi_size hdrl_size; 142 avi_int32 max_duration; //最大的时长,单位:ms 143 avi_int32 combine_position; //合并文件后的相对位置 144 avi_int32 max_frame_bytes; // 最大帧的长度 145 avi_int32 num_frames; // 146 avi_int32 num_audios; 147 avi_int32 num_chunk; // 当前数据块的个数 148 avi_int32 num_idx; // 当前索引块的个数 149 avi_int32 num_streams; 150 151 /** < video param > */ 152 avi_int32 frame_per_sec; 153 avi_int32 frame_width; 154 avi_int32 frame_height; 155 156 /** < audio param > */ 157 avi_int32 sample_per_sec; 158 avi_int32 bits_per_sample; 159 avi_int32 audio_channels; 160 161 avi_int8 file_name[MAX_OUTPUT_HANDLE_NUM][64]; 162 163 } avi_st; 164 165 static avi_fn_alloc g_fn_alloc = NULL; 166 static avi_fn_realloc g_fn_realloc = NULL; 167 static avi_fn_free g_fn_free = NULL; 168 static char *g_file_postfix[] = {".avi", ".hdrl", ".movi", ".idx1"}; 169 170 void dp_avi_set_alloc_func(avi_fn_alloc alloc, avi_fn_realloc realloc, avi_fn_free free) 171 { 172 g_fn_alloc = alloc; 173 g_fn_realloc = realloc; 174 g_fn_free = free; 175 } 176 177 static inline void *avi_alloc(avi_size size) 178 { 179 if (g_fn_alloc) 180 return g_fn_alloc(size); 181 else 182 return malloc(size); 183 } 184 185 186 static inline void *avi_realloc(void *ptr, avi_size size) 187 { 188 if (g_fn_realloc) 189 return g_fn_realloc(ptr, size); 190 else 191 return realloc(ptr, size); 192 } 193 194 195 static inline void avi_free (void *ptr) 196 { 197 if (g_fn_free) 198 g_fn_free(ptr); 199 else 200 free(ptr); 201 } 202 203 ////////////////////////////////////////////////////////////////////////// 204 205 static avi_fn_log g_fn_log_debug = NULL; 206 static avi_fn_log g_fn_log_error = NULL; 207 208 void dp_avi_set_log_func (avi_fn_log debug, avi_fn_log error) 209 { 210 g_fn_log_debug = debug; 211 g_fn_log_error = error; 212 } 213 214 static inline void avi_log_debug (avi_cstring message) 215 { 216 if (g_fn_log_debug) 217 g_fn_log_debug(message); 218 #ifdef _DEBUG 219 else 220 printf("[dbg]%s\r\n",message); 221 #endif 222 } 223 224 225 static inline void avi_log_error (avi_cstring message) 226 { 227 if (g_fn_log_error) 228 g_fn_log_error(message); 229 #ifdef _DEBUG 230 else 231 printf("[err]%s\r\n",message); 232 #endif 233 } 234 235 ////////////////////////////////////////////////////////////////////////// 236 237 /* Copy n into dst as a 4 byte, little endian number. 238 Should also work on big endian machines */ 239 240 static avi_size avi_ulong2bstr (avi_bptr dst, unsigned long num) 241 { 242 dst[0] = 0xff & (num); 243 dst[1] = 0xff & (num >> 8); 244 dst[2] = 0xff & (num >> 16); 245 dst[3] = 0xff & (num >> 24); 246 return 4; 247 } 248 249 static avi_size avi_ushort2bstr (avi_bptr dst, unsigned short num) 250 { 251 dst[0] = (num) & 0xff; dst[1] = (num >> 8) & 0xff; 252 return 2; 253 } 254 255 static avi_size avi_bstr2ulong (unsigned long dst, avi_cbptr str) 256 { 257 dst = ( str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24) ); 258 return 4; 259 } 260 261 static avi_size avi_bstr2ushort (unsigned short dst, avi_cbptr str) 262 { 263 dst = ( str[0] | (str[1] << 8) ); 264 return 2; 265 } 266 267 static avi_size avi_tag2bstr (avi_bptr dst, avi_cstring tag) 268 { 269 dst[0] = (avi_byte)(0x7f & tag[0]); 270 dst[1] = (avi_byte)(0x7f & tag[1]); 271 dst[2] = (avi_byte)(0x7f & tag[2]); 272 dst[3] = (avi_byte)(0x7f & tag[3]); 273 return 4; 274 } 275 276 static unsigned long avi_tag2ulong (avi_cstring tag) 277 { 278 avi_byte dst[4]; 279 dst[0] = (avi_byte)(0x7f & tag[0]); 280 dst[1] = (avi_byte)(0x7f & tag[1]); 281 dst[2] = (avi_byte)(0x7f & tag[2]); 282 dst[3] = (avi_byte)(0x7f & tag[3]); 283 return *(unsigned long *)dst; 284 } 285 286 static avi_size avi_string_merge_path_file_postfix (avi_string dest, 287 avi_cstring path, 288 avi_cstring file, 289 avi_cstring postfix, 290 avi_size maxsize) 291 { 292 char _dest[MAX_PATH] = {0}; 293 avi_size len = strlen(strcpy(_dest, path)); 294 len += strlen(strcpy(_dest + len, "\\")); 295 len += strlen(strcpy(_dest + len, file)); 296 len += strlen(strcpy(_dest + len, postfix)); 297 298 for(avi_size i = 0, j = 0; i < MAX_PATH; i++, j++) 299 { 300 if (_dest[j] == '\\') 301 { 302 dest[i++] = '\\'; 303 } 304 dest[i] = _dest[j]; 305 if (_dest[j] == '\0') 306 return i; 307 } 308 return 0; 309 } 310 311 ////////////////////////////////////////////////////////////////////////// 312 313 static avi_hfile avi_open_file (avi_cstring file_path) 314 { 315 FILE *fp; 316 317 if ((fp = fopen(file_path,"wb+")) == NULL) 318 { 319 avi_log_error("avi_write_open: fail to open file!"); 320 return 0; 321 } 322 return fp; 323 } 324 static avi_hfile avi_open_existed_file (avi_cstring file_path) 325 { 326 FILE *fp; 327 328 if ((fp = fopen(file_path,"rb")) == NULL) 329 { 330 avi_log_error("avi_write_open: fail to open file!"); 331 return 0; 332 } 333 return fp; 334 } 335 static avi_int32 avi_close_file (avi_hfile hfile) 336 { 337 if (hfile) 338 return fclose(hfile); 339 else 340 avi_log_error("avi_close_file: invalid file handle"); 341 return 0; 342 } 343 344 static avi_size avi_read_file (avi_hfile fd, avi_bptr buf, avi_size len) 345 { 346 size_t n = 0; 347 size_t r = 0; 348 FILE *fp = (FILE *)fd; 349 350 while (r < len) { 351 n = fread (buf + r, 1, len - r, fp); 352 353 if ((avi_ssize)n <= 0) 354 return r; 355 r += n; 356 } 357 358 return r; 359 } 360 361 static avi_size avi_write_file (avi_hfile fd, avi_cbptr buf, avi_size len) 362 { 363 size_t n = 0; 364 size_t r = 0; 365 FILE *fp = (FILE *)fd; 366 367 while (r < len) { 368 n = fwrite (buf + r, 1, len - r, fp); 369 if ((avi_ssize)n < 0) 370 return n; 371 372 r += n; 373 } 374 return r; 375 } 376 377 static avi_bool avi_seek_file (avi_hfile fd, avi_int32 offset, avi_int32 fromwhere) 378 { 379 size_t n = 0; 380 size_t r = 0; 381 FILE *fp = (FILE *)fd; 382 avi_int32 flag_map[] = { SEEK_SET, SEEK_CUR, SEEK_END }; 383 384 if ( fseek (fp, offset, flag_map[fromwhere]) < 0 ) 385 { 386 avi_log_error("avi_seek_file: move point error"); 387 return avi_false; 388 } 389 390 return avi_true; 391 } 392 393 394 static avi_size avi_tell_file (avi_hfile fd) 395 { 396 return ftell(fd); 397 } 398 399 #define AVI_FILE_SIZE (avi_size)(0x100000) // 1 MB = 1048576 Byte 400 401 402 static avi_bptr avi_write_chunk(avi_bptr pb, avi_cstring tag, avi_size size) 403 { 404 LPCHUNK p = (LPCHUNK)pb; 405 p->ChunkId = avi_tag2ulong(tag); 406 p->ChunkSize = size; 407 return p->ChunkData; 408 } 409 410 static avi_bptr avi_write_chunk_with_data(avi_bptr pb, avi_cstring tag, avi_size size, void *payload, avi_size payload_size) 411 { 412 return ((avi_bptr)memcpy(avi_write_chunk(pb, tag, size), payload, payload_size) + payload_size); 413 } 414 415 ////////////////////////////////////////////////////////////////////////// 416 417 static avi_bptr avi_write_video_avih(avi_bptr pb, 418 avi_int32 frame_per_sec, 419 avi_int32 streams, 420 avi_int32 duration, 421 avi_int32 width, 422 avi_int32 height) 423 { 424 LPAVIHEADER avih; 425 426 avih = (LPAVIHEADER)avi_write_chunk(pb,"avih", sizeof(AVIHEADER)); 427 428 avih->MaximumDataRate = 400000; 429 avih->TimeBetweenFrames = 1000000/frame_per_sec; 430 avih->TotalNumberOfFrames = duration * frame_per_sec; 431 avih->NumberOfStreams = streams; 432 avih->Width = width; 433 avih->Height = height; 434 435 return (pb + sizeof(AVIHEADER) + sizeof(CHUNK)); 436 } 437 static avi_bptr avi_write_video_strh_payload(avi_bptr pb, 438 avi_int32 frame_per_sec, 439 avi_int32 record_time) 440 { 441 /** < CHUNK - strh - payload */ 442 443 LPSTREAMHEADER payload = (LPSTREAMHEADER)pb; 444 445 payload->DataType = avi_tag2ulong("vids"); 446 payload->DataHandler = avi_tag2ulong("H264"); 447 payload->TimeScale = 1000000/frame_per_sec; 448 payload->DataRate = 1000000; 449 payload->DataLength = frame_per_sec * record_time; 450 451 return (pb + sizeof(STREAMHEADER)); 452 } 453 454 static avi_bptr avi_write_video_strf_payload(avi_bptr pb, avi_int32 width, avi_int32 height) 455 { 456 /** < CHUNK - strf - payload */ 457 458 LPBITMAPINFOHEADER payload = (LPBITMAPINFOHEADER)pb; 459 460 payload->biSize = sizeof(BITMAPINFOHEADER); 461 payload->biWidth = width; 462 payload->biHeight = height; 463 payload->biPlanes = 1; 464 payload->biBitCount = 24; 465 payload->biCompression = avi_tag2ulong("H264"); 466 payload->biSizeImage = width * height; 467 468 return (pb + sizeof(BITMAPINFOHEADER)); 469 } 470 471 static avi_bptr avi_write_video_strl(avi_bptr pb, 472 avi_int32 frame_rate, 473 avi_int32 width, 474 avi_int32 height, 475 avi_int32 max_duration) 476 { 477 avi_bptr wt_ptr; 478 avi_int32 tlen = sizeof(STREAMHEADER) + sizeof(BITMAPINFOHEADER) + 8*2 + 4; 479 wt_ptr = avi_write_chunk_with_data(pb,"LIST", tlen, "strl", 4); 480 481 wt_ptr = avi_write_chunk(wt_ptr, "strh", sizeof(STREAMHEADER)); 482 wt_ptr = avi_write_video_strh_payload(wt_ptr, frame_rate, max_duration); 483 484 wt_ptr = avi_write_chunk(wt_ptr, "strf", sizeof(BITMAPINFOHEADER)); 485 wt_ptr = avi_write_video_strf_payload(wt_ptr, width, height); 486 487 return wt_ptr; 488 } 489 490 491 static avi_bptr avi_write_audio_strh_payload(avi_bptr pb, 492 avi_int32 sample_per_sec, 493 avi_int32 bits_per_sample, 494 avi_int32 channels, 495 avi_int32 max_duration) 496 { 497 /** < CHUNK - strh - payload */ 498 499 LPSTREAMHEADER payload = (LPSTREAMHEADER)pb; 500 501 payload->DataType = avi_tag2ulong("auds"); 502 //payload->DataHandler = avi_tag2ulong("H264"); 503 payload->TimeScale = (bits_per_sample >> 3) * channels; 504 payload->DataRate = payload->TimeScale * sample_per_sec; 505 payload->DataLength = sample_per_sec * max_duration; 506 payload->SampleSize = (bits_per_sample >> 3) * channels; 507 return (pb + sizeof(STREAMHEADER)); 508 } 509 510 511 static avi_bptr avi_write_audio_strf_payload(avi_bptr pb, 512 avi_int32 sample_per_sec, 513 avi_int32 bits_per_sample, 514 avi_int32 channels) 515 { 516 /** < CHUNK - strf - payload */ 517 518 LPWAVEFORMATHEADER payload = (LPWAVEFORMATHEADER)pb; 519 520 payload->wFormatTag = 1; 521 payload->nChannels = channels; 522 payload->nSamplesPerSec = sample_per_sec; 523 payload->nAvgBytesPerSec = sample_per_sec * channels * (bits_per_sample >> 3); 524 payload->nBlockAlign = channels * (bits_per_sample >> 3); 525 payload->wBitsPerSample = bits_per_sample; 526 527 return (pb + sizeof(WAVEFORMATHEADER)); 528 } 529 530 static avi_bptr avi_write_audio_strl(avi_bptr pb, 531 avi_int32 sample_per_sec, // 8000 532 avi_int32 bits_per_sample, // 16bit 533 avi_int32 channels, // 2 534 avi_int32 record_time) 535 { 536 avi_bptr wt_ptr; 537 avi_int32 tlen = sizeof(STREAMHEADER) + sizeof(WAVEFORMATHEADER) + 8*2 + 4; 538 wt_ptr = avi_write_chunk_with_data (pb,"LIST", tlen, "strl", 4); 539 540 wt_ptr = avi_write_chunk (wt_ptr, "strh", sizeof(STREAMHEADER)); 541 wt_ptr = avi_write_audio_strh_payload (wt_ptr, sample_per_sec, bits_per_sample, channels, record_time); 542 543 wt_ptr = avi_write_chunk (wt_ptr, "strf", sizeof(WAVEFORMATHEADER)); 544 wt_ptr = avi_write_audio_strf_payload (wt_ptr, sample_per_sec, bits_per_sample, channels); 545 546 return wt_ptr; 547 } 548 549 550 static avi_size avi_write_hdrl(avi_st *pst, avi_bptr buff) 551 { 552 avi_bptr pb = buff; 553 //#ifdef _DEBUG 554 // pb = avi_write_chunk_with_data (pb, "RIFF", 512, "AVI ", 4); 555 //#endif 556 pb = avi_write_chunk_with_data (pb,"LIST", 308, "hdrl", 4); 557 pb = avi_write_video_avih (pb, pst->frame_per_sec, pst->num_streams, pst->max_duration, pst->frame_width, pst->frame_height); 558 pb = avi_write_video_strl (pb, pst->frame_per_sec, pst->frame_width, pst->frame_height, pst->max_duration); 559 pb = avi_write_audio_strl (pb, pst->sample_per_sec,pst->bits_per_sample, pst->audio_channels, pst->max_duration); 560 pb = avi_write_chunk_with_data (pb,"JUNK", HEADERBYTES - 308 - 40, "hdrl", 4); 561 return (pb - buff); 562 } 563 564 ////////////////////////////////////////////////////////////////////////// 565 566 567 /* Add a chunk (=tag and data) to the AVI file, 568 returns -1 on write error, 0 on success */ 569 570 static avi_bool avi_add_chunk(avi_hfile fd, avi_cstring tag, avi_cbptr data, avi_size length) 571 { 572 avi_byte buff[8] = {0}; 573 LPCHUNK pchunk = (LPCHUNK)buff; 574 575 avi_size pos = avi_tell_file(fd); 576 577 /* Copy tag and length int c, so that we need only 1 write system call 578 for these two values */ 579 580 pchunk->ChunkId = avi_tag2ulong(tag); 581 pchunk->ChunkSize = length; 582 583 /* Output tag, length and data, restore previous position 584 if the write fails */ 585 586 length = PAD_EVEN(length); 587 588 if (avi_write_file(fd, buff, 8) != 8 || avi_write_file(fd, data, length) != length) { 589 avi_seek_file (fd, pos, AVI_SEEK_SET); 590 avi_log_error ("avi_add_chunk: write error"); 591 return avi_false; 592 } 593 594 return avi_true; 595 } 596 597 static avi_bool avi_add_index_entry(avi_hfile fd, avi_cstring tag, long flags, long pos, long len) 598 { 599 avi_size wlen; 600 AVIINDEX _index; 601 602 /* Add index entry */ 603 604 _index.Identifier = avi_tag2ulong(tag); 605 _index.Flags = flags; 606 _index.Offset = pos - HEADERMOVISTART; 607 _index.Length = len; 608 609 if (avi_write_file(fd, (avi_cbptr)&_index, sizeof(AVIINDEX)) == 0) 610 { 611 avi_log_error ("avi_add_index_entry: write file error"); 612 return avi_false; 613 } 614 615 return avi_true; 616 } 617 618 static avi_bool avi_write_data(avi_st *pst, avi_int32 index, avi_cbptr data, avi_size length, avi_int32 is_video) 619 { 620 int n; 621 avi_size len; 622 avi_hfile idx1, movi; 623 624 idx1 = pst->ohandles[index].idx1; 625 movi = pst->ohandles[index].movi; 626 627 /* Check for maximum file length */ 628 629 if ( (pst->combine_position + 8 + length + 8 + (pst->num_idx + 1) * 16) > AVI_MAX_LEN ) { 630 avi_log_error("exceed the file size limit"); 631 return avi_false; 632 } 633 634 /* Add index entry */ 635 636 if (!is_video) 637 n = avi_add_index_entry(idx1, "01wb", 0x00, pst->combine_position, length); 638 else if (is_video == 1) // 非压缩帧 639 n = avi_add_index_entry(idx1, "00db", 0x10, pst->combine_position, length); 640 else if (is_video == 2) // 压缩帧 641 n = avi_add_index_entry(idx1, "00dc", 0x10, pst->combine_position, length); 642 643 if (!n) return avi_false; 644 645 pst->combine_position += 8 + length; 646 647 /* Output tag and data */ 648 649 if (!is_video) 650 len = avi_add_chunk(movi, "01wb", data, length); 651 else if (is_video == 1) // 非压缩帧 652 len = avi_add_chunk(movi, "00db", data, length); 653 else if (is_video == 2) // 压缩帧 654 len = avi_add_chunk(movi, "00dc", data, length); 655 656 if (!len) return avi_false; 657 658 pst->combine_position += 16; 659 660 return avi_true; 661 } 662 663 static avi_bool avi_write_frame(avi_st *pst, avi_int32 index, avi_cbptr data, avi_size bytes, avi_bool is_iframe) 664 { 665 if ( avi_write_data(pst, index, data, bytes, is_iframe ? 1 : 2) ) 666 return avi_false; 667 668 pst->combine_position += bytes; 669 pst->num_frames++; 670 671 if (pst->max_frame_bytes < bytes) 672 pst->max_frame_bytes = bytes; 673 674 return avi_true; 675 } 676 677 static avi_bool avi_write_audio(avi_st *pst, avi_int32 index, avi_cbptr data, avi_size len) 678 { 679 if ( avi_write_data(pst, index, data, len, 0) ) 680 return avi_false; 681 682 pst->num_audios++; 683 return avi_true; 684 } 685 686 avi_bool avi_set_video(avi_st * pst, avi_int32 width, avi_int32 height, avi_int32 fps) 687 { 688 if (!(((width == 352) && (height == 288)) 689 || ((width == 640) && (height == 480)) 690 || ((width == 1024) && (height == 600)) 691 || ((width == 1024) && (height == 720)))) 692 return avi_false; 693 694 if (!((fps >= 10) && (fps <= 30))) 695 return avi_false; 696 697 pst->frame_width = width; 698 pst->frame_height = height; 699 pst->frame_per_sec = fps; 700 return avi_true; 701 } 702 703 avi_bool avi_set_audio(avi_st * pst, 704 avi_int32 channels, 705 avi_int32 sample_per_sec, 706 avi_int32 bits_per_sample) 707 { 708 if ((channels != 1) && (channels != 2)) 709 return avi_false; 710 711 if ((sample_per_sec != 8000) 712 && (sample_per_sec != 16000) 713 && (sample_per_sec != 44100) 714 && (sample_per_sec != 48000)) 715 return avi_false; 716 717 if ((bits_per_sample != 8) 718 && (bits_per_sample != 16) 719 && (bits_per_sample != 32)) 720 return avi_false; 721 722 pst->audio_channels = channels; 723 pst->sample_per_sec = sample_per_sec; 724 pst->bits_per_sample = bits_per_sample; 725 726 return avi_true; 727 } 728 729 avi_bool avi_set_max_duration(avi_st * pst, avi_int32 duration) 730 { 731 if ((duration <= 30) || (duration >= 60000)) 732 return avi_false; 733 734 pst->max_duration = duration; 735 736 return avi_true; 737 } 738 739 avi_int32 avi_bytes_remain(avi_st * pst) 740 { 741 return 0; 742 } 743 744 ////////////////////////////////////////////////////////////////////////// 745 746 static avi_st *avi_init(avi_cstring savedir) 747 { 748 avi_st *pst = NULL; 749 750 if (NULL == savedir) 751 { 752 avi_log_error("rbuff_init::save dir is invalid"); 753 return NULL; 754 } 755 pst = (avi_st *)avi_alloc(sizeof(avi_st)); 756 if (!pst) 757 { 758 avi_log_error("rbuff_init::alloc failed"); 759 return NULL; 760 } 761 762 memset(pst, 0, sizeof(avi_st)); 763 764 strcpy(pst->dir_path, savedir); 765 766 pst->frame_per_sec = 15; 767 pst->frame_width = 640; 768 pst->frame_height = 480; 769 pst->max_duration = 30; 770 pst->num_streams = 1; 771 772 avi_log_debug("avird_init ok"); 773 return pst; 774 } 775 776 static avi_bool avi_combine_file(avi_st * pst, avi_int32 index) 777 { 778 /** < open avi file > */ 779 char path[MAX_PATH] = {0}; 780 avi_hfile fh_avi, fh_hdrl, fh_movi, fh_idxl; 781 782 if (!pst->ohandles[index].name) 783 return avi_false; 784 785 avi_string_merge_path_file_postfix(path, 786 pst->dir_path, 787 pst->ohandles[index].name, 788 g_file_postfix[AVI_POSTFIX_AVI], 789 MAX_PATH); 790 791 if ((fh_avi = avi_open_file(path)) == NULL) 792 { 793 avi_log_error("avi_release: open avi failed"); 794 return avi_false; 795 } 796 797 avi_string_merge_path_file_postfix(path, 798 pst->dir_path, 799 pst->ohandles[index].name, 800 g_file_postfix[AVI_POSTFIX_HDRL], 801 MAX_PATH); 802 803 if ((fh_hdrl = avi_open_existed_file(path)) == NULL) 804 { 805 avi_log_error("avi_release: open hdrl failed"); 806 return avi_false; 807 } 808 809 avi_string_merge_path_file_postfix(path, 810 pst->dir_path, 811 pst->ohandles[index].name, 812 g_file_postfix[AVI_POSTFIX_MOVI], 813 MAX_PATH); 814 815 if ((fh_movi = avi_open_existed_file(path)) == NULL) 816 { 817 avi_log_error("avi_release: open movi failed"); 818 return avi_false; 819 } 820 821 avi_string_merge_path_file_postfix(path, 822 pst->dir_path, 823 pst->ohandles[index].name, 824 g_file_postfix[AVI_POSTFIX_IDX1], 825 MAX_PATH); 826 827 if ((fh_idxl = avi_open_existed_file(path)) == NULL) 828 { 829 avi_log_error("avi_release: open idxl failed"); 830 return avi_false; 831 } 832 833 ////////////////////////////////////////////////////////////////////////// 834 835 avi_byte buffer[HEADERBYTES] = {0}; 836 avi_int32 tlen, flen = 0; 837 838 avi_write_chunk_with_data (buffer, "RIFF", 512, "AVI ", 4); 839 840 avi_seek_file (fh_hdrl, 0, AVI_SEEK_END); 841 tlen = avi_tell_file (fh_hdrl); 842 avi_seek_file (fh_hdrl, 0, AVI_SEEK_SET); 843 avi_read_file (fh_hdrl, buffer + 12, (tlen-12 < HEADERBYTES)?(tlen-12):HEADERBYTES); 844 avi_write_file (fh_avi, buffer, tlen - 12); 845 avi_close_file(fh_hdrl); 846 flen += HEADERBYTES; 847 848 ////////////////////////////////////////////////////////////////////////// 849 avi_int32 rlen; 850 avi_seek_file (fh_movi, 0, AVI_SEEK_END); 851 flen += tlen = avi_tell_file (fh_movi); 852 avi_write_chunk_with_data (buffer, "LIST", tlen+4, "movi", 4); 853 avi_write_file (fh_avi, buffer, 12); 854 avi_seek_file (fh_movi, 0, AVI_SEEK_SET); 855 while (tlen) 856 { 857 rlen = avi_read_file (fh_movi, buffer, HEADERBYTES); 858 if (rlen == 0) 859 break; 860 861 avi_write_file (fh_avi, buffer, rlen); 862 tlen -= rlen; 863 } 864 avi_close_file(fh_movi); 865 //flen += tlen; 866 867 ////////////////////////////////////////////////////////////////////////// 868 avi_seek_file (fh_idxl, 0, AVI_SEEK_END); 869 flen += tlen = avi_tell_file (fh_idxl); 870 avi_write_chunk (buffer, "idx1", tlen); 871 avi_write_file (fh_avi, buffer, 8); 872 avi_seek_file (fh_idxl, 0, AVI_SEEK_SET); 873 while (tlen) 874 { 875 rlen = avi_read_file (fh_idxl, buffer, HEADERBYTES); 876 if (rlen == 0) 877 break; 878 879 avi_write_file (fh_avi, buffer, rlen); 880 tlen -= rlen; 881 } 882 avi_close_file(fh_idxl); 883 //flen += tlen; 884 885 avi_seek_file (fh_avi, 0, AVI_SEEK_SET); 886 avi_write_chunk_with_data (buffer, "RIFF", flen, "AVI ", 4); 887 avi_write_file (fh_avi, buffer, 12); 888 889 avi_close_file(fh_avi); 890 } 891 892 static avi_bool avi_release(avi_st * pst) 893 { 894 if (pst) 895 { 896 for (avi_size i = 0; i< MAX_OUTPUT_HANDLE_NUM; i++) 897 { 898 avi_combine_file(pst, i); 899 } 900 901 free(pst); 902 } 903 avi_log_debug("avi_release ok"); 904 905 return avi_true; 906 } 907 908 static avi_int32 avi_get_free_ohandle(avi_st *pst, avi_string name) 909 { 910 avi_uint32 idx; 911 912 for (idx = 0; idx < 4; idx++) 913 { 914 avi_string str = pst->ohandles[idx].name; 915 916 if ( str && strlen(str) > 0) 917 { 918 continue; 919 } 920 memset (&pst->ohandles[idx], 0, sizeof(output_handles)); 921 pst->ohandles[idx].name = name; 922 return idx; 923 } 924 return -1; 925 } 926 927 static avi_bool avi_open_output_file(avi_st *pst, avi_cstring name, avi_int32ptr index) 928 { 929 avi_hfile fh; 930 char path[MAX_PATH] = {0}; 931 avi_int32 idx; 932 933 if ( (idx = avi_get_free_ohandle(pst, name)) < 0) 934 return avi_false; 935 936 pst->combine_position = HEADERBYTES; 937 938 /** < open hdrl file > */ 939 avi_string_merge_path_file_postfix(path , pst->dir_path, name, 940 g_file_postfix[AVI_POSTFIX_HDRL], MAX_PATH); 941 942 if ((fh = avi_open_file(path)) == NULL) 943 { 944 avi_log_error("avi_open_output_file: open hdrl failed"); 945 return avi_false; 946 } 947 948 avi_byte buffer[HEADERBYTES] = {0}; 949 950 avi_write_hdrl(pst, buffer); 951 952 avi_write_file(fh, buffer, HEADERBYTES); 953 954 avi_close_file(fh); 955 956 /** < open index file > */ 957 avi_string_merge_path_file_postfix(path , pst->dir_path, name, 958 g_file_postfix[AVI_POSTFIX_IDX1], MAX_PATH); 959 960 if ((fh = avi_open_file(path)) == NULL) 961 { 962 avi_log_error("avi_open_output_file: open index failed"); 963 return avi_false; 964 } 965 966 pst->ohandles[idx].idx1 = fh; 967 968 /** < open movi file > */ 969 avi_string_merge_path_file_postfix(path , pst->dir_path, name, 970 g_file_postfix[AVI_POSTFIX_MOVI], MAX_PATH); 971 972 if ((fh = avi_open_file(path)) == NULL) 973 { 974 avi_log_error("avi_open_output_file: open movi failed"); 975 return avi_false; 976 } 977 978 pst->ohandles[idx].movi = fh; 979 980 *index = idx; 981 return avi_true; 982 } 983 984 avi_bool avi_close_output_file(avi_st * pst, avi_int32 index) 985 { 986 if (pst->ohandles[index].name != NULL) 987 { 988 avi_close_file(pst->ohandles[index].movi); 989 avi_close_file(pst->ohandles[index].idx1); 990 return avi_false; 991 } 992 993 return avi_true; 994 } 995 996 997 ////////////////////////////////////////////////////////////////////////// 998 999 avi_handle dp_avi_init(avi_string savedir) 1000 { 1001 return avi_init(savedir); 1002 } 1003 1004 avi_bool dp_avi_release(avi_handle handle) 1005 { 1006 return avi_release((avi_st *)handle); 1007 } 1008 1009 avi_bool dp_avi_open_file(avi_handle handle, avi_string file_name, avi_int32 mode, avi_int32ptr index) 1010 { 1011 if (mode == AVI_MODE_WRITE) 1012 return avi_open_output_file((avi_st *)handle, file_name, index)?avi_true:avi_false; 1013 else 1014 return avi_false; 1015 } 1016 1017 avi_bool dp_avi_close_output_file(avi_handle handle, avi_int32 index) 1018 { 1019 return avi_close_output_file((avi_st *)handle, index); 1020 } 1021 1022 avi_bool dp_avi_write_frame(avi_handle handle, avi_int32 index, avi_cbptr frame_data, avi_size bytes, avi_bool is_iframe) 1023 { 1024 return avi_write_frame((avi_st *)handle, index, frame_data, bytes, is_iframe); 1025 } 1026 1027 avi_bool dp_avi_write_audio(avi_handle handle, avi_int32 index, avi_cbptr audio_data, avi_size bytes) 1028 { 1029 return avi_write_audio((avi_st *)handle, index, audio_data, bytes); 1030 } 1031 1032 avi_bool dp_avi_set_output_frame(avi_handle handle, avi_int32 width, avi_int32 height, avi_int32 fps) 1033 { 1034 return avi_set_video((avi_st *)handle, width, height, fps); 1035 } 1036 1037 avi_bool dp_avi_set_output_audio(avi_handle handle, avi_int32 channels, avi_int32 sample_per_sec, avi_int32 bits_per_sample) 1038 { 1039 return avi_set_audio((avi_st *)handle, channels, sample_per_sec, bits_per_sample); 1040 } 1041 1042 avi_bool dp_avi_set_output_max_duration(avi_handle handle, avi_int32 duration) 1043 { 1044 return avi_set_max_duration((avi_st *)handle, duration); 1045 } 1046 1047 avi_int32 dp_avi_bytes_remain(avi_handle handle) 1048 { 1049 return avi_bytes_remain((avi_st *)handle); 1050 }
测试代码:
int _tmain(int argc, _TCHAR* argv[]) { avi_bool ret; avi_handle handle; avi_int32 index; handle = dp_avi_init("D:\\Output"); dp_avi_set_output_frame(handle, 1024,720,30); dp_avi_set_output_audio(handle, 2, 44100, 16); dp_avi_set_output_max_duration(handle, 30); ret = dp_avi_open_file(handle, "output1", AVI_MODE_WRITE,&index); if (ret) { dp_avi_write_frame(handle, index, (avi_cbptr)("\x01\x02\x03\x04\x05\x06\x07\x08\x09"), 9, 1); dp_avi_write_audio(handle, index, (avi_cbptr)("\x01\x02\x03\x04\x05\x06\x07\x08\x09"), 9); dp_avi_close_output_file(handle, index); } dp_avi_release(handle); return 0; }
关键点:
1、需要写avi文件之前设置文件的时长。
2、同时生成*.movi、*.hdrl、*.idx1 3个文件,分别保存音视频数据,avi格式和索引表。
3、最后,在释放的时候,将上面3个文件合并成一个文件,原因是hdrl文件除了时长,其他信息都是固定的。
而movi文件会不停添加音视频数据,更新频繁;而idx1文件可以记录关键索引和非关键索引,可以配置。
4、尽量排除与平台相关的实现代码。