Files
TAIXIN/sdk/lib/video/miniMP4/mp4_demux_msi.c

1718 lines
56 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "osal/string.h"
#include "fatfs/osal_file.h"
#include "stream_define.h"
#include "adts.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
/**************************************************************************************************************
* stts:记录每一帧的解码时间,如果时间一致,count增加,压缩大小,所以录像的时候,可以适当修改解码时间,
* 让解码时间尽量一样,可以减少存储空间也有助于解码需要的空间
* stsc:chunk_box,代表每一个chunk有多少个sample,可能现阶段只考虑普通的,后续遇到不同的mp4视频,再去实现
* stsz:每一帧数据的size
* stco:每一个chunk的偏移
* stss:关键帧的位置(第几帧)
*
*
* 这里是简单的mp4解码,暂时不考虑多流情况,解析的是minimp4保存单视频的文件
**************************************************************************************************************/
#define MP4_ABORT(a) \
{ \
if (a) \
{ \
ret = __LINE__; \
goto abort_end; \
} \
else \
{ \
ret = 0; \
} \
}
// 结构体申请空间函数
#define STREAM_MALLOC av_psram_malloc
#define STREAM_FREE av_psram_free
#define STREAM_ZALLOC av_psram_zalloc
// 结构体申请空间函数
#define STREAM_LIBC_MALLOC os_malloc
#define STREAM_LIBC_FREE os_free
#define STREAM_LIBC_ZALLOC os_zalloc
#define MAX_MP4_DEMUX_TX 8
#define MAX_TRY_COUNT (10)
extern uint8_t *mp4_demux_get_sps(struct msi *msi, uint16_t *sps_len);
extern uint8_t *mp4_demux_get_pps(struct msi *msi, uint16_t *pps_len);
enum MP4_DEMUX_EVT
{
MP4_DEMUX_JMP = BIT(0),
MP4_DEMUX_START = BIT(1),
MP4_DEMUX_STOP = BIT(2),
MP4_DEMUX_EXIT = BIT(3),
};
typedef struct
{
uint8_t reserved4[4];
uint8_t reserved2[2];
uint16_t dataReferenceIndex;
uint16_t version, revisionLevel;
uint32_t vendor;
uint16_t channelCount;
uint16_t sampleSize;
uint8_t reserved[4];
uint32_t time_scale;
} mp4_mp4a;
typedef struct
{
uint32_t sample_count;
uint32_t sample_delta;
} mp4_stts;
typedef struct
{
uint32_t first_chunk;
uint32_t sample_per_chunk;
uint32_t sample_description_index;
} mp4_stsc;
typedef struct
{
uint32_t sample_size;
} mp4_stsz;
typedef struct
{
uint32_t sample_time;
} mp4_stsz_time;
typedef struct
{
uint32_t chunk_offset; // Sample size MP4 Explorer
} mp4_stco;
typedef struct
{
uint32_t sample_number;
} mp4_stss;
typedef struct
{
uint8_t version;
uint8_t flags[3];
uint8_t pre_defined[4];
uint8_t handler_type[4];
uint8_t reserved[12];
} mp4_hdlr;
typedef struct
{
uint8_t version;
uint8_t flags[3];
uint32_t creation_time;
uint32_t modification_time;
uint32_t track_id;
uint32_t reserved1;
uint32_t duration;
uint32_t reserved2[2];
uint16_t layer;
uint16_t alternate_group;
uint16_t volume;
uint16_t reserved3;
uint8_t matrix[36];
uint32_t width;
uint32_t height;
} mp4_trak;
typedef struct
{
uint32_t stts_count;
mp4_stts *stts;
uint32_t stsc_count;
mp4_stsc *stsc;
uint32_t stsz_count;
mp4_stsz *stsz;
mp4_stsz_time *stsz_time;
uint32_t stco_count;
mp4_stco *stco;
uint32_t stss_count;
mp4_stss *stss;
uint32_t key_frame_offset; // 关键帧的偏移,0-stss_count
uint8_t *pps;
uint16_t pps_len;
uint8_t *sps;
uint16_t sps_len;
uint32_t *key_frame_bitmap;
uint32_t key_frame_bitmap_count;
uint32_t timescale;
uint32_t duration;
} main_box;
#pragma pack(2)
typedef struct
{
uint8_t reserved[6]; // 保留字段,通常为 0
uint16_t data_reference_index; // 数据引用索引
uint16_t pre_defined; // 保留字段,通常为 0
uint16_t reserved1; // 保留字段,通常为 0
uint32_t pre_defined2[3]; // 保留字段,通常为 0
uint16_t width; // 视频宽度
uint16_t height; // 视频高度
uint32_t horizresolution; // 水平分辨率,通常为 0x0048000072 dpi
uint32_t vertresolution; // 垂直分辨率,通常为 0x0048000072 dpi
uint32_t reserved2; // 保留字段,通常为 0
uint16_t frame_count; // 每个样本的帧数,通常为 1
uint8_t compressorname[32]; // 压缩器名称,第一个字节表示名称长度,后续为名称字符串,剩余部分填充为 0
uint16_t depth; // 图像的颜色深度,通常为 0x001824 位)
int16_t pre_defined3; // 保留字段,通常为 -10xFFFF
// 后续可能包含 avcC boxAVCDecoderConfigurationRecord
} AVC1Box;
#pragma pack()
typedef struct
{
uint8_t configurationVersion; // 配置版本,通常为 1
uint8_t AVCProfileIndication; // 表示使用的 H.264 配置文件
uint8_t profile_compatibility; // 配置文件兼容性
uint8_t AVCLevelIndication; // 表示使用的 H.264 等级
uint8_t lengthSizeMinusOne; // NAL 单元长度字段的字节数减一,通常为 3表示长度字段为 4 字节)
} AVCC_BOX;
// 解析pps和sps的结构体
typedef struct
{
uint8_t numOfSequenceParameterSets;
uint8_t sequenceParameterSetLength_H;
uint8_t sequenceParameterSetLength_L;
} PPS_SPS;
typedef struct
{
uint32_t type; // video或者sound,0是无效,1是视频,2是音频
uint32_t init;
mp4_trak trak;
main_box box;
} trak;
typedef struct
{
uint8_t version;
uint8_t flags[3];
} MP4_gen_head;
typedef struct
{
uint32_t version : 8, flags : 24; // version+flags
uint32_t creation_time;
uint32_t modification_time;
uint32_t timescale;
uint32_t duration;
uint16_t language;
uint16_t pre_defined;
} mp4_mdhd;
struct mp4_demux_msi_s
{
struct msi *msi;
char *filename;
F_FILE *fp;
trak trak_t[2]; // trak0:视频trak1:音频
uint8_t trak_index;
uint8_t aac_dsi[2];
uint8_t *pps;
uint8_t *sps;
uint16_t pps_len;
uint16_t sps_len;
uint16_t w;
uint16_t h;
uint32_t play_vframe_num; // 视频播放第几帧
uint32_t jmp_vframe_num; // 视频播放第几帧
uint32_t play_aframe_num; // 音频播放第几帧
uint32_t audio_samplerate;
struct os_event evt;
struct fbpool tx_pool;
};
extern uint32_t box_read(struct mp4_demux_msi_s *mp4_demux, const char *name, int32_t max_size);
typedef uint32_t (*MP4_parse_func)(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size);
typedef struct
{
const char *name;
MP4_parse_func func;
} MP4_parse_register;
#define BIG4_ENDIAN(X) ((X & 0xff000000) >> 24 | (X & 0x00FF0000) >> 8 | (X & 0x0000FF00) << 8 | (X & 0x000000FF) << 24)
#define BIG2_ENDIAN(X) ((X & 0xFF00) >> 8 | (X & 0x00FF) << 8)
uint32_t general_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
int32_t r_len = max_size - 8;
uint32_t ret;
// os_printf("[%s] offset:%X\tlen:%d\n", box_name, osal_ftell(fp), r_len);
ret = box_read(mp4_demux, box_name, r_len);
return ret;
}
uint32_t trak_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
int32_t r_len = max_size - 8;
uint32_t ret;
// os_printf("[%s] offset:%X\tlen:%d\n", box_name, osal_ftell(fp), r_len);
ret = box_read(mp4_demux, box_name, r_len);
// 如果是视频,则生成关键帧的key_bitmap
trak *trak_t = &mp4_demux->trak_t[mp4_demux->trak_index];
if (!ret && trak_t->type == 1)
{
uint32_t bitmap;
uint32_t bitmap_index;
uint32_t bitmap_offset;
main_box *box = &trak_t->box;
// 这里申请key_frame的空间,用于记录关键帧
uint32_t key_frame_count = (box->stsz_count + 0x1f) & (~0x1f);
if (key_frame_count)
{
key_frame_count = key_frame_count / 0x20;
box->key_frame_bitmap = (uint32_t *) STREAM_ZALLOC(key_frame_count * sizeof(uint32_t));
box->key_frame_bitmap_count = key_frame_count;
if (box->key_frame_bitmap)
{
for (int i = 0; i < box->stss_count; i++)
{
// MP4的索引从1开始
bitmap = BIG4_ENDIAN(box->stss[i].sample_number) - 1;
// 如果超过了,就不要去处理了
if (box->key_frame_bitmap_count * 32 > bitmap)
{
bitmap_index = bitmap / 0x20;
bitmap_offset = bitmap % 0x20;
box->key_frame_bitmap[bitmap_index] |= (1 << bitmap_offset);
// 记录关键帧的偏移,用bitmap
}
}
}
}
else
{
ret = 1;
}
}
return ret;
}
uint32_t not_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
os_printf("%s:%d\tname:%s\n", __FUNCTION__, __LINE__, box_name);
return 0;
}
// 实际内容的位置
uint32_t mdat_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
int32_t r_len = max_size - 8;
os_printf("offset:%X\tlen:%d\n", osal_ftell(fp), r_len);
return 0;
}
uint32_t hdlr_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
mp4_hdlr hdlr;
uint32_t ret;
trak *trak_t = &mp4_demux->trak_t[mp4_demux->trak_index];
ret = osal_fread(&hdlr, 1, sizeof(mp4_hdlr), fp);
if (os_memcmp(hdlr.handler_type, "vide", 4) == 0)
{
trak_t->type = 1;
os_printf("video\n");
}
else if (os_memcmp(hdlr.handler_type, "soun", 4) == 0)
{
trak_t->type = 2;
os_printf("audio\n");
}
return 0;
}
static uint32_t stts_sample_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
trak *trak_t = &mp4_demux->trak_t[mp4_demux->trak_index];
main_box *box = &trak_t->box;
MP4_gen_head head;
uint32_t entry_count = 0;
uint32_t ret = 0;
ret = osal_fread(&head, 1, sizeof(MP4_gen_head), fp);
MP4_ABORT(ret == 0);
ret = osal_fread(&entry_count, 1, sizeof(entry_count), fp);
MP4_ABORT(ret == 0);
entry_count = BIG4_ENDIAN(entry_count);
os_printf("[%s] entry_count:%d\n", box_name, entry_count);
// 需要解释各个sample的时间
box->stts_count = entry_count;
if (box->stts)
{
os_printf("%s:%d err,not support more stts\n", __FUNCTION__, __LINE__);
}
else
{
box->stts = (mp4_stts *) STREAM_MALLOC(entry_count * sizeof(mp4_stts));
ret = osal_fread(box->stts, entry_count, sizeof(mp4_stts), fp);
MP4_ABORT(ret == 0);
// 读取完毕,然后为stsz_time创建空间保存时间戳的空间,可以加快索引,但是需要空间变多
// 先计算一下sample总数量,理论应该和stsz_count一致才对
uint32_t sample_count = 0;
for (uint32_t i = 0; i < entry_count; i++)
{
sample_count += BIG4_ENDIAN(box->stts[i].sample_count);
}
// 申请空间,时间用uint32来保存
box->stsz_time = (mp4_stsz_time *) STREAM_MALLOC(sample_count * sizeof(mp4_stsz_time));
if (box->stsz_time)
{
uint32_t count = 0;
uint32_t offset = 0;
uint32_t time = 0;
uint32_t remain_mod = 0;
for (uint32_t i = 0; i < entry_count; i++)
{
count = BIG4_ENDIAN(box->stts[i].sample_count);
for (uint32_t j = 0; j < count; j++)
{
// os_printf(KERN_ALERT"BIG4_ENDIAN(box->stts[i].sample_delta):%d\tscale:%d\t", BIG4_ENDIAN(box->stts[i].sample_delta), box->timescale);
time += ((BIG4_ENDIAN(box->stts[i].sample_delta) * 1000 + remain_mod) / box->timescale);
remain_mod = ((BIG4_ENDIAN(box->stts[i].sample_delta) * 1000 + remain_mod) % box->timescale);
box->stsz_time[offset].sample_time = time;
offset++;
}
}
}
}
abort_end:
return ret;
}
uint32_t stts_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
return stts_sample_parse(mp4_demux, box_name, max_size - 8);
}
static uint32_t stsc_sample_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
trak *trak_t = &mp4_demux->trak_t[mp4_demux->trak_index];
MP4_gen_head head;
uint32_t ret = 0;
uint32_t entry_count = 0;
ret = osal_fread(&head, 1, sizeof(MP4_gen_head), fp);
MP4_ABORT(ret == 0);
ret = osal_fread(&entry_count, 1, sizeof(entry_count), fp);
MP4_ABORT(ret == 0);
entry_count = BIG4_ENDIAN(entry_count);
os_printf("[%s] entry_count:%d\n", box_name, entry_count);
main_box *box = &trak_t->box;
// 需要解释chunk的位置
box->stsc_count = entry_count;
if (box->stsc)
{
os_printf("%s:%d err,not support more stts\n", __FUNCTION__, __LINE__);
}
else
{
box->stsc = (mp4_stsc *) STREAM_MALLOC(entry_count * sizeof(mp4_stsc));
ret = osal_fread(box->stsc, entry_count, sizeof(mp4_stsc), fp);
MP4_ABORT(ret == 0);
}
abort_end:
return ret;
}
uint32_t stsc_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
return stsc_sample_parse(mp4_demux, box_name, max_size - 8);
}
static uint32_t stsd_sample_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
MP4_gen_head head;
uint32_t entry_count = 0;
uint32_t ret = 0;
ret = osal_fread(&head, 1, sizeof(MP4_gen_head), fp);
MP4_ABORT(ret == 0);
ret = osal_fread(&entry_count, 1, sizeof(entry_count), fp);
MP4_ABORT(ret == 0);
entry_count = BIG4_ENDIAN(entry_count);
os_printf("[%s] entry_count:%d\n", box_name, entry_count);
ret = general_parse(mp4_demux, box_name, max_size - 8);
MP4_ABORT(ret > 0);
abort_end:
return ret;
}
uint32_t stsd_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
return stsd_sample_parse(mp4_demux, box_name, max_size - 8);
}
static uint32_t avc1_sample_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
AVC1Box avc1_box;
uint32_t ret = 0;
os_printf("AVC1 Box size:%d\tmax_size:%d\n", sizeof(AVC1Box), max_size);
ret = osal_fread(&avc1_box, 1, sizeof(AVC1Box), fp);
MP4_ABORT(ret == 0);
ret = box_read(mp4_demux, box_name, max_size - sizeof(AVC1Box));
MP4_ABORT(ret > 0);
os_printf("box width:%X\theight:%X\n", BIG2_ENDIAN(avc1_box.width), BIG2_ENDIAN(avc1_box.height));
mp4_demux->w = BIG2_ENDIAN(avc1_box.width);
mp4_demux->h = BIG2_ENDIAN(avc1_box.height);
abort_end:
mp4_demux->h = BIG2_ENDIAN(avc1_box.height);
os_printf("%s:%d\tret:%d\n", __FUNCTION__, __LINE__, ret);
return ret;
}
uint32_t avc1_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
return avc1_sample_parse(mp4_demux, box_name, max_size - 8);
}
// 解析AVCC,这里当作是h264去解析,解析pps和sps的参数
static uint32_t avcc_sample_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
trak *trak_t = &mp4_demux->trak_t[mp4_demux->trak_index];
AVCC_BOX avcc_box;
PPS_SPS pps_sps;
main_box *box = &trak_t->box;
uint32_t ret = 0;
ret = osal_fread(&avcc_box, 1, sizeof(AVCC_BOX), fp);
MP4_ABORT(ret == 0);
os_printf("[%s] offset:%X\tlen:%d\n", box_name, osal_ftell(fp), max_size);
uint8_t pps = 0, sps = 0;
uint16_t len;
while (!pps || !sps)
{
// 开始解析pps或者sps
ret = osal_fread(&pps_sps, 1, sizeof(PPS_SPS), fp);
MP4_ABORT(ret == 0);
os_printf("pps_sps.numOfSequenceParameterSets:%X\tpps_sps.sequenceParameterSetLength_H:%d\tpps_sps.sequenceParameterSetLength_L:%d\n", pps_sps.numOfSequenceParameterSets,
pps_sps.sequenceParameterSetLength_H, pps_sps.sequenceParameterSetLength_L);
len = (pps_sps.sequenceParameterSetLength_H << 8 | pps_sps.sequenceParameterSetLength_L);
// SPS
if (pps_sps.numOfSequenceParameterSets == 0xE1)
{
sps = 1;
os_printf("SPS len:%d\n", len);
// 读取sps数据保存
box->sps = (uint8_t *) STREAM_MALLOC(len);
if (box->sps)
{
ret = osal_fread(box->sps, 1, len, fp);
MP4_ABORT(ret == 0);
box->sps_len = len;
}
else
{
os_printf("malloc sps err\n");
}
}
// PPS
else if (pps_sps.numOfSequenceParameterSets == 0x01)
{
pps = 1;
os_printf("PPS len:%d\n", len);
// 读取pps数据保存
box->pps = (uint8_t *) STREAM_MALLOC(len);
if (box->pps)
{
ret = osal_fread(box->pps, 1, len, fp);
MP4_ABORT(ret == 0);
box->pps_len = len;
}
else
{
os_printf("malloc pps err\n");
}
}
}
abort_end:
return ret;
}
uint32_t avcc_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
return avcc_sample_parse(mp4_demux, box_name, max_size - 8);
}
static uint32_t stsz_sample_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
trak *trak_t = &mp4_demux->trak_t[mp4_demux->trak_index];
MP4_gen_head head;
main_box *box = &trak_t->box;
uint32_t sample_size;
uint32_t sample_count = 0;
uint32_t ret = 0;
ret = osal_fread(&head, 1, sizeof(MP4_gen_head), fp);
MP4_ABORT(ret == 0);
ret = osal_fread(&sample_size, 1, sizeof(sample_size), fp);
MP4_ABORT(ret == 0);
sample_size = BIG4_ENDIAN(sample_size);
os_printf("[%s] sample_size:%d\n", box_name, sample_size);
if (sample_size != 0)
{
os_printf("[%s] not support sample_size == 0\n", box_name);
return ret;
}
// 读取sample_count
ret = osal_fread(&sample_count, 1, sizeof(sample_count), fp);
MP4_ABORT(ret == 0);
sample_count = BIG4_ENDIAN(sample_count);
os_printf("[%s] sample_count:%d\n", box_name, sample_count);
// 需要解释chunk的位置
box->stsz_count = sample_count;
if (box->stsz)
{
os_printf("%s:%d err,not support more stts\n", __FUNCTION__, __LINE__);
}
else
{
box->stsz = (mp4_stsz *) STREAM_MALLOC(sample_count * sizeof(mp4_stsz));
ret = osal_fread(box->stsz, sample_count, sizeof(mp4_stsz), fp);
MP4_ABORT(ret == 0);
}
abort_end:
return ret;
}
uint32_t stsz_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
return stsz_sample_parse(mp4_demux, box_name, max_size - 8);
}
static uint32_t stco_sample_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
trak *trak_t = &mp4_demux->trak_t[mp4_demux->trak_index];
main_box *box = &trak_t->box;
MP4_gen_head head;
uint32_t chunk_offset_box_entry_count;
uint32_t ret = 0;
ret = osal_fread(&head, 1, sizeof(MP4_gen_head), fp);
MP4_ABORT(ret == 0);
ret = osal_fread(&chunk_offset_box_entry_count, 1, sizeof(chunk_offset_box_entry_count), fp);
MP4_ABORT(ret == 0);
chunk_offset_box_entry_count = BIG4_ENDIAN(chunk_offset_box_entry_count);
os_printf("[%s] chunk_offset_box_entry_count:%d\n", box_name, chunk_offset_box_entry_count);
// 需要解析chunk_box的位置的位置
box->stco_count = chunk_offset_box_entry_count;
if (box->stco)
{
os_printf("%s:%d err,not support more stco:%X\n", __FUNCTION__, __LINE__, box->stco);
}
else
{
box->stco = (mp4_stco *) STREAM_MALLOC(chunk_offset_box_entry_count * sizeof(mp4_stco));
ret = osal_fread(box->stco, chunk_offset_box_entry_count, sizeof(mp4_stco), fp);
MP4_ABORT(ret == 0);
}
abort_end:
return ret;
}
uint32_t stco_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
return stco_sample_parse(mp4_demux, box_name, max_size - 8);
}
static uint32_t stss_sample_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
trak *trak_t = &mp4_demux->trak_t[mp4_demux->trak_index];
main_box *box = &trak_t->box;
MP4_gen_head head;
uint32_t Sync_sample_box_entry_count;
uint32_t ret = 0;
ret = osal_fread(&head, 1, sizeof(MP4_gen_head), fp);
MP4_ABORT(ret == 0);
ret = osal_fread(&Sync_sample_box_entry_count, 1, sizeof(Sync_sample_box_entry_count), fp);
MP4_ABORT(ret == 0);
Sync_sample_box_entry_count = BIG4_ENDIAN(Sync_sample_box_entry_count);
os_printf("[%s] Sync_sample_box_entry_count:%d\n", box_name, Sync_sample_box_entry_count);
// 需要解析Sync_sample_box_entry_count的列表
box->stss_count = Sync_sample_box_entry_count;
if (box->stss)
{
os_printf("%s:%d err,not support more stts\n", __FUNCTION__, __LINE__);
}
else
{
box->stss = (mp4_stss *) STREAM_MALLOC(Sync_sample_box_entry_count * sizeof(mp4_stss));
ret = osal_fread(box->stss, Sync_sample_box_entry_count, sizeof(mp4_stss), fp);
MP4_ABORT(ret == 0);
}
abort_end:
return ret;
}
uint32_t stss_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
return stss_sample_parse(mp4_demux, box_name, max_size - 8);
}
static uint32_t mp4a_sample_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
mp4_mp4a mp4a;
// uint8_t samplerate_index;
F_FILE *fp = mp4_demux->fp;
uint32_t ret = 0;
ret = osal_fread(&mp4a, 1, sizeof(mp4a), fp);
MP4_ABORT(ret == 0);
// 尝试读取下一个box
if (max_size - sizeof(mp4a) > 0)
{
ret = box_read(mp4_demux, box_name, max_size - sizeof(mp4a));
MP4_ABORT(ret > 0);
}
abort_end:
return ret;
}
uint32_t mp4a_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
return mp4a_sample_parse(mp4_demux, box_name, max_size - 8);
}
// 如果tag_size返回0应该是不正确的
uint8_t get_tag(uint8_t *data, uint32_t max_size, uint32_t *tag_size, uint8_t *head_size)
{
uint8_t tag;
uint8_t *l_data = data;
tag = *data++;
uint32_t size = 0;
uint8_t calc;
// uint8_t head_offset = 0;
for (int i = 0; i < 4; i++)
{
calc = *data++;
size = size << 7;
size |= (calc & (0x7f));
if (!(calc & 0x80))
{
break;
}
}
if (tag_size)
{
// 判断max_size是否足够,不足够,应该是有错
if (max_size - 5 >= size)
{
*tag_size = size;
}
else
{
*tag_size = 0;
}
}
// 计算偏移量
if (head_size)
{
*head_size = data - l_data;
}
return tag;
}
// 寻找特定的tag
// 返回对应tag的size,0代表没有找到或者异常,offset是指对应tag的偏移
uint8_t *get_tag_value(uint8_t tag_v, uint8_t *data, uint32_t max_size, uint32_t *size)
{
uint8_t tag = 0;
// uint8_t calc;
uint32_t tag_size;
uint8_t head_size;
uint8_t *tag_data = NULL;
for (int i = 0; i < max_size;)
{
tag = get_tag(data + i, max_size, &tag_size, &head_size);
// 异常
if (tag_size == 0)
{
break;
}
if (tag == tag_v)
{
tag_data = data + i + head_size;
if (size)
{
*size = tag_size;
}
break;
}
i += (head_size + tag_size);
}
os_printf("tag:%d\ttag_data:%X\ttag_size:%d\tsize:%d\n", tag, tag_data, tag_size, *size);
return tag_data;
}
uint8_t *get_tag_5(uint8_t *data, uint32_t max_size, uint32_t *size)
{
return get_tag_value(0x05, data, max_size, size);
}
uint8_t *get_tag_4(uint8_t *data, uint32_t max_size, uint32_t *size)
{
return get_tag_value(0x04, data, max_size, size);
}
uint32_t esds_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
uint8_t *esds_data = (uint8_t *) STREAM_MALLOC(max_size - 8);
osal_fread(esds_data, 1, max_size - 8, fp);
uint8_t *ES_Descriptor_data;
uint32_t es_descriptor_size = 0;
uint8_t calc;
uint32_t size;
uint8_t *data;
uint8_t found = 0;
// 直接跳过esds前面4byte,固定的数据
// 仅仅解析紧接着的ES_Descriptor_data数据
if (esds_data[4] == 0x03)
{
ES_Descriptor_data = esds_data + 5;
for (int i = 0; i < 4; i++)
{
calc = *ES_Descriptor_data++;
es_descriptor_size = es_descriptor_size << 7;
es_descriptor_size |= (calc & (0x7f));
if (!(calc & 0x80))
{
ES_Descriptor_data += 3; // 跳过固定字段
found = 1;
break;
}
}
}
// 开始解析esds,我们只是关注自己需要的字段,我们只是
if (found)
{
data = get_tag_4(ES_Descriptor_data, es_descriptor_size, &size);
// 如果找到ID=4,则继续内部找ID=5
if (data)
{
if (size > 13)
{
data += 13;
}
size = size - 13;
data = get_tag_5(data, size, &size);
if (data && size >= 2)
{
mp4_demux->aac_dsi[0] = data[0];
mp4_demux->aac_dsi[1] = data[1];
uint8_t samplerate_index;
samplerate_index = ((mp4_demux->aac_dsi[0] & 0x07) << 1) | (mp4_demux->aac_dsi[1] >> 7);
switch (samplerate_index)
{
case 0x8:
mp4_demux->audio_samplerate = 16000;
break;
case 0xB:
mp4_demux->audio_samplerate = 8000;
break;
default:
mp4_demux->audio_samplerate = 8000;
break;
}
os_printf("mp4_demux->audio_samplerate:%d\n", mp4_demux->audio_samplerate);
}
}
}
STREAM_FREE(esds_data);
return 0;
}
static uint32_t mdhd_parse(struct mp4_demux_msi_s *mp4_demux, const char *box_name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
trak *trak_t = &mp4_demux->trak_t[mp4_demux->trak_index];
main_box *box = &trak_t->box;
mp4_mdhd mdhd;
uint32_t ret;
ret = osal_fread(&mdhd, 1, sizeof(mdhd), fp);
MP4_ABORT(ret == 0);
box->timescale = BIG4_ENDIAN(mdhd.timescale);
box->duration = BIG4_ENDIAN(mdhd.duration);
os_printf("mdhd.timescale:%d\tmdhd.duration:%d\n", box->timescale, box->duration);
abort_end:
return ret;
}
const MP4_parse_register MP4_func[] = {
{"moov", general_parse},
{"trak", trak_parse},
{"mdia", general_parse},
{"minf", general_parse},
{"stbl", general_parse},
{"stts", stts_parse},
{"stsc", stsc_parse},
{"stsz", stsz_parse},
{"stco", stco_parse},
{"stss", stss_parse},
{"mdat", mdat_parse},
{"stsd", stsd_parse},
{"avc1", avc1_parse},
{"avcC", avcc_parse},
{"mp4a", mp4a_parse},
{"hdlr", hdlr_parse},
{"esds", esds_parse},
{"mdhd", mdhd_parse},
{(const char *) NULL, not_parse},
};
MP4_parse_func get_func(const char *name)
{
int i = 0;
for (i = 0; MP4_func[i].name != NULL; i++)
{
if (strcmp(MP4_func[i].name, name) == 0)
{
return MP4_func[i].func;
}
}
return MP4_func[i].func;
}
uint32_t box_read(struct mp4_demux_msi_s *mp4_demux, const char *name, int32_t max_size)
{
F_FILE *fp = mp4_demux->fp;
uint8_t get_trak = 0;
int32_t read_size = max_size;
int32_t box_len;
uint8_t box_name[5] = {0};
uint32_t cur_offset;
MP4_parse_func func;
uint32_t ret = 0;
while (read_size > 8)
{
ret = osal_fread(&box_len, 1, 4, fp);
MP4_ABORT(ret == 0);
ret = osal_fread(box_name, 1, 4, fp);
MP4_ABORT(ret == 0);
box_len = BIG4_ENDIAN(box_len);
cur_offset = osal_ftell(fp);
if (get_trak && (os_strncmp(box_name, "trak", os_strlen("trak")) == 0))
{
os_printf("more trak\n");
mp4_demux->trak_index++;
}
if (os_strncmp(box_name, "trak", os_strlen("trak")) == 0)
{
get_trak = 1;
mp4_demux->trak_t[mp4_demux->trak_index].init = 1;
}
os_printf("box_name:%s\tbox_len:%d\n", box_name, box_len);
func = get_func((const char *) box_name);
if (func)
{
// 检查是否有注册对应的函数,如果有就执行,没有就使用通用的方法
ret = func(mp4_demux, (const char *) box_name, box_len);
MP4_ABORT(ret > 0);
}
osal_fseek(fp, cur_offset + box_len - 8);
read_size = read_size - box_len;
}
abort_end:
return ret;
}
// 获取一个帧相对文件的偏移
uint32_t get_frame_offset(trak *trak_t, uint32_t sample_offset, uint32_t *read_size)
{
main_box *box = &trak_t->box;
// 这里暂时仅仅考虑sssc都是1 1 1的情况,暂时看到minimp4输出是这样的格式
if (box->stsz_count > sample_offset)
{
// 现在理论chunk只有一种,所以这里粗暴判断,不匹配就不能解析
if (box->stsz)
{
*read_size = BIG4_ENDIAN(box->stsz[sample_offset].sample_size);
}
return BIG4_ENDIAN(box->stco[sample_offset].chunk_offset);
}
else
{
return 0;
}
}
// 获取pts的一个帧的NUM,应该返回一个关键帧,同时返回pts的真实值,因为传入一个pts,是一个约等于值,实际pts应该与关键帧一致
// 都是使用90000为视频帧的默认值计算
uint32_t get_frame_num_from_pts(trak *trak_t, uint32_t ms)
{
main_box *box = &trak_t->box;
uint32_t duration = ms;
uint32_t last_duation = 0;
uint32_t now_duation = 0;
uint32_t last_frame_num = 0;
uint32_t now_frame_num = 0;
uint32_t find_main_frame = 0;
uint32_t last_find_main_frame = 0;
uint32_t match_frame_num = 0;
// 搜索duration范围,记录最近的关键帧,规则是往回退
// 这里可以加速,使用二分法或者其他算法加速,现在仅仅实现逻辑
if (box->stts && box->stts_count > 0)
{
for (int i = 0; i < box->stsz_count; i++)
{
now_frame_num = i;
now_duation = box->stsz_time[i].sample_time;
// 找到duation对应的位置
if (duration >= last_duation && duration <= now_duation)
{
break;
}
last_duation = now_duation;
last_frame_num = now_frame_num;
}
}
// 找到,开始寻找关键帧位置,通过last_frame_num和now_frame_num来寻找最近的关键帧
if (duration >= last_duation && duration <= now_duation)
{
if (box->stss && box->stss_count > 0)
{
last_find_main_frame = BIG4_ENDIAN(box->stss[0].sample_number);
for (int i = 0; i < box->stss_count; i++)
{
find_main_frame = BIG4_ENDIAN(box->stss[i].sample_number);
// 找到了,退出,应该返回last_find_main_frame
if (find_main_frame > now_frame_num)
{
match_frame_num = last_find_main_frame;
break;
}
last_find_main_frame = find_main_frame;
}
}
}
return match_frame_num;
}
uint32_t get_frame_num_pts(trak *trak_t, uint32_t num)
{
main_box *box = &trak_t->box;
if (num < box->stsz_count)
{
// os_printf(KERN_ALERT"box->stsz_time[%d].sample_time:%d\n",num,box->stsz_time[num].sample_time);
return box->stsz_time[num].sample_time;
}
else
{
return 0;
}
}
void mp4_demux_thread(void *d)
{
struct msi *msi = (struct msi *) d;
struct mp4_demux_msi_s *mp4_demux = (struct mp4_demux_msi_s *) msi->priv;
int ret = 0;
if (!mp4_demux->fp)
{
os_printf("file open fail\n");
return;
}
uint32_t vframe_offset = 0;
uint32_t vframe_size = 0;
uint32_t aframe_offset = 0;
uint32_t aframe_size = 0;
uint32_t video_timestamp = 0;
// 默认播放是第一帧
mp4_demux->play_vframe_num = 0;
mp4_demux->play_aframe_num = 0;
struct framebuff *fb = NULL;
uint32_t rflags = 0;
uint32_t err_times = 0;
uint32_t err;
uint8_t count = 0;
uint8_t flag = 0;
// 这里开始进行视频的播放,需要填充时间戳,然后解码给到播放器或者其他地方
// 这里会不停发送数据,这里只是管自己是否有多余的节点,播放速度以及快进快退由其他地方发命令
// 主要是为了不要随意删除当前msi,只有触发命令才可以退出
msi_get(msi);
os_event_wait(&mp4_demux->evt, MP4_DEMUX_START | MP4_DEMUX_STOP, &rflags, OS_EVENT_WMODE_OR, -1);
uint32_t last_play_time = os_jiffies();
if (rflags & MP4_DEMUX_STOP)
{
ret = __LINE__;
goto mp4_demux_thread_exit;
}
os_printf("mp4_demux->trak_t[1].init:%d\t%d\n", mp4_demux->trak_t[0].init, mp4_demux->trak_t[1].init);
while (1)
{
mp4_demux_thread_open_again:
if (!mp4_demux->fp)
{
err_times++;
mp4_demux->fp = osal_fopen(mp4_demux->filename, "rb");
}
if (!mp4_demux->fp)
{
if (err_times > MAX_TRY_COUNT)
{
ret = __LINE__;
goto mp4_demux_thread_exit;
}
os_sleep_ms(5);
goto mp4_demux_thread_open_again;
}
err_times = 0;
mp4_demux_thread_JMP:
video_timestamp = get_frame_num_pts(&mp4_demux->trak_t[0], mp4_demux->play_vframe_num);
if (video_timestamp > os_jiffies() - last_play_time)
{
os_sleep_ms(1);
}
else
{
vframe_offset = get_frame_offset(&mp4_demux->trak_t[0], mp4_demux->play_vframe_num, &vframe_size);
// os_printf("vframe_offset:%d\tvframe_size:%d\n", vframe_offset, vframe_size);
// os_printf("mp4_demux->play_vframe_num:%d\n", mp4_demux->play_vframe_num);
if (!vframe_offset || vframe_size == 0)
{
flag |= BIT(0);
if (flag == (BIT(0) | BIT(1)))
{
break;
}
ret = __LINE__;
goto mp4_demux_audio;
}
mp4_demux_thread_again:
rflags = 0;
os_event_wait(&mp4_demux->evt, MP4_DEMUX_JMP | MP4_DEMUX_STOP, &rflags, OS_EVENT_WMODE_CLEAR | OS_EVENT_WMODE_OR, 0);
// 退出线程
if (rflags & MP4_DEMUX_STOP)
{
break;
}
// 快进快退
if (rflags & MP4_DEMUX_JMP)
{
mp4_demux->play_vframe_num = mp4_demux->jmp_vframe_num;
video_timestamp = get_frame_num_pts(&mp4_demux->trak_t[0], mp4_demux->play_vframe_num);
// 配置播放的时间
last_play_time = os_jiffies() - video_timestamp;
mp4_demux->play_aframe_num = video_timestamp / (1024 * 1000 / mp4_demux->audio_samplerate);
flag = 0;
goto mp4_demux_thread_JMP;
}
rflags = 0;
// 如果是暂停,则重复等待
os_event_wait(&mp4_demux->evt, MP4_DEMUX_START, &rflags, OS_EVENT_WMODE_OR, 0);
if (!(rflags & MP4_DEMUX_START))
{
os_sleep_ms(1);
last_play_time = os_jiffies() - video_timestamp;
goto mp4_demux_thread_again;
}
fb = fbpool_get(&mp4_demux->tx_pool, 0, mp4_demux->msi);
if (!fb)
{
os_sleep_ms(1);
goto mp4_demux_thread_again;
}
else
{
extern uint8_t is_key_frame(trak * trak_t, uint32_t frame_num);
// os_printf("is key:%d\n", is_key_frame(NULL, mp4_demux->play_vframe_num));
fb->time = get_frame_num_pts(&mp4_demux->trak_t[0], mp4_demux->play_vframe_num);
fb->data = (uint8_t *) STREAM_MALLOC(vframe_size - 4);
if (!fb->data)
{
msi_delete_fb(NULL, fb);
os_sleep_ms(1);
continue;
}
fb->len = vframe_size - 4;
fb->mtype = F_H264;
fb->stype = FSTYPE_H264_FILE;
osal_fseek(mp4_demux->fp, vframe_offset + 4);
err = osal_fread(fb->data, 1, vframe_size - 4, mp4_demux->fp);
// os_printf("frame_size:%d\t%02X\t%02X\n", frame_size,fb->data[0], fb->data[1]);
// 暂时只有I帧和P帧
// 可能文件系统有错,重新尝试打开文件系统,超过一定次数就退出
if (err)
{
if (is_key_frame(&mp4_demux->trak_t[0], mp4_demux->play_vframe_num))
{
struct fb_h264_s *priv = (struct fb_h264_s *) STREAM_LIBC_ZALLOC(sizeof(struct fb_h264_s));
priv->pps = mp4_demux->pps;
priv->pps_len = mp4_demux->pps_len;
priv->sps = mp4_demux->sps;
priv->sps_len = mp4_demux->sps_len;
fb->priv = (void *) priv;
priv->type = 1;
priv->count = count;
priv->start_len = 0;
priv->w = mp4_demux->w;
priv->h = mp4_demux->h;
}
else
{
struct fb_h264_s *priv = (struct fb_h264_s *) STREAM_LIBC_ZALLOC(sizeof(struct fb_h264_s));
priv->type = 2;
fb->priv = (void *) priv;
priv->count = count;
priv->start_len = 0;
priv->w = mp4_demux->w;
priv->h = mp4_demux->h;
}
count++;
msi_output_fb(mp4_demux->msi, fb);
_os_printf("M");
fb = NULL;
}
if (!err)
{
msi_delete_fb(NULL, fb);
fb = NULL;
osal_fclose(mp4_demux->fp);
mp4_demux->fp = NULL;
goto mp4_demux_thread_open_again;
}
}
mp4_demux->play_vframe_num++;
}
mp4_demux_audio:
if (!mp4_demux->trak_t[1].init)
{
flag |= BIT(1);
continue;
}
if (flag & BIT(1))
{
continue;
}
if (get_frame_num_pts(&mp4_demux->trak_t[1], mp4_demux->play_aframe_num) > os_jiffies() - last_play_time)
{
os_sleep_ms(1);
}
else
{
aframe_offset = get_frame_offset(&mp4_demux->trak_t[1], mp4_demux->play_aframe_num, &aframe_size);
if ((!aframe_offset || aframe_size == 0))
{
flag |= BIT(1);
if (flag == (BIT(0) | BIT(1)))
{
break;
}
continue;
}
if (aframe_size)
{
fb = fbpool_get(&mp4_demux->tx_pool, 0, mp4_demux->msi);
while (!fb)
{
os_sleep_ms(1);
fb = fbpool_get(&mp4_demux->tx_pool, 0, mp4_demux->msi);
}
fb->data = (uint8_t *) STREAM_MALLOC(aframe_size + 7);
if (!fb->data)
{
msi_delete_fb(NULL, fb);
os_sleep_ms(1);
continue;
}
fb->time = get_frame_num_pts(&mp4_demux->trak_t[1], mp4_demux->play_aframe_num);
fb->len = aframe_size + 7;
fb->mtype = F_AUDIO;
osal_fseek(mp4_demux->fp, aframe_offset);
err = osal_fread(fb->data + 7, 1, aframe_size, mp4_demux->fp);
if (err)
{
aac_dsi_to_adts(mp4_demux->aac_dsi, fb->data, aframe_size);
_os_printf("A");
msi_output_fb(mp4_demux->msi, fb);
fb = NULL;
}
if (!err)
{
msi_delete_fb(NULL, fb);
fb = NULL;
osal_fclose(mp4_demux->fp);
mp4_demux->fp = NULL;
goto mp4_demux_thread_open_again;
}
mp4_demux->play_aframe_num++;
}
}
}
mp4_demux_thread_exit:
if (mp4_demux->fp)
{
osal_fclose(mp4_demux->fp);
mp4_demux->fp = NULL;
}
for (uint32_t i = 0; i <= mp4_demux->trak_index; i++)
{
main_box *box = &mp4_demux->trak_t[i].box;
// 释放空间
if (box->stts)
{
STREAM_FREE(box->stts);
}
if (box->stsc)
{
STREAM_FREE(box->stsc);
}
if (box->stsz)
{
STREAM_FREE(box->stsz);
}
if (box->stsz_time)
{
STREAM_FREE(box->stsz_time);
}
if (box->stco)
{
STREAM_FREE(box->stco);
}
if (box->stss)
{
STREAM_FREE(box->stss);
}
if (box->sps)
{
STREAM_FREE(box->sps);
}
if (box->pps)
{
STREAM_FREE(box->pps);
}
if (box->key_frame_bitmap)
{
STREAM_FREE(box->key_frame_bitmap);
}
}
os_event_set(&mp4_demux->evt, MP4_DEMUX_EXIT, NULL);
os_printf("mp4_demux_thread exit\tret:%d\n", ret);
// 这个时候才可以安全释放msi
msi_put(msi);
}
static int32_t mp4_demux_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct mp4_demux_msi_s *mp4_demux = (struct mp4_demux_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
os_event_wait(&mp4_demux->evt, MP4_DEMUX_EXIT, NULL, OS_EVENT_WMODE_OR, -1);
os_printf("############################################%s:%d\n", __FUNCTION__, __LINE__);
os_event_del(&mp4_demux->evt);
fbpool_destroy(&mp4_demux->tx_pool);
if (mp4_demux->fp)
{
osal_fclose(mp4_demux->fp);
}
if (mp4_demux->sps)
{
STREAM_LIBC_FREE(mp4_demux->sps);
mp4_demux->sps = NULL;
}
if (mp4_demux->pps)
{
STREAM_LIBC_FREE(mp4_demux->pps);
mp4_demux->pps = NULL;
}
STREAM_LIBC_FREE(mp4_demux);
if (msi->name)
{
msi->name = NULL;
}
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_event_set(&mp4_demux->evt, MP4_DEMUX_STOP, NULL);
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// os_printf("mp4_demux:%X\tfb:%X\tdata:%X\n", mp4_demux, fb, fb->data);
if (fb->data)
{
STREAM_FREE(fb->data);
fb->data = NULL;
}
if (fb->priv)
{
STREAM_LIBC_FREE(fb->priv);
fb->priv = NULL;
}
fbpool_put(&mp4_demux->tx_pool, fb);
// 不需要内核去释放fb
ret = RET_OK + 1;
}
break;
#if 1
// 这个属于媒体控制
case MSI_CMD_VIDEO_DEMUX_CTRL:
{
uint32_t cmd_self = (uint32_t) param1;
uint32_t arg = param2;
switch (cmd_self)
{
case MSI_VIDEO_DEMUX_JMP_TIME:
{
uint32_t jmp_vframe_num = get_frame_num_from_pts(&mp4_demux->trak_t[0], arg);
os_printf("jmp_vframe_num:%d\n", jmp_vframe_num);
// 找不到,保持原样
if (jmp_vframe_num)
{
mp4_demux->jmp_vframe_num = jmp_vframe_num - 1;
// 唤醒线程,修改
os_event_set(&mp4_demux->evt, MP4_DEMUX_JMP, NULL);
// 返回当前播放的时间(h264就是关键帧的时间)
return get_frame_num_pts(&mp4_demux->trak_t[0], jmp_vframe_num);
}
}
break;
case MSI_VIDEO_DEMUX_REWIND_TIME:
break;
case MSI_VIDEO_DEMUX_SET_STATUS:
break;
case MSI_VIDEO_DEMUX_GET_STATUS:
break;
case MSI_VIDEO_DEMUX_START:
{
os_event_set(&mp4_demux->evt, MP4_DEMUX_START, NULL);
break;
}
case MSI_VIDEO_DEMUX_PAUSE:
{
os_event_wait(&mp4_demux->evt, MP4_DEMUX_START, NULL, OS_EVENT_WMODE_CLEAR | OS_EVENT_WMODE_OR, 0);
break;
}
break;
default:
break;
}
}
break;
#endif
default:
break;
}
return ret;
}
uint8_t *mp4_demux_get_sps(struct msi *msi, uint16_t *sps_len)
{
struct mp4_demux_msi_s *mp4_demux = (struct mp4_demux_msi_s *) msi->priv;
main_box *box = &mp4_demux->trak_t[0].box;
if (box->sps)
{
if (sps_len)
{
*sps_len = box->sps_len;
}
return box->sps;
}
return NULL;
}
uint8_t *mp4_demux_get_pps(struct msi *msi, uint16_t *pps_len)
{
struct mp4_demux_msi_s *mp4_demux = (struct mp4_demux_msi_s *) msi->priv;
main_box *box = &mp4_demux->trak_t[0].box;
if (box->pps)
{
if (pps_len)
{
*pps_len = box->pps_len;
}
return box->pps;
}
return NULL;
}
uint8_t is_key_frame(trak *trak_t, uint32_t frame_num)
{
main_box *box = &trak_t->box;
// 没有stss,默认所有都是关键帧,暂时仅仅支持视频
if (!box->stss || trak_t->type != 1)
{
return 1;
}
uint32_t bitmap = frame_num;
uint32_t bitmap_index;
uint32_t bitmap_offset;
if (box->key_frame_bitmap)
{
if (box->key_frame_bitmap_count * 32 > bitmap)
{
bitmap_index = bitmap / 0x20;
bitmap_offset = bitmap % 0x20;
return box->key_frame_bitmap[bitmap_index] & (1 << bitmap_offset) ? 1 : 0;
}
}
return 0;
}
// 在对应位置生成视频帧,注意有个最大值,不能越界
// 从当前
static uint32_t build_chunk(main_box *box, mp4_stco *ex_stco, uint32_t frame_num, uint32_t stco_offset, uint32_t sample_per_chunk)
{
// 异常退出,要分析代码哪里异常了
if (stco_offset > box->stco_count)
{
os_printf("fail stco_offset:%d,box->stco_count:%d\n", stco_offset, box->stco_count);
return 1;
}
// stco表的基础偏移
uint32_t stco_base_offset = BIG4_ENDIAN(box->stco[stco_offset].chunk_offset);
// 计算后续帧的偏移
for (int i = 0; i < sample_per_chunk; i++)
{
ex_stco[frame_num + i].chunk_offset = BIG4_ENDIAN(stco_base_offset);
stco_base_offset += BIG4_ENDIAN(box->stsz[frame_num + i].sample_size);
}
return 0;
}
uint32_t mp4_demux_stco_rebuild(struct mp4_demux_msi_s *mp4_demux)
{
for (int box_num = 0; box_num < mp4_demux->trak_index+1; box_num++)
{
trak *trak_t = &mp4_demux->trak_t[box_num];
main_box *box = &trak_t->box;
mp4_stco *ex_stco = NULL;
uint32_t first_chunk;
uint32_t next_chunk;
uint32_t first_sample_per_chunk;
uint32_t frame_num = 0;
uint32_t stco_offset = 0;
// 重构一下stco
if (box->stsz_count != box->stco_count)
{
ex_stco = (mp4_stco *) STREAM_MALLOC(box->stsz_count * sizeof(mp4_stco));
ASSERT(ex_stco);
if (box->stsc_count)
{
for (int i = 0; i < box->stsc_count - 1; i++)
{
first_chunk = BIG4_ENDIAN(box->stsc[i].first_chunk);
next_chunk = BIG4_ENDIAN(box->stsc[i + 1].first_chunk);
first_sample_per_chunk = BIG4_ENDIAN(box->stsc[i].sample_per_chunk);
for (int j = first_chunk; j < next_chunk; j++)
{
build_chunk(box, ex_stco, frame_num, stco_offset, first_sample_per_chunk);
stco_offset++;
frame_num += first_sample_per_chunk;
}
}
first_sample_per_chunk = BIG4_ENDIAN(box->stsc[box->stsc_count - 1].sample_per_chunk);
// 最后读取就按照最后一个per_chunk方式去读取
while (stco_offset < box->stco_count)
{
build_chunk(box, ex_stco, frame_num, stco_offset, first_sample_per_chunk);
stco_offset++;
frame_num += first_sample_per_chunk;
}
}
STREAM_FREE(box->stco);
box->stco = ex_stco;
}
}
return 0;
}
struct msi *mp4_demux_msi_init(const char *msi_name, const char *filename)
{
uint8_t isnew = 0;
struct msi *msi = msi_new(msi_name, 0, &isnew);
struct mp4_demux_msi_s *mp4_demux = NULL;
if (isnew)
{
mp4_demux = (struct mp4_demux_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct mp4_demux_msi_s) + strlen(filename) + 1 + strlen(msi_name) + 1);
if (!mp4_demux)
{
goto mp4_demux_msi_init_err;
}
mp4_demux->filename = (char *) (mp4_demux + 1);
mp4_demux->msi = msi;
memcpy(mp4_demux->filename, filename, strlen(filename) + 1);
msi->priv = (void *) mp4_demux;
// 先open文件
mp4_demux->fp = osal_fopen(mp4_demux->filename, "rb");
if (!mp4_demux->fp)
{
os_printf("file open fail:%s\n", mp4_demux->filename);
goto mp4_demux_msi_init_err;
}
char *new_msi_name = mp4_demux->filename + strlen(filename) + 1;
memcpy(new_msi_name, msi_name, strlen(msi_name) + 1);
msi->name = new_msi_name;
msi->action = mp4_demux_msi_action;
new_msi_name = NULL;
os_event_init(&mp4_demux->evt);
fbpool_init(&mp4_demux->tx_pool, MAX_MP4_DEMUX_TX);
uint32_t filesize = osal_fsize(mp4_demux->fp);
uint32_t mp4_ret = box_read(mp4_demux, "start", filesize);
os_printf("mp4_ret:%d\n", mp4_ret);
// 解析完成,检查stsc是否是1 1 1的情况(如果是这种情况,stco直接使用)
// 如果不是1 1 1的情况,就解析stco,重新生成可用的stco(整个视频的偏移)
if (!mp4_ret)
{
mp4_ret = mp4_demux_stco_rebuild(mp4_demux);
}
uint16_t sps_len, pps_len;
uint8_t *sps = mp4_demux_get_sps(msi, &sps_len);
uint8_t *pps = mp4_demux_get_pps(msi, &pps_len);
os_printf("sps:%X\tlen:%d\n", sps, sps_len);
os_printf("pps:%X\tlen:%d\n", pps, pps_len);
mp4_demux->sps_len = sps_len;
mp4_demux->pps_len = pps_len;
if (sps && pps)
{
mp4_demux->sps = (uint8_t *) STREAM_LIBC_MALLOC(sps_len);
mp4_demux->pps = (uint8_t *) STREAM_LIBC_MALLOC(pps_len);
memcpy(mp4_demux->sps, sps, sps_len);
memcpy(mp4_demux->pps, pps, pps_len);
}
else
{
os_printf("malloc sps or pps err\n");
goto mp4_demux_msi_init_err;
}
msi->enable = 1;
}
else
{
goto mp4_demux_msi_init_err;
}
void *mp4_hdl = os_task_create("mp4_demux", mp4_demux_thread, msi, OS_TASK_PRIORITY_NORMAL, 0, NULL, 2048);
// void *mp4_hdl = os_task_create("mp4", mp4_encode_thread, msi, OS_TASK_PRIORITY_NORMAL, 0, NULL, 2048);
os_printf("mp4_hdl:%X\n", mp4_hdl);
// 正常退出
goto mp4_demux_msi_init_end;
mp4_demux_msi_init_err:
if (mp4_demux)
{
STREAM_LIBC_FREE(mp4_demux);
}
// 不要重复打开,同一个名称需要等待上一次写入完成后并且关闭后才可以重新打开
// 这里是因为可能有错误,但是又new了一次,所以删除
// mp4_demux由destroy内部去释放
if (msi)
{
msi_destroy(msi);
msi = NULL;
}
mp4_demux_msi_init_end:
return msi;
}