Files
ANSCORE/MediaClient/media/avi_write.cpp

1145 lines
26 KiB
C++

/***************************************************************************************
*
* 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 <math.h>
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;
}