/*************************************************************************************** * * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. * * By downloading, copying, installing or using the software you agree to this license. * If you do not agree to this license, do not download, install, * copy or use the software. * * Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved. * * Redistribution and use in binary forms, with or without modification, are permitted. * * Unless required by applicable law or agreed to in writing, software distributed * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific * language governing permissions and limitations under the License. * ****************************************************************************************/ #include "sys_inc.h" #include "avi.h" #include "avi_write.h" #include "h264.h" #include "h265.h" #include "mjpeg.h" #include "media_util.h" #include "h264_util.h" #include "h265_util.h" #include int avi_write_uint16_(AVICTX * p_ctx, uint16 w) { return fwrite(&w, 2, 1, p_ctx->fp); } int avi_write_uint32_(AVICTX * p_ctx, uint32 dw) { return fwrite(&dw, 4, 1, p_ctx->fp); } int avi_write_fourcc_(AVICTX * p_ctx, const char fcc[4]) { return fwrite(fcc, 4, 1, p_ctx->fp); } int avi_write_buffer_(AVICTX * p_ctx, char * p_data, int len) { return fwrite(p_data, len, 1, p_ctx->fp); } #define avi_write_uint16(p_ctx, w) do{ if(avi_write_uint16_(p_ctx, w) != 1) goto w_err; }while(0) #define avi_write_uint32(p_ctx, w) do{ if(avi_write_uint32_(p_ctx, w) != 1) goto w_err; }while(0) #define avi_write_fourcc(p_ctx, fcc) do{ if(avi_write_fourcc_(p_ctx, fcc) != 1) goto w_err; }while(0) #define avi_write_buffer(p_ctx, p_data, len) do{ if(avi_write_buffer_(p_ctx, p_data, len) != 1) goto w_err; }while(0) void avi_free_idx(AVICTX * p_ctx) { if (p_ctx->idx) { free(p_ctx->idx); p_ctx->idx = NULL; } if (p_ctx->idx_fp) { fclose(p_ctx->idx_fp); p_ctx->idx_fp = NULL; } p_ctx->i_idx = 0; p_ctx->i_idx_max = 0; } int avi_write_idx(AVICTX * p_ctx) { avi_write_fourcc(p_ctx, "idx1"); avi_write_uint32(p_ctx, p_ctx->i_idx * 16); if (p_ctx->ctxf_idx_m == 1) { if (p_ctx->i_idx > 0) { if (fwrite(p_ctx->idx, p_ctx->i_idx * 16, 1, p_ctx->fp) != 1) { return -1; } } } else if (p_ctx->idx_fp) { // Write index data in a fixed buffer to an index file if (p_ctx->idx_fix_off > 0) { if (fwrite(p_ctx->idx_fix, p_ctx->idx_fix_off * 4, 1, p_ctx->idx_fp) != 1) { goto w_err; } p_ctx->idx_fix_off = 0; } // Write index temporary file data to the avi file fseek(p_ctx->idx_fp, 0, SEEK_END); int idx_len = ftell(p_ctx->idx_fp); if (idx_len != (p_ctx->i_idx * 16)) { log_print(HT_LOG_ERR, "%s, idx real len[%d] != idx file len[%d],idx_fix_off[%d]!!!\r\n", __FUNCTION__, p_ctx->i_idx * 16, idx_len, p_ctx->idx_fix_off * 4); return -1; } fseek(p_ctx->fp, 0, SEEK_END); fseek(p_ctx->idx_fp, 0, SEEK_SET); int rlen; do { rlen = fread(p_ctx->idx_fix, 1, sizeof(p_ctx->idx_fix), p_ctx->idx_fp); if (rlen <= 0) { break; } if (fwrite(p_ctx->idx_fix, rlen, 1, p_ctx->fp) != 1) { log_print(HT_LOG_ERR, "%s, write idx into avi file failed!!!\r\n", __FUNCTION__); return -1; } } while(rlen > 0); } return 0; w_err: return -1; } void avi_set_dw(void * p, uint32 dw) { uint8 * ptr = (uint8 *)p; ptr[0] = (dw ) & 0xff; ptr[1] = (dw >> 8 ) & 0xff; ptr[2] = (dw >> 16) & 0xff; ptr[3] = (dw >> 24) & 0xff; } int avi_end(AVICTX * p_ctx) { if (p_ctx->fp == NULL) { return -1; } p_ctx->i_movi_end = ftell(p_ctx->fp); if (avi_write_idx(p_ctx) < 0) { goto end_err; } p_ctx->i_riff = ftell(p_ctx->fp); if (avi_write_header(p_ctx) < 0) { goto end_err; } // AVI file has been completed, delete the temporary index file if (p_ctx->idx_fp) { fclose(p_ctx->idx_fp); p_ctx->idx_fp = NULL; #if defined(ANDROID) char cmds[512]; snprintf(cmds, sizeof(cmds)-1, "rm -f %s.idx", p_ctx->filename); system(cmds); #else char filename[512]; snprintf(filename, sizeof(filename)-1, "%s.idx", p_ctx->filename); remove(filename); #endif } end_err: if (p_ctx->fp) { fclose(p_ctx->fp); p_ctx->fp = NULL; } avi_free_idx(p_ctx); return 0; } /**************************************************************************/ AVICTX * avi_write_open(const char * filename) { AVICTX * p_ctx = (AVICTX *)malloc(sizeof(AVICTX)); if (p_ctx == NULL) { log_print(HT_LOG_ERR, "%s, malloc fail!!!\r\n", __FUNCTION__); return NULL; } memset(p_ctx, 0, sizeof(AVICTX)); p_ctx->ctxf_write = 1; p_ctx->fp = fopen(filename, "wb+"); if (p_ctx->fp == NULL) { free(p_ctx); log_print(HT_LOG_ERR, "%s, fopen [%s] failed!!!\r\n", __FUNCTION__, filename); return NULL; } strncpy(p_ctx->filename, filename, sizeof(p_ctx->filename)); char idx_path[256]; snprintf(idx_path, sizeof(idx_path), "%s.idx", filename); p_ctx->idx_fp = fopen(idx_path, "wb+"); if (p_ctx->idx_fp == NULL) { fclose(p_ctx->fp); free(p_ctx); log_print(HT_LOG_ERR, "%s, fopen [%s] failed!!!\r\n", __FUNCTION__, idx_path); return NULL; } p_ctx->mutex = sys_os_create_mutex(); return p_ctx; } int avi_write_video_frame(AVICTX * p_ctx, void * p_data, uint32 len, uint32 ts, int b_key, int frame) { int ret = -1; if (NULL == p_ctx || NULL == p_ctx->fp) { return -1; } int i_pos = ftell(p_ctx->fp); avi_write_fourcc(p_ctx, "00dc"); avi_write_uint32(p_ctx, len); if (fwrite(p_data, len, 1, p_ctx->fp) != 1) { goto w_err; } if (len & 0x01) /* pad */ { fputc(0, p_ctx->fp); } if (p_ctx->ctxf_idx_m == 1) { if (p_ctx->i_idx_max <= p_ctx->i_idx) { p_ctx->i_idx_max += 1000; p_ctx->idx = (int *)realloc(p_ctx->idx, p_ctx->i_idx_max * 16); if (p_ctx->idx == NULL) { log_print(HT_LOG_ERR, "%s, realloc ret null!!!\r\n", __FUNCTION__); } } if (p_ctx->idx) { memcpy(&p_ctx->idx[4*p_ctx->i_idx+0], "00dc", 4); avi_set_dw(&p_ctx->idx[4*p_ctx->i_idx+1], b_key ? AVIIF_KEYFRAME : 0); avi_set_dw(&p_ctx->idx[4*p_ctx->i_idx+2], i_pos); avi_set_dw(&p_ctx->idx[4*p_ctx->i_idx+3], len); p_ctx->i_idx++; } } else if (p_ctx->idx_fp) { memcpy(&p_ctx->idx_fix[p_ctx->idx_fix_off + 0], "00dc", 4); avi_set_dw(&p_ctx->idx_fix[p_ctx->idx_fix_off + 1], b_key ? AVIIF_KEYFRAME : 0); avi_set_dw(&p_ctx->idx_fix[p_ctx->idx_fix_off + 2], i_pos); avi_set_dw(&p_ctx->idx_fix[p_ctx->idx_fix_off + 3], len); p_ctx->idx_fix_off += 4; if (p_ctx->idx_fix_off == (sizeof(p_ctx->idx_fix) / sizeof(int))) { if (fwrite(p_ctx->idx_fix, sizeof(p_ctx->idx_fix), 1, p_ctx->idx_fp) != 1) { goto w_err; } p_ctx->idx_fix_off = 0; } p_ctx->i_idx++; } if (frame) { p_ctx->i_frame_video++; if (p_ctx->v_s_time == 0) { p_ctx->v_s_time = sys_os_get_ms(); p_ctx->v_e_time = p_ctx->v_s_time; } else { p_ctx->v_e_time = sys_os_get_ms(); } } p_ctx->prev_ts = ts; ret = ftell(p_ctx->fp); w_err: if (ret < 0) { log_print(HT_LOG_ERR, "%s, ret[%d] err[%d] [%s]!!!\r\n", __FUNCTION__, ret, errno, strerror(errno)); if (p_ctx->fp) { fclose(p_ctx->fp); p_ctx->fp = NULL; } if (p_ctx->idx_fp) { fclose(p_ctx->idx_fp); p_ctx->idx_fp = NULL; } } return ret; } int avi_write_h264_nalu(AVICTX * p_ctx, uint8 * sps, int sps_len, uint8 * pps, int pps_len) { int flag = 0; if (sps_len > 0 && sps) { if (avi_write_video_frame(p_ctx, sps, sps_len, 0, 0, 0) > 0) { flag = 1; } } if (pps_len > 0 && pps) { if (avi_write_video_frame(p_ctx, pps, pps_len, 0, 0, 0) > 0) { flag = 1; } } p_ctx->ctxf_nalu = flag; return flag; } int avi_write_h265_nalu(AVICTX * p_ctx, uint8 * vps, int vps_len, uint8 * sps, int sps_len, uint8 * pps, int pps_len) { int flag = 0; if (vps_len > 0 && vps) { if (avi_write_video_frame(p_ctx, vps, vps_len, 0, 0, 0) > 0) { flag = 1; } } if (sps_len > 0 && sps) { if (avi_write_video_frame(p_ctx, sps, sps_len, 0, 0, 0) > 0) { flag = 1; } } if (pps_len > 0 && pps) { if (avi_write_video_frame(p_ctx, pps, pps_len, 0, 0, 0) > 0) { flag = 1; } } p_ctx->ctxf_nalu = flag; return flag; } int avi_write_h264(AVICTX * p_ctx, void * p_data, uint32 len, uint32 ts, int b_key) { if (p_ctx->ctxf_nalu) { if (!p_ctx->ctxf_iframe) { if (b_key) { if (avi_write_video_frame(p_ctx, p_data, len, ts, b_key, 1) < 0) { log_print(HT_LOG_ERR, "%s, avi_write_video_frame failed\r\n", __FUNCTION__); return -1; } else { p_ctx->ctxf_iframe = 1; } } } else { if (avi_write_video_frame(p_ctx, p_data, len, ts, b_key, 1) < 0) { log_print(HT_LOG_ERR, "%s, avi_write_video_frame failed\r\n", __FUNCTION__); return -1; } } } return 0; } int avi_write_h265(AVICTX * p_ctx, void * p_data, uint32 len, uint32 ts, int b_key) { if (p_ctx->ctxf_nalu) { if (!p_ctx->ctxf_iframe) { if (b_key) { if (avi_write_video_frame(p_ctx, p_data, len, ts, b_key, 1) < 0) { log_print(HT_LOG_ERR, "%s, avi_write_video_frame failed\r\n", __FUNCTION__); return -1; } else { p_ctx->ctxf_iframe = 1; } } } else { if (avi_write_video_frame(p_ctx, p_data, len, ts, b_key, 1) < 0) { log_print(HT_LOG_ERR, "%s, avi_write_video_frame failed\r\n", __FUNCTION__); return -1; } } } return 0; } int avi_write_video(AVICTX * p_ctx, uint8 * p_data, uint32 len, uint32 ts, int b_key) { int ret = 0; sys_os_mutex_enter(p_ctx->mutex); if (p_ctx->v_fps == 0) { avi_calc_fps(p_ctx); } if (memcmp(p_ctx->v_fcc, "H264", 4) == 0) { ret = avi_write_h264(p_ctx, p_data, len, ts, b_key); } else if (memcmp(p_ctx->v_fcc, "H265", 4) == 0) { ret = avi_write_h265(p_ctx, p_data, len, ts, b_key); } else { ret = avi_write_video_frame(p_ctx, p_data, len, ts, b_key, 1); } sys_os_mutex_leave(p_ctx->mutex); return ret; } int avi_write_nalu(AVICTX * p_ctx, uint8 * vps, int vps_len, uint8 * sps, int sps_len, uint8 * pps, int pps_len) { int ret = 0; if (p_ctx->ctxf_nalu) { return 0; } sys_os_mutex_enter(p_ctx->mutex); if (memcmp(p_ctx->v_fcc, "H264", 4) == 0) { ret = avi_write_h264_nalu(p_ctx, sps, sps_len, pps, pps_len); } else if (memcmp(p_ctx->v_fcc, "H265", 4) == 0) { ret = avi_write_h265_nalu(p_ctx, vps, vps_len, sps, sps_len, pps, pps_len); } sys_os_mutex_leave(p_ctx->mutex); return ret; } int avi_write_audio(AVICTX * p_ctx, uint8 * p_data, uint32 len, uint32 ts) { int ret = -1; if (NULL == p_ctx) { return -1; } if (p_ctx->ctxf_video) { if (memcmp(p_ctx->v_fcc, "H264", 4) == 0 || memcmp(p_ctx->v_fcc, "H265", 4) == 0) { if (!p_ctx->ctxf_iframe) { return 0; } } } if (p_ctx->a_fmt == AUDIO_FORMAT_AAC) // AAC { if (p_data[0] == 0xFF && (p_data[1] & 0xF0) == 0xF0) { // skip ADTS header p_data += 7; len -= 7; } } sys_os_mutex_enter(p_ctx->mutex); if (NULL == p_ctx->fp) { sys_os_mutex_leave(p_ctx->mutex); return -1; } int i_pos = ftell(p_ctx->fp); /* chunk header */ avi_write_fourcc(p_ctx, "01wb"); avi_write_uint32(p_ctx, len); if (fwrite(p_data, len, 1, p_ctx->fp) != 1) { goto w_err; } if (len & 0x01) /* pad */ { fputc(0, p_ctx->fp); } if (p_ctx->ctxf_idx_m == 1) { if (p_ctx->i_idx_max <= p_ctx->i_idx) { p_ctx->i_idx_max += 1000; p_ctx->idx = (int *)realloc(p_ctx->idx, p_ctx->i_idx_max * 16); if (p_ctx->idx == NULL) { log_print(HT_LOG_ERR, "%s, realloc ret null!!!\r\n", __FUNCTION__); } } if (p_ctx->idx) { memcpy(&p_ctx->idx[4*p_ctx->i_idx+0], "01wb", 4); avi_set_dw(&p_ctx->idx[4*p_ctx->i_idx+1], AVIIF_KEYFRAME); avi_set_dw(&p_ctx->idx[4*p_ctx->i_idx+2], i_pos); avi_set_dw(&p_ctx->idx[4*p_ctx->i_idx+3], len); p_ctx->i_idx++; } } else if (p_ctx->idx_fp) { memcpy(&p_ctx->idx_fix[p_ctx->idx_fix_off + 0], "01wb", 4); avi_set_dw(&p_ctx->idx_fix[p_ctx->idx_fix_off + 1], AVIIF_KEYFRAME); avi_set_dw(&p_ctx->idx_fix[p_ctx->idx_fix_off + 2], i_pos); avi_set_dw(&p_ctx->idx_fix[p_ctx->idx_fix_off + 3], len); p_ctx->idx_fix_off += 4; if (p_ctx->idx_fix_off == (sizeof(p_ctx->idx_fix) / sizeof(int))) { if (fwrite(p_ctx->idx_fix, sizeof(p_ctx->idx_fix), 1, p_ctx->idx_fp) != 1) { goto w_err; } p_ctx->idx_fix_off = 0; } p_ctx->i_idx++; } p_ctx->i_frame_audio++; p_ctx->audio_total_bytes += len; if (p_ctx->a_s_time == 0) { p_ctx->a_s_time = sys_os_get_ms(); p_ctx->a_e_time = p_ctx->a_s_time; } else { p_ctx->a_e_time = sys_os_get_ms(); } ret = ftell(p_ctx->fp); w_err: if (ret < 0) { log_print(HT_LOG_ERR, "%s, ret[%d]!!!\r\n", __FUNCTION__, ret); if (p_ctx->fp) { fclose(p_ctx->fp); p_ctx->fp = NULL; } if (p_ctx->idx_fp) { fclose(p_ctx->idx_fp); p_ctx->idx_fp = NULL; } } sys_os_mutex_leave(p_ctx->mutex); return ret; } void avi_write_close(AVICTX * p_ctx) { if (NULL == p_ctx) { return; } sys_os_mutex_enter(p_ctx->mutex); avi_end(p_ctx); avi_free_idx(p_ctx); if (p_ctx->v_extra) { free(p_ctx->v_extra); } if (p_ctx->a_extra) { free(p_ctx->a_extra); } sys_os_mutex_leave(p_ctx->mutex); sys_os_destroy_sig_mutex(p_ctx->mutex); free(p_ctx); } void avi_set_video_info(AVICTX * p_ctx, int fps, int width, int height, const char fcc[4]) { memcpy(p_ctx->v_fcc, fcc, 4); // "H264","H265","JPEG","MP4V" p_ctx->v_fps = fps; p_ctx->v_width = width; p_ctx->v_height = height; p_ctx->ctxf_video = 1; p_ctx->ctxf_calcfps = fps > 0 ? 0 : 1; } void avi_set_video_extra_info(AVICTX * p_ctx, uint8 * extra, int extra_len) { if (NULL != extra && extra_len > 0) { if (p_ctx->v_extra) { free(p_ctx->v_extra); } p_ctx->v_extra_len = 0; p_ctx->v_extra = (uint8*) malloc(extra_len); if (p_ctx->v_extra) { memcpy(p_ctx->v_extra, extra, extra_len); p_ctx->v_extra_len = extra_len; } } } void avi_set_audio_info(AVICTX * p_ctx, int chns, int rate, uint16 fmt) { p_ctx->a_chns = chns; p_ctx->a_rate = rate; p_ctx->a_fmt = fmt; p_ctx->ctxf_audio = 1; } void avi_set_audio_extra_info(AVICTX * p_ctx, uint8 * extra, int extra_len) { if (NULL != extra && extra_len > 0) { if (p_ctx->a_extra) { free(p_ctx->a_extra); } p_ctx->a_extra_len = 0; p_ctx->a_extra = (uint8*) malloc(extra_len); if (p_ctx->a_extra) { memcpy(p_ctx->a_extra, extra, extra_len); p_ctx->a_extra_len = extra_len; } } } void avi_build_video_hdr(AVICTX * p_ctx) { if (p_ctx->ctxf_video == 0) { return; } avi_calc_fps(p_ctx); memcpy(&p_ctx->str_v.fccHandler, p_ctx->v_fcc, 4); p_ctx->str_v.fccType = mmioFOURCC('v','i','d','s'); p_ctx->str_v.dwFlags = 0; // Contains AVITF_ flags p_ctx->str_v.wPriority = 0; p_ctx->str_v.wLanguage = 0; p_ctx->str_v.dwInitialFrames = 0; p_ctx->str_v.dwScale = 1; p_ctx->str_v.dwRate = (p_ctx->v_fps == 0) ? 25 : p_ctx->v_fps; // dwRate / dwScale == samplessecond p_ctx->str_v.dwStart = 0; p_ctx->str_v.dwLength = p_ctx->i_frame_video; // In units above.. p_ctx->str_v.dwSuggestedBufferSize = 1024*1024; p_ctx->str_v.dwQuality = -1; p_ctx->str_v.dwSampleSize = 0; p_ctx->str_v.rcFrame.left = 0; p_ctx->str_v.rcFrame.top = 0; p_ctx->str_v.rcFrame.right = p_ctx->v_width; p_ctx->str_v.rcFrame.bottom = p_ctx->v_height; memcpy(&p_ctx->bmp.biCompression, p_ctx->v_fcc, 4); p_ctx->bmp.biSize = sizeof(BMPHDR); p_ctx->bmp.biWidth = p_ctx->v_width; p_ctx->bmp.biHeight = p_ctx->v_height; p_ctx->bmp.biPlanes = 1; p_ctx->bmp.biBitCount = 24; p_ctx->bmp.biSizeImage = p_ctx->v_width * p_ctx->v_height * 3; p_ctx->bmp.biXPelsPerMeter = 0; p_ctx->bmp.biYPelsPerMeter = 0; p_ctx->bmp.biClrUsed = 0; p_ctx->bmp.biClrImportant = 0; } void avi_build_audio_hdr(AVICTX * p_ctx) { if (p_ctx->ctxf_audio == 0) { return; } int bps, blkalign, bytespersec; uint32 scale = 1; uint32 length; if (p_ctx->a_fmt == AUDIO_FORMAT_ALAW) // G711A { bps = 8; blkalign = p_ctx->a_chns; bytespersec = p_ctx->a_rate * p_ctx->a_chns; length = p_ctx->audio_total_bytes / blkalign; } else if (p_ctx->a_fmt == AUDIO_FORMAT_MULAW) // G711U { bps = 8; blkalign = p_ctx->a_chns; bytespersec = p_ctx->a_rate * p_ctx->a_chns; length = p_ctx->audio_total_bytes / blkalign; } else if (p_ctx->a_fmt == AUDIO_FORMAT_AAC) // AAC { bps = 16; blkalign = 768 * p_ctx->a_chns; /* maximum bytes per frame */ bytespersec = p_ctx->a_rate * p_ctx->a_chns / 8; scale = 1024; length = p_ctx->i_frame_audio; } else if (p_ctx->a_fmt == AUDIO_FORMAT_G726) // G726 { bps = 8; blkalign = p_ctx->a_chns; bytespersec = p_ctx->a_rate * p_ctx->a_chns; length = p_ctx->audio_total_bytes / blkalign; } else if (p_ctx->a_fmt == AUDIO_FORMAT_G722) // G722 { bps = 4; blkalign = p_ctx->a_chns; bytespersec = p_ctx->a_rate * p_ctx->a_chns; length = p_ctx->audio_total_bytes / blkalign; } else { bps = 8; blkalign = p_ctx->a_chns; bytespersec = p_ctx->a_rate * p_ctx->a_chns; length = p_ctx->audio_total_bytes / blkalign; } if (p_ctx->audio_total_bytes > 0 && p_ctx->a_e_time > p_ctx->a_s_time + 5000) { bytespersec = p_ctx->audio_total_bytes / ((p_ctx->a_e_time - p_ctx->a_s_time) / 1000); } p_ctx->str_a.fccType = mmioFOURCC('a','u','d','s'); p_ctx->str_a.fccHandler = 1; p_ctx->str_a.dwFlags = 0; // Contains AVITF_ flags p_ctx->str_a.wPriority = 0; p_ctx->str_a.wLanguage = 0; p_ctx->str_a.dwInitialFrames = 0; p_ctx->str_a.dwScale = scale; p_ctx->str_a.dwRate = p_ctx->a_rate; p_ctx->str_a.dwStart = 0; p_ctx->str_a.dwLength = length; p_ctx->str_a.dwSuggestedBufferSize = 12*1024; p_ctx->str_a.dwQuality = -1; p_ctx->str_a.dwSampleSize = blkalign; p_ctx->str_a.rcFrame.left = 0; p_ctx->str_a.rcFrame.top = 0; p_ctx->str_a.rcFrame.right = 0; p_ctx->str_a.rcFrame.bottom = 0; p_ctx->wave.wFormatTag = p_ctx->a_fmt; p_ctx->wave.nChannels = p_ctx->a_chns; p_ctx->wave.nSamplesPerSec = p_ctx->a_rate; p_ctx->wave.nAvgBytesPerSec = bytespersec; p_ctx->wave.nBlockAlign = blkalign; p_ctx->wave.wBitsPerSample = bps; p_ctx->wave.cbSize = p_ctx->a_extra_len; } int avi_write_header(AVICTX * p_ctx) { if (p_ctx->fp == NULL) { return -1; } log_print(HT_LOG_DBG, "%s, enter...\r\n", __FUNCTION__); avi_build_video_hdr(p_ctx); avi_build_audio_hdr(p_ctx); fseek(p_ctx->fp, 0, SEEK_SET); int tlen; int avih_len = sizeof(AVIMHDR) + 8; int strl_v_len = sizeof(AVISHDR) + 8 + sizeof(BMPHDR) + 8; int strl_a_len = sizeof(AVISHDR) + 8 + sizeof(WAVEFMT) + 8; int s_v_ll = strl_v_len + 12; int s_a_ll = strl_a_len + 12; int a_extra_len = 0; int a_pad_len = 0; int v_extra_len = 0; int v_pad_len = 0; if (p_ctx->a_extra && p_ctx->a_extra_len) { a_extra_len = p_ctx->a_extra_len; if (a_extra_len & 0x01) { a_pad_len = 1; } } if (p_ctx->v_extra && p_ctx->v_extra_len) { v_extra_len = p_ctx->v_extra_len; if (v_extra_len & 0x01) { v_pad_len = 1; } } avi_write_fourcc(p_ctx, "RIFF"); avi_write_uint32(p_ctx, p_ctx->i_riff > 0 ? p_ctx->i_riff - 8 : 0xFFFFFFFF); // Total file length - ('RIFF') - 4 avi_write_fourcc(p_ctx, "AVI "); avi_write_fourcc(p_ctx, "LIST"); tlen = 4 + avih_len; if (p_ctx->ctxf_video == 1) { tlen += s_v_ll + v_extra_len + v_pad_len; } if (p_ctx->ctxf_audio == 1) { tlen += s_a_ll + a_extra_len + a_pad_len; } avi_write_uint32(p_ctx, tlen); avi_write_fourcc(p_ctx, "hdrl"); avi_write_fourcc(p_ctx, "avih"); avi_write_uint32(p_ctx, sizeof(AVIMHDR)); if (p_ctx->v_fps == 0) { p_ctx->avi_hdr.dwMicroSecPerFrame = 1000000 / 25; // Video frame interval (in microseconds) } else { p_ctx->avi_hdr.dwMicroSecPerFrame = 1000000 / p_ctx->v_fps; // Video frame interval (in microseconds) } p_ctx->avi_hdr.dwMaxBytesPerSec = 0xffffffff; // The maximum data rate of this AVI file p_ctx->avi_hdr.dwPaddingGranularity = 0; // Granularity of data padding p_ctx->avi_hdr.dwFlags = AVIF_HASINDEX|AVIF_ISINTERLEAVED|AVIF_TRUSTCKTYPE; p_ctx->avi_hdr.dwTotalFrames = p_ctx->i_frame_video; // Total number of frames p_ctx->avi_hdr.dwInitialFrames = 0; // Specify the initial number of frames for the interactive format if (p_ctx->ctxf_audio == 1) { p_ctx->avi_hdr.dwStreams = 2; // The number of streams included in this file } else { p_ctx->avi_hdr.dwStreams = 1; // The number of streams included in this file } p_ctx->avi_hdr.dwSuggestedBufferSize= 1024*1024; // It is recommended to read the cache size of this file (should be able to accommodate the largest block) p_ctx->avi_hdr.dwWidth = p_ctx->v_width; // The width of the video in pixels p_ctx->avi_hdr.dwHeight = p_ctx->v_height; // The height of the video in pixels avi_write_buffer(p_ctx, (char*)&p_ctx->avi_hdr, sizeof(AVIMHDR)); if (p_ctx->ctxf_video == 1) { avi_write_fourcc(p_ctx, "LIST"); avi_write_uint32(p_ctx, 4 + strl_v_len + v_extra_len + v_pad_len); avi_write_fourcc(p_ctx, "strl"); // How many streams are there in the file, and how many 'strl' sublists there are avi_write_fourcc(p_ctx, "strh"); avi_write_uint32(p_ctx, sizeof(AVISHDR)); avi_write_buffer(p_ctx, (char*)&p_ctx->str_v, sizeof(AVISHDR)); avi_write_fourcc(p_ctx, "strf"); avi_write_uint32(p_ctx, sizeof(BMPHDR) + v_extra_len); avi_write_buffer(p_ctx, (char*)&p_ctx->bmp, sizeof(BMPHDR)); // Write video extra information if (p_ctx->v_extra && p_ctx->v_extra_len) { avi_write_buffer(p_ctx, (char*)p_ctx->v_extra, p_ctx->v_extra_len); if (v_pad_len) /* pad */ { fputc(0, p_ctx->fp); } } } if (p_ctx->ctxf_audio == 1) { avi_write_fourcc(p_ctx, "LIST"); avi_write_uint32(p_ctx, 4 + strl_a_len + a_extra_len + a_pad_len); avi_write_fourcc(p_ctx, "strl"); avi_write_fourcc(p_ctx, "strh"); avi_write_uint32(p_ctx, sizeof(AVISHDR)); avi_write_buffer(p_ctx, (char*)&p_ctx->str_a, sizeof(AVISHDR)); avi_write_fourcc(p_ctx, "strf"); avi_write_uint32(p_ctx, sizeof(WAVEFMT) + a_extra_len); avi_write_buffer(p_ctx, (char*)&p_ctx->wave, sizeof(WAVEFMT)); // Write audio extra information if (p_ctx->a_extra && p_ctx->a_extra_len) { avi_write_buffer(p_ctx, (char*)p_ctx->a_extra, p_ctx->a_extra_len); if (a_pad_len) /* pad */ { fputc(0, p_ctx->fp); } } } avi_write_fourcc(p_ctx, "LIST"); avi_write_uint32(p_ctx, p_ctx->i_movi_end > 0 ? (p_ctx->i_movi_end - p_ctx->i_movi + 4) : 0xFFFFFFFF); avi_write_fourcc(p_ctx, "movi"); p_ctx->i_movi = ftell(p_ctx->fp); if (p_ctx->i_movi < 0) { goto w_err; } return 0; w_err: if (p_ctx->fp) { fclose(p_ctx->fp); p_ctx->fp = NULL; } return -1; } int avi_update_header(AVICTX * p_ctx) { log_print(HT_LOG_DBG, "%s, enter...\r\n", __FUNCTION__); if (NULL == p_ctx) { return -1; } sys_os_mutex_enter(p_ctx->mutex); if (NULL == p_ctx->fp) { sys_os_mutex_leave(p_ctx->mutex); return -1; } if (avi_write_header(p_ctx) == 0) { if (p_ctx->fp) { fseek(p_ctx->fp, 0, SEEK_END); sys_os_mutex_leave(p_ctx->mutex); return 0; } } sys_os_mutex_leave(p_ctx->mutex); return -1; } int avi_calc_fps(AVICTX * p_ctx) { if (p_ctx->ctxf_calcfps && p_ctx->i_frame_video >= 100) { float fps = (float) (p_ctx->i_frame_video * 1000.0) / (p_ctx->v_e_time - p_ctx->v_s_time); p_ctx->v_fps = (uint32)(fps - 0.5); log_print(HT_LOG_DBG, "%s, stime=%u, etime=%u, frames=%d, fps=%d\r\n", __FUNCTION__, p_ctx->v_s_time, p_ctx->v_e_time, p_ctx->i_frame_video, p_ctx->v_fps); } return 0; } uint64 avi_get_file_length(AVICTX * p_ctx) { if (p_ctx && p_ctx->fp) { return ftell(p_ctx->fp); } return 0; } uint32 avi_get_media_time(AVICTX * p_ctx) { if (NULL == p_ctx) { return 0; } uint32 vtime = 0, atime = 0; if (p_ctx->ctxf_video) { vtime = p_ctx->v_e_time - p_ctx->v_s_time; } if (p_ctx->ctxf_audio) { atime = p_ctx->a_e_time - p_ctx->a_s_time; } return vtime > atime ? vtime : atime; }