本帖最后由 feiyinglala 于 2023-1-10 01:09 编辑
最近在用JPEG数据合成AVI文件,但是AVI视频存盘发现,索引块数据缺失较多(JPG数据200帧全部存盘成功的),我设置参数是捕获200帧图片数据,实际AVI文件尾部只有31帧的索引(仿真看时,双链表长度不止31)。我现在不知道该从哪里查问题,请各位指点一下。
AVI尾部索引详见下图
主函数和调用写文件函数代码如下
//**************main.c************************
#define JPEG_NUM 200 //捕获JPEG图片数据200张,合成AVI
f_open(&fp_avi,"0:/sample-2.avi",FA_CREATE_ALWAYS|FA_WRITE); //创建AVI文件
jpeg2avi_start(&fp_avi); //写AVI文件头数据
if(jpglen) //捕获JPG数据正常
{
if(Video_i < JPEG_NUM) //若捕获次数在指定JPEG数量内,数据写入当前AVI文件
{
jpeg2avi_add_frame(&fp_avi, &jpgdat, jpglen); //【主体】增加视频帧
}
else if(Video_i == JPEG_NUM)
{//达到数量后则写文件结尾并关闭文件
jpeg2avi_end(&fp_avi, jpeg_img_size_tbl[3][0],jpeg_img_size_tbl[3][1], 1); //最后一个数字为每秒帧速
f_close(&fp_avi);
printf("endn");
}
Video_i++;
}
//*************Jpeg2AVI.c*******************
// 参考 https://article.itxueyuan.com/rQj30
#include "Jpeg2AVI.h"
#include "list.h"
#include <stdlib.h>
#include <string.h>
#include "ff.h"
static int nframes; //总帧数
static int totalsize; //帧的总大小
static struct list_head list; //保存各帧图像大小的链表,用于写索引块
unsigned int bww;
/*链表宿主结构,用于保存真正的图像大小数据*/
struct ListNode
{
int value;
struct list_head head; //双链表
};
static void write_index_chunk(FIL *fp)
{unsigned char count;
unsigned char index[4] = {'i', 'd', 'x', '1'}; //索引块ID
unsigned int index_chunk_size = 16 * nframes; //索引块大小
unsigned int offset = 4;
struct list_head *slider = NULL; //
struct list_head *tmpslider = NULL; //
f_write(fp,index,4,&bww); //写数据:索引字符(4B)
f_write(fp,&index_chunk_size,4,&bww); //写数据:索引大小字符(4B)
list_for_each_safe(slider, tmpslider, &list) //宏定义见下行
//for(slider=(head)->next,tmpslider=pos->next; pos!= (head);pos=tmpslider,tmpslider=pos->next)
{//【运行次数存疑】
unsigned char tmp[4] = {'0', '0', 'd', 'c'}; //00dc = 压缩的视频数据
unsigned int keyframe = 0x10; //0x10表示当前帧为关键帧
struct ListNode *node = list_entry(slider, struct ListNode, head); //获取数值
f_write(fp,tmp,4,&bww); //写数据:视频块前导码(4B)
f_write(fp,&keyframe,4,&bww); //写数据:关键帧(4B)
f_write(fp,&offset,4,&bww); //写数据:(4B)
f_write(fp,&node->value,4,&bww); //写数据:(4B)
offset = offset + node->value + 8; //计算偏移
list_del(slider); //从清单中删除对应入口
free(node); //释放
}
}
static void back_fill_data(FIL *fp, int width, int height, int fps)
{//回填数据
AVI_RIFF_HEAD riff_head =
{
{'R', 'I', 'F', 'F'},
4 + sizeof(AVI_HDRL_LIST) + sizeof(AVI_LIST_HEAD) + nframes * 8 + totalsize,
{'A', 'V', 'I', ' '}
};
AVI_HDRL_LIST hdrl_list =
{
{'L', 'I', 'S', 'T'},
sizeof(AVI_HDRL_LIST) - 8,
{'h', 'd', 'r', 'l'},
{//关于帧率,L63 L75是关键配置
{'a', 'v', 'i', 'h'},
sizeof(AVI_AVIH_CHUNK) - 8,
1000000 / fps, 25000, 0, 0, nframes, 0, 1, 100000, width, height,
{0, 0, 0, 0}
},
{
{'L', 'I', 'S', 'T'},
sizeof(AVI_STRL_LIST) - 8,
{'s', 't', 'r', 'l'},
{
{'s', 't', 'r', 'h'},
sizeof(AVI_STRH_CHUNK) - 8,
{'v', 'i', 'd', 's'},
{'J', 'P', 'E', 'G'},
0, 0, 0, 0, 1, fps, 0, nframes, 100000, 0xFFFFFF, 0,
{0, 0, width, height}
},
{
{'s', 't', 'r', 'f'},
sizeof(AVI_STRF_CHUNK) - 8,
sizeof(AVI_STRF_CHUNK) - 8,
width, height, 1, 24,
{'J', 'P', 'E', 'G'},
width * height * 3, 0, 0, 0, 0
}
}
};
AVI_LIST_HEAD movi_list_head =
{
{'L', 'I', 'S', 'T'},
4 + nframes * 8 + totalsize,
{'m', 'o', 'v', 'i'}
};
//定位到文件头,回填各块数据
f_lseek(fp, 0);
f_write(fp,&riff_head,sizeof(riff_head),&bww); //写RIFF头
f_write(fp,&hdrl_list,sizeof(hdrl_list),&bww); //写HDRL头
f_write(fp,&movi_list_head,sizeof(movi_list_head),&bww); //【关键数据写入位置】写MOVE_LIST头
}
void jpeg2avi_start(FIL *fp)
{//AVI文件头
int offset1 = sizeof(AVI_RIFF_HEAD); //riff head大小:C
int offset2 = sizeof(AVI_HDRL_LIST); //hdrl list大小:C8
int offset3 = sizeof(AVI_LIST_HEAD); //movi list head大小:C
//AVI文件偏移量设置到movi list head后,从该位置向后依次写入JPEG数据
f_lseek(fp, offset1 + offset2 + offset3); //不确定具体改法
//初始化链表
list_head_init(&list);
nframes = 0;
totalsize = 0;
}
void jpeg2avi_add_frame(FIL *fp, void *data, unsigned int len)
{//
unsigned char tmp[4] = {'0', '0', 'd', 'c'}; //00dc = 压缩的视频数据
struct ListNode *node = (struct ListNode *)malloc(sizeof(struct ListNode));
/*JPEG图像大小4字节对齐*/
while (len % 4)
{
len++;
}
f_write(fp,tmp,4,&bww); //写入是否是压缩的视频数据信息
f_write(fp,&len,4,&bww); //写入4字节对齐后的JPEG图像大小
f_write(fp,data,len,&bww); //写入真正的JPEG数据【干货数据】
nframes += 1;
totalsize += len;
/*将4字节对齐后的JPEG图像大小保存在链表中*/
if (node != NULL)
{
node->value = len; //将长度值赋予链表项
list_add_tail(&node->head, &list); //【重点排查】插入到链表尾部,主要在list.c中
}//所在链表,单链表list_head
}
void jpeg2avi_end(FIL *fp, int width, int height, int fps)
{//写索引块,
write_index_chunk(fp);
//从文件头开始,回填各块数据
back_fill_data(fp, width, height, fps);
}
对应链表定义如下
//**************list.c************
#include "list.h"
#include <stdio.h>
static void __list_add(struct list_head *_new, struct list_head *prev, struct list_head *next)
{
next->prev = _new;
_new->next = next;
_new->prev = prev;
prev->next = _new;
}
static void __list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
void list_head_init(struct list_head *list)
{
list->next = list;
list->prev = list;
}
/**
* list_add_tail - insert a new entry before the specified head
* @_new: new entry to be added
* @head: list head to add it before
*/
void list_add_tail(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head->prev, head);
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
*/
void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = NULL;
entry->prev = NULL;
}
//**************list.h***************
// 参考 https://article.itxueyuan.com/rQj30
#ifndef _LIST_H_
#define _LIST_H_
struct list_head
{
struct list_head *next;
struct list_head *prev;
};
void list_head_init(struct list_head *list);
void list_add_tail(struct list_head *_new, struct list_head *head);
void list_del(struct list_head *entry);
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#ifndef container_of
#define container_of(ptr, type, member) ((type *)((char *)ptr - offsetof(type,member)))
#endif
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) container_of(ptr, type, member)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) for (pos = (head)->next, n = pos->next; pos != (head); pos = n, n = pos->next)
#endif //_LIST_H_
|
|