1145 lines
26 KiB
C++
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;
|
|
}
|
|
|
|
|
|
|