1718 lines
56 KiB
C
1718 lines
56 KiB
C
|
|
#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; // 水平分辨率,通常为 0x00480000(72 dpi)
|
|||
|
|
uint32_t vertresolution; // 垂直分辨率,通常为 0x00480000(72 dpi)
|
|||
|
|
|
|||
|
|
uint32_t reserved2; // 保留字段,通常为 0
|
|||
|
|
|
|||
|
|
uint16_t frame_count; // 每个样本的帧数,通常为 1
|
|||
|
|
|
|||
|
|
uint8_t compressorname[32]; // 压缩器名称,第一个字节表示名称长度,后续为名称字符串,剩余部分填充为 0
|
|||
|
|
|
|||
|
|
uint16_t depth; // 图像的颜色深度,通常为 0x0018(24 位)
|
|||
|
|
|
|||
|
|
int16_t pre_defined3; // 保留字段,通常为 -1(0xFFFF)
|
|||
|
|
|
|||
|
|
// 后续可能包含 avcC box(AVCDecoderConfigurationRecord)
|
|||
|
|
} 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;
|
|||
|
|
}
|