#include "basic_include.h" #include "lib/multimedia/msi.h" #include "stream_define.h" #include "lib/video/h264/h264_drv.h" #include "osal/work.h" #include "lib/video/dvp/jpeg/jpg.h" #include "hal/h264.h" #include "lib/heap/av_heap.h" #include "lib/heap/av_psram_heap.h" #include "lib/heap/av_heap.h" #include "lib/heap/av_psram_heap.h" #include "gen420_hardware_msi.h" #include "user_work/user_work.h" #include "hal/scale.h" extern uint32 get_h264_srcID(void *d); extern uint8_t get_vpp_w_h(uint16_t *w, uint16_t *h); extern uint8_t get_vpp1_w_h(uint16_t *w, uint16_t *h); extern uint32 get_h264_w_h(void *d, uint16_t *w, uint16_t *h); extern uint8_t get_vpp_scale_w_h(uint16_t *w, uint16_t *h); extern void set_vpp_scale_w_h(uint8_t en, uint16_t w, uint16_t h); #ifndef SAVE_COUNT #define SAVE_COUNT 4 #endif #define USE_WORK 1 #define MAX_BYTES 0 #if VIDEO_YUV_RANGE_TYPE #undef MAX_BYTES #define MAX_BITS 160 #define MAX_BYTES (MAX_BITS / 8) // 位缓冲区结构体 typedef struct { uint8_t data[MAX_BYTES]; // 存储数据的字节数组 int current_bit_pos; // 当前位位置(0-127) } BitBuffer; // 初始化位缓冲区 void bit_buffer_init(BitBuffer *buffer) { // memset(buffer->data, 0, sizeof(buffer->data)); buffer->current_bit_pos = 0; } // 设置位缓冲区 void bit_buffer_set(BitBuffer *buffer, uint8_t data) { buffer->data[buffer->current_bit_pos / 8] = data; buffer->current_bit_pos += 8; } // 二进制bit左移拼接函数 // 输入:buffer - 位缓冲区指针 // bits - 要添加的二进制数据(最多16位) // bit_count - 二进制位数(1-16) // padding - 是否补零到8的倍数(1:是,0:否) // 返回值:成功添加的位数 int bit_buffer_append(BitBuffer *buffer, uint16_t bits, int bit_count, int padding) { if (bit_count < 1 || bit_count > 16) { os_printf(KERN_ERR "err:bit_count must 1-16\n"); return 0; } if (buffer->current_bit_pos + bit_count > MAX_BITS) { printf(KERN_ERR "warning:buff full\n"); bit_count = MAX_BITS - buffer->current_bit_pos; if (bit_count <= 0) { return 0; } } // 确保只取bit_count位 bits &= (1 << bit_count) - 1; // 逐个bit添加 for (int i = bit_count - 1; i >= 0; i--) { int current_byte = buffer->current_bit_pos / 8; int current_bit_in_byte = 7 - (buffer->current_bit_pos % 8); // 从高位到低位 // 获取当前bit的值 uint8_t bit_value = (bits >> i) & 1; // 设置对应的bit位 if (bit_value) { buffer->data[current_byte] |= (1 << current_bit_in_byte); } else { buffer->data[current_byte] &= ~(1 << current_bit_in_byte); } buffer->current_bit_pos++; } // 如果需要补零到8的倍数 if (padding) { int remainder = buffer->current_bit_pos % 8; if (remainder != 0) { int bits_to_pad = 8 - remainder; if (buffer->current_bit_pos + bits_to_pad > MAX_BITS) { // printf("warning:buff full when fill zero\n"); bits_to_pad = MAX_BITS - buffer->current_bit_pos; } for (int i = 0; i < bits_to_pad; i++) { int current_byte = buffer->current_bit_pos / 8; int current_bit_in_byte = 7 - (buffer->current_bit_pos % 8); // 补零 buffer->data[current_byte] &= ~(1 << current_bit_in_byte); buffer->current_bit_pos++; } // printf("fill zero %d bit,make byte align\n", bits_to_pad); } } return bit_count; } // 打印缓冲区内容(十六进制格式) void bit_buffer_print_hex(const BitBuffer *buffer) { int bytes_used = (buffer->current_bit_pos + 7) / 8; for (int i = 0; i < bytes_used; i++) { os_printf("buff[%02d]:%02x\n", i, buffer->data[i]); } } // 获取当前已使用的字节数 int bit_buffer_get_used_bytes(const BitBuffer *buffer) { return (buffer->current_bit_pos + 7) / 8; } // 指数哥伦布编码 uint8_t len_exp = 0; uint8_t len_pre = 0; uint8_t len_suf = 0; uint16 exp_out = 0; void ue_se_enc(uint16 in_data) { len_exp = 0; len_pre = 0; len_suf = 0; exp_out = 0; if ((in_data & 0x1e0) != 0) { if ((in_data & 0x100) != 0) { len_exp = 14; len_pre = 7; len_suf = 8; } else if ((in_data & 0x080) != 0) { len_exp = 14; len_pre = 6; len_suf = 7; } else if ((in_data & 0x040) != 0) { len_exp = 12; len_pre = 5; len_suf = 6; } else { len_exp = 10; len_pre = 4; len_suf = 5; } } else { if ((in_data & 0x010) != 0) { len_exp = 8; len_pre = 3; len_suf = 4; } else if ((in_data & 0x008) != 0) { len_exp = 6; len_pre = 2; len_suf = 3; } else if ((in_data & 0x004) != 0) { len_exp = 4; len_pre = 1; len_suf = 2; } else if ((in_data & 0x002) != 0) { len_exp = 2; len_pre = 0; len_suf = 1; } else { len_exp = 0; len_pre = 0; len_suf = 1; } } len_exp += 1; len_pre += 1; len_suf += 1; exp_out = (in_data & 0x1ff); } // main 1920 1080 void h264_sps_gen_test(BitBuffer *buffer, uint16_t wrap_w, uint16_t wrap_h, uint8_t fr) { uint8_t level_idc = 52; // h264 0x50 uint16 img_x = wrap_w / 16; uint16 img_y = (wrap_h + 0xf) / 16; uint8_t crop_en = 0; uint8_t crop_y = (img_y * 16 - wrap_h) / 2; uint8_t full_range = fr; if (crop_y) { crop_en = 1; } bit_buffer_init(buffer); // fixed bit_buffer_set(buffer, 0x00); bit_buffer_set(buffer, 0x00); bit_buffer_set(buffer, 0x00); bit_buffer_set(buffer, 0x01); bit_buffer_set(buffer, 0x67); bit_buffer_set(buffer, 0x4d); bit_buffer_set(buffer, 0x00); bit_buffer_set(buffer, level_idc); bit_buffer_set(buffer, 0x96); bit_buffer_set(buffer, 0x54); // img width ue_se_enc(img_x); bit_buffer_append(buffer, 0x0000, len_pre, 0); bit_buffer_append(buffer, exp_out, len_suf, 0); // img height ue_se_enc(img_y); bit_buffer_append(buffer, 0x0000, len_pre, 0); bit_buffer_append(buffer, exp_out, len_suf, 0); // other bit_buffer_append(buffer, 0x0003, 2, 0); // img crop if (crop_en) { bit_buffer_append(buffer, 0x000f, 4, 0); ue_se_enc((crop_y + 1)); bit_buffer_append(buffer, exp_out, len_exp, 0); } else { bit_buffer_append(buffer, 0x0000, 1, 0); } // full range if (full_range) { bit_buffer_append(buffer, 0x4880, 15, 0); } else { bit_buffer_append(buffer, 0x0000, 1, 0); } // byte align bit_buffer_append(buffer, 0x0001, 1, 1); // bit_buffer_print_hex(buffer); } #endif enum video_app_h264_enum { VIDEO_APP_H264_EXIT = BIT(0), VIDEO_APP_H264_STOP = BIT(1), }; // data申请空间函数 #define STREAM_MALLOC av_psram_malloc #define STREAM_FREE av_psram_free #define STREAM_ZALLOC av_psram_zalloc // 结构体申请空间函数 #ifdef MORE_SRAM #define STREAM_LIBC_MALLOC av_psram_malloc #define STREAM_LIBC_FREE av_psram_free #define STREAM_LIBC_ZALLOC av_psram_zalloc #else #define STREAM_LIBC_MALLOC av_malloc #define STREAM_LIBC_FREE av_free #define STREAM_LIBC_ZALLOC av_zalloc #endif #define MAX_VIDEO_APP_264 64 // 获取nal的size,从0开始搜索,返回的是的nal头的size,offset相对于头的偏移(通过多次调用,可以用于计算nal_size) // 返回0代表搜索不到nal的头 static uint8_t get_nal_size(uint8_t *buf, uint32_t size, uint32_t *offset) { uint32_t pos = 0; while ((size - pos) > 3) { if (buf[pos] == 0 && buf[pos + 1] == 0 && buf[pos + 2] == 1) { *offset = pos; return 3; } if (buf[pos] == 0 && buf[pos + 1] == 0 && buf[pos + 2] == 0 && buf[pos + 3] == 1) { *offset = pos; return 4; } pos++; } return 0; } // 只是获取pps和sps的nalsize static uint8_t *get_sps_pps_nal_size(uint8_t *buf, uint32_t size, uint32_t *nal_size, uint8_t *head_size) { uint32_t offset; uint8_t nal_head_size = get_nal_size(buf, size, &offset); uint8_t nal_type; uint8_t *ret_buf = NULL; // 找到头部,检查类型 if (nal_head_size && offset + nal_head_size < size) { nal_type = buf[nal_head_size + offset] & 0x1f; // 找到sps和pps就返回长度和偏移(相对buf的偏移) if (nal_type == 7 || nal_type == 8) { // 查找下一个nal nal_head_size = get_nal_size(buf + offset + nal_head_size, size - (offset + nal_head_size), nal_size); // os_printf("nal_head_size:%d\tnal_size:%d\n",nal_head_size,*nal_size); if (nal_head_size) { // 偏移到nal的头部 ret_buf = buf + offset; // 返回nal的头size *head_size = nal_head_size; } } } return ret_buf; } uint8_t *skip_sps(uint8_t *next_nal_buf, uint32_t size, uint32_t *skip_size) { uint8_t sps_times = 0; uint8_t nal_head_size; uint32_t nal_size; uint8_t pps_sps_size = 0; uint8_t *sps_pps_buf = NULL; while (sps_times < 1) { sps_pps_buf = get_sps_pps_nal_size(next_nal_buf, 64, &nal_size, &nal_head_size); if (sps_pps_buf && (sps_pps_buf[nal_head_size] & 0x1f) == 7) { pps_sps_size += (nal_size + nal_head_size); next_nal_buf = sps_pps_buf + nal_size + nal_head_size; } // 不匹配,就不再去获取pps或者sps了 else { break; } // os_printf("sps_pps_buf[nal_head_size]& 0x1f):%d\n",sps_pps_buf[nal_head_size]& 0x1f); sps_times++; } if (skip_size) { *skip_size = pps_sps_size; } return next_nal_buf; } #if USE_WORK == 0 struct video_h264_msi_s { // struct os_work work; struct os_task task; struct os_event evt; struct msi *msi; struct h264_device *h264_dev; uint16_t filter_type; uint16_t sub_stream_en; struct fbpool tx_pool; }; /******************************************************************************************** * I帧和P帧对应的位置图,主要通过寻找nal,定位pps和sps的起始位置并且记录 * 实际内容也会记录,到终端可以采取是否直接读取结构体的pps和sps(尽量从结构体读取) * * * 00 00 00 01 67 xx xx xx 00 00 00 01 68 xx xx xx 00 00 00 01 65 xx xx xx I帧 * sps_buf pps_buf start_len * * * * * * 00 00 00 01 61 xx xx xx P帧 * start_len *******************************************************************************************/ static int8_t h264_output_msi(struct list_head *get_f, struct video_h264_msi_s *video_h264) { int ret = RET_ERR; uint32_t node_len; uint32_t h264_len; uint32_t h264_len_tmp; uint32_t cp_len; uint32_t cp_offset; uint8_t *tmp_buf; uint32_t timestamp; uint8_t which; uint8_t srcID; uint8_t h264_type_frame; node_len = get_h264_node_len_new((void *) get_f); h264_len = get_h264_len(get_f); timestamp = get_h264_timestamp(get_f); h264_type_frame = get_h264_type(get_f); // 获取是否为I帧还是P帧 which = get_h264_which(get_f); srcID = get_h264_srcID(get_f); uint8_t count = get_h264_loop_num(get_f); struct framebuff *fb = NULL; if (!h264_len) { goto h264_output_msi_end; } fb = fbpool_get(&video_h264->tx_pool, 0, video_h264->msi); if (fb) { // 先去msi寻找是否有空闲节点,如果有,才需要拷贝,否则就丢弃 uint8_t *h264_buf = (uint8_t *) STREAM_MALLOC(h264_len + SAVE_COUNT); if (!h264_buf) { msi_delete_fb(NULL, fb); goto h264_output_msi_end; } // 拷贝264的数据 h264_len_tmp = h264_len; cp_offset = 0; while (h264_buf) { // 图片节点提取 if (list_empty(get_f) || h264_len_tmp == 0) { break; } if (h264_len_tmp > node_len) { cp_len = node_len; } else { cp_len = h264_len_tmp; } h264_len_tmp -= cp_len; tmp_buf = get_h264_first_buf(get_f); hw_memcpy0(h264_buf + cp_offset, get_h264_first_buf(get_f), cp_len); del_h264_first_node(get_f); cp_offset += cp_len; } // 264末尾加一个序号 if (SAVE_COUNT > 0) { os_sprintf(h264_buf + h264_len, "%03d", count & 0xff); h264_buf[h264_len + 3] = '#'; } sys_dcache_clean_range((uint32_t *) h264_buf, h264_len + SAVE_COUNT); fb->data = (uint8_t *) h264_buf; fb->len = h264_len + SAVE_COUNT; fb->mtype = F_H264; fb->stype = which + FSTYPE_H264_VPP_DATA0; // 基于stype某个值 fb->time = timestamp; fb->srcID = srcID; // I帧 if (h264_type_frame == 1) { // 先去读取sps和pps的长度 uint8_t nal_head_size; uint32_t nal_size; uint8_t *sps_pps_buf = fb->data; uint8_t *pps_buf = NULL; uint8_t *sps_buf = NULL; uint8_t pps_len = 0, sps_len = 0; uint8_t pps_sps_times = 0; uint8_t *next_nal_buf = fb->data; // 读取sps和pps,仅仅读取两次,没有就退出 while (sps_pps_buf && pps_sps_times < 2) { sps_pps_buf = get_sps_pps_nal_size(next_nal_buf, 64, &nal_size, &nal_head_size); if (sps_pps_buf && (sps_pps_buf[nal_head_size] & 0x1f) == 7) { sps_buf = sps_pps_buf + nal_head_size; sps_len = nal_size; next_nal_buf = sps_pps_buf + nal_size + nal_head_size; } else if (sps_pps_buf && (sps_pps_buf[nal_head_size] & 0x1f) == 8) { pps_buf = sps_pps_buf + nal_head_size; pps_len = nal_size; next_nal_buf = sps_pps_buf + nal_size + nal_head_size; } // 不匹配,就不再去获取pps或者sps了 else { break; } // os_printf("sps_pps_buf[nal_head_size]& 0x1f):%d\n",sps_pps_buf[nal_head_size]& 0x1f); pps_sps_times++; } struct fb_h264_s *priv = (struct fb_h264_s *) STREAM_LIBC_ZALLOC(sizeof(struct fb_h264_s)); // 寻找一下nal头有多少字节 uint32_t pos; uint8_t h264_nal_size = get_nal_size(next_nal_buf, 16, &pos); priv->pps = pps_buf; priv->pps_len = pps_len; priv->sps = sps_buf; priv->sps_len = sps_len; fb->priv = (void *) priv; priv->type = 1; priv->count = count; priv->start_len = next_nal_buf - fb->data + pos + h264_nal_size; // os_printf("priv->start_len:%d\ttype:%d\n",priv->start_len,fb->data[priv->start_len]&0x1f); // os_printf("pps:%d\tsps:%d\n",pps_buf[0]&0x1f,sps_buf[0]&0x1f); } else { struct fb_h264_s *priv = (struct fb_h264_s *) STREAM_LIBC_ZALLOC(sizeof(struct fb_h264_s)); uint32_t pos; uint8_t h264_nal_size = get_nal_size(fb->data, 16, &pos); priv->type = 2; fb->priv = (void *) priv; priv->count = count; priv->start_len = pos + h264_nal_size; } //_os_printf("H%d", h264_type_frame); // 在msi_output_fb后,不要继续调用其他和msi有关的东西,因为有可能在这个之后,会释放对应的内存 msi_output_fb(video_h264->msi, fb); ret = RET_OK; } h264_output_msi_end: return ret; } static int32 h264_msi_thread(void *d) { struct video_h264_msi_s *video_h264 = (struct video_h264_msi_s *) d; uint32_t flags; // uint8_t which; uint32_t stop_flag = 0; msi_get(video_h264->msi); while (1) { // 如果需要退出,则直接退出线程 os_event_wait(&video_h264->evt, VIDEO_APP_H264_STOP, &stop_flag, OS_EVENT_WMODE_OR, 0); if (stop_flag & VIDEO_APP_H264_STOP) { break; } flags = disable_irq(); struct list_head *get_f = (void *) get_h264_frame(); enable_irq(flags); if (get_f) { h264_output_msi(get_f, video_h264); del_h264_frame(get_f); } else { os_sleep_ms(1); } } // 设置退出标志 os_event_set(&video_h264->evt, VIDEO_APP_H264_EXIT, NULL); // 释放对应的msi(不是open的),实际是这个时候才会真正释放 msi_put(video_h264->msi); return 0; } static int32_t h264_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2) { int32_t ret = RET_OK; struct video_h264_msi_s *video_h264 = (struct video_h264_msi_s *) msi->priv; switch (cmd_id) { case MSI_CMD_POST_DESTROY: { h264_mem_init(0, 0, 0, 0, 0); fbpool_destroy(&video_h264->tx_pool); // 等待线程退出 os_event_wait(&video_h264->evt, VIDEO_APP_H264_EXIT, NULL, OS_EVENT_WMODE_CLEAR, -1); os_event_del(&video_h264->evt); STREAM_LIBC_FREE(video_h264); } break; case MSI_CMD_PRE_DESTROY: { // os_work_cancle2(&video_h264->work, 1); os_event_set(&video_h264->evt, VIDEO_APP_H264_STOP, NULL); h264_close(video_h264->h264_dev); if (video_h264->sub_stream_en) { unregister_gen420_queue(GEN420_QUEUE_H264); } } break; case MSI_CMD_FREE_FB: { struct framebuff *fb = (struct framebuff *) param1; if (fb->data) { STREAM_FREE(fb->data); fb->data = NULL; } if (fb->priv) { STREAM_LIBC_FREE(fb->priv); fb->priv = NULL; } fbpool_put(&video_h264->tx_pool, fb); // 不需要内核去释放fb ret = RET_OK + 1; } break; default: break; } return ret; } struct msi *h264_msi_init_with_mode(uint32_t drv1_from, uint32_t drv1_w, uint32_t drv1_h, uint32_t drv2_from, uint32_t drv2_w, uint32_t drv2_h) { int ret = 0; struct msi *msi = msi_new(S_H264, 0, NULL); struct video_h264_msi_s *video_h264 = (struct video_h264_msi_s *) msi->priv; if (!video_h264) { video_h264 = (struct video_h264_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct video_h264_msi_s)); msi->priv = (void *) video_h264; msi->enable = 1; msi->action = h264_msi_action; video_h264->msi = msi; fbpool_init(&video_h264->tx_pool, MAX_VIDEO_APP_264); os_event_init(&video_h264->evt); video_h264->h264_dev = (struct h264_device *) dev_get(HG_H264_DEVID); if (video_h264->h264_dev) { if (drv1_from == GEN420_DATA || drv2_from == GEN420_DATA) { extern int32_t h264_gen420_kick(); uint32_t gen_w = drv1_from == GEN420_DATA ? drv1_w : drv2_w; uint32_t gen_h = drv1_from == GEN420_DATA ? drv1_h : drv2_h; ret = register_gen420_queue(GEN420_QUEUE_H264, gen_w, gen_h, h264_gen420_kick, NULL, (uint32) NULL); if (!ret) { video_h264->sub_stream_en = 1; } else { video_h264->sub_stream_en = 0; if (drv1_from == GEN420_DATA) { drv1_from = 0; drv1_w = 0; drv1_h = 0; } if (drv2_from == GEN420_DATA) { drv2_from = 0; drv2_w = 0; drv2_h = 0; } } } // 自己适应w和h if (drv1_from == VPP_DATA0) { get_vpp_w_h(&drv1_w, &drv1_h); } else if (drv1_from == VPP_DATA1) { get_vpp1_w_h(&drv1_w, &drv1_h); } if (drv2_from == VPP_DATA0) { get_vpp_w_h(&drv2_w, &drv2_h); } else if (drv2_from == VPP_DATA1) { get_vpp1_w_h(&drv2_w, &drv2_h); } h264_set_oe_select(video_h264->h264_dev, 0, 0); h264_enc(drv1_from, drv1_w, drv1_h, drv2_from, drv2_w, drv2_h); h264_open(video_h264->h264_dev); h264_set_sw_ready(video_h264->h264_dev); } // 启动workqueue // 创建一个任务去做h264的工作 OS_TASK_INIT("video_h264", &video_h264->task, h264_msi_thread, (void *) video_h264, OS_TASK_PRIORITY_ABOVE_NORMAL, NULL, 1024); // OS_WORK_INIT(&video_h264->work, h264_msi_work, 0); // os_run_work_delay(&video_h264->work, 1); } return msi; } struct msi *h264_msi_init_with_mode_for_264wq(uint32_t drv1_from, uint32_t drv1_w, uint32_t drv1_h) { int ret = 0; struct msi *msi = msi_new(S_H264, 0, NULL); struct video_h264_msi_s *video_h264 = (struct video_h264_msi_s *) msi->priv; if (!video_h264) { video_h264 = (struct video_h264_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct video_h264_msi_s)); msi->priv = (void *) video_h264; msi->enable = 1; msi->action = h264_msi_action; video_h264->msi = msi; fbpool_init(&video_h264->tx_pool, MAX_VIDEO_APP_264); os_event_init(&video_h264->evt); video_h264->h264_dev = (struct h264_device *) dev_get(HG_H264_DEVID); if (video_h264->h264_dev) { if (drv1_from == GEN420_DATA) { extern int32_t h264_gen420_kick(); uint32_t gen_w = drv1_w; uint32_t gen_h = drv1_h; ret = register_gen420_queue(GEN420_QUEUE_H264, gen_w, gen_h, h264_gen420_kick, NULL, (uint32) NULL); if (!ret) { video_h264->sub_stream_en = 1; } else { video_h264->sub_stream_en = 0; if (drv1_from == GEN420_DATA) { drv1_from = 0; drv1_w = 0; drv1_h = 0; } } } // 自己适应w和h if (drv1_from == VPP_DATA0) { get_vpp_w_h(&drv1_w, &drv1_h); } else if (drv1_from == VPP_DATA1) { get_vpp1_w_h(&drv1_w, &drv1_h); } h264_set_oe_select(video_h264->h264_dev, 0, 0); h264_enc(drv1_from, drv1_w, drv1_h, -1, 0, 0); // h264_open(video_h264->h264_dev); } // 启动workqueue // 创建一个任务去做h264的工作 OS_TASK_INIT("video_h264", &video_h264->task, h264_msi_thread, (void *) video_h264, OS_TASK_PRIORITY_ABOVE_NORMAL, NULL, 1024); // OS_WORK_INIT(&video_h264->work, h264_msi_work, 0); // os_run_work_delay(&video_h264->work, 1); } return msi; } #else struct video_h264_msi_s { struct os_work work; struct os_event evt; struct msi *msi; struct h264_device *h264_dev; uint16_t filter_type; uint16_t sub_stream_en; uint8_t drv1_from; uint8_t drv2_from; struct fbpool tx_pool; }; #if VIDEO_YUV_RANGE_TYPE uint8_t h264_dec_sps_src_param(uint32 w, uint32 h, uint8_t *buf) { BitBuffer buffer; // uint32 itk; buffer.current_bit_pos = 0; h264_sps_gen_test(&buffer, w, h, 0); memcpy(buf, buffer.data, buffer.current_bit_pos / 8); return buffer.current_bit_pos / 8; } #endif /******************************************************************************************** * I帧和P帧对应的位置图,主要通过寻找nal,定位pps和sps的起始位置并且记录 * 实际内容也会记录,到终端可以采取是否直接读取结构体的pps和sps(尽量从结构体读取) * * * 00 00 00 01 67 xx xx xx 00 00 00 01 68 xx xx xx 00 00 00 01 65 xx xx xx I帧 * sps_buf pps_buf start_len * * * * * * 00 00 00 01 61 xx xx xx P帧 * start_len *******************************************************************************************/ static int8_t h264_output_msi(struct list_head *get_f, struct video_h264_msi_s *video_h264) { int ret = RET_ERR; uint32_t node_len; uint32_t h264_len; uint32_t h264_len_tmp; uint32_t cp_len; uint32_t cp_offset; uint8_t *tmp_buf; uint32_t timestamp; uint8_t which; uint8_t srcID; uint16_t w, h; uint8_t h264_type_frame; node_len = get_h264_node_len_new((void *) get_f); h264_len = get_h264_len(get_f); timestamp = get_h264_timestamp(get_f); h264_type_frame = get_h264_type(get_f); // 获取是否为I帧还是P帧 which = get_h264_which(get_f); srcID = get_h264_srcID(get_f); uint8_t count = get_h264_loop_num(get_f); struct framebuff *fb = NULL; get_h264_w_h((void *) get_f, &w, &h); // 新的sps和pps的buf if (!h264_len) { goto h264_output_msi_end; } #if VIDEO_YUV_RANGE_TYPE == 1 BitBuffer buffer; // os_printf("h264_type_frame:%d\n", h264_type_frame); // 如果是I帧,就重新生成一下对应的sps和pps if (h264_type_frame == 1) { buffer.current_bit_pos = 0; h264_sps_gen_test(&buffer, w, h, 1); } #endif fb = fbpool_get(&video_h264->tx_pool, 0, video_h264->msi); if (fb) { // 先去msi寻找是否有空闲节点,如果有,才需要拷贝,否则就丢弃 // MAX_BYTES是重新生成的sps和pps的最大空间 uint8_t *h264_buf = (uint8_t *) STREAM_MALLOC(h264_len + SAVE_COUNT + MAX_BYTES); if (!h264_buf) { msi_delete_fb(NULL, fb); goto h264_output_msi_end; } uint32_t reserve_len = 0; #if VIDEO_YUV_RANGE_TYPE == 1 uint32_t skip_size; if (h264_type_frame == 1) { uint8_t *new_sps_pps_buf = get_h264_first_buf(get_f); skip_sps(new_sps_pps_buf, 128, &skip_size); if (skip_size) { reserve_len = bit_buffer_get_used_bytes(&buffer) - skip_size; } } #endif // 拷贝264的数据 h264_len_tmp = h264_len; // 设置预留的长度 cp_offset = reserve_len; while (h264_buf) { // 图片节点提取 if (list_empty(get_f) || h264_len_tmp == 0) { break; } if (h264_len_tmp > node_len) { cp_len = node_len; } else { cp_len = h264_len_tmp; } h264_len_tmp -= cp_len; tmp_buf = get_h264_first_buf(get_f); hw_memcpy0(h264_buf + cp_offset, get_h264_first_buf(get_f), cp_len); del_h264_first_node(get_f); cp_offset += cp_len; } #if VIDEO_YUV_RANGE_TYPE == 1 // skip_size代表找到sps,替换新的sps if (h264_type_frame == 1 && skip_size) { os_memcpy(h264_buf, buffer.data, bit_buffer_get_used_bytes(&buffer)); } #endif // 264末尾加一个序号 if (SAVE_COUNT > 0) { os_sprintf((char *) h264_buf + h264_len + reserve_len, "%03d", count & 0xff); h264_buf[h264_len + reserve_len + 3] = '#'; } sys_dcache_clean_range((uint32_t *) h264_buf, h264_len + SAVE_COUNT + reserve_len); fb->data = (uint8_t *) h264_buf; fb->len = h264_len + SAVE_COUNT + reserve_len; fb->mtype = F_H264; fb->stype = which + FSTYPE_H264_VPP_DATA0; // 基于stype某个值 fb->time = timestamp; fb->srcID = srcID; // I帧 if (h264_type_frame == 1) { // 先去读取sps和pps的长度 uint8_t nal_head_size; uint32_t nal_size; uint8_t *sps_pps_buf = fb->data; uint8_t *pps_buf = NULL; uint8_t *sps_buf = NULL; uint8_t pps_len = 0, sps_len = 0; uint8_t pps_sps_times = 0; uint8_t *next_nal_buf = fb->data; // 读取sps和pps,仅仅读取两次,没有就退出 while (sps_pps_buf && pps_sps_times < 2) { sps_pps_buf = get_sps_pps_nal_size(next_nal_buf, 64, &nal_size, &nal_head_size); if (sps_pps_buf && (sps_pps_buf[nal_head_size] & 0x1f) == 7) { sps_buf = sps_pps_buf + nal_head_size; sps_len = nal_size; next_nal_buf = sps_pps_buf + nal_size + nal_head_size; } else if (sps_pps_buf && (sps_pps_buf[nal_head_size] & 0x1f) == 8) { pps_buf = sps_pps_buf + nal_head_size; pps_len = nal_size; next_nal_buf = sps_pps_buf + nal_size + nal_head_size; } // 不匹配,就不再去获取pps或者sps了 else { break; } // os_printf("sps_pps_buf[nal_head_size]& 0x1f):%d\n",sps_pps_buf[nal_head_size]& 0x1f); pps_sps_times++; } struct fb_h264_s *priv = (struct fb_h264_s *) STREAM_LIBC_ZALLOC(sizeof(struct fb_h264_s)); // 寻找一下nal头有多少字节 uint32_t pos; uint8_t h264_nal_size = get_nal_size(next_nal_buf, 16, &pos); priv->pps = pps_buf; priv->pps_len = pps_len; priv->sps = sps_buf; priv->sps_len = sps_len; fb->priv = (void *) priv; priv->type = 1; priv->count = count; priv->start_len = next_nal_buf - fb->data + pos + h264_nal_size; priv->w = w; priv->h = h; // os_printf("priv->start_len:%d\ttype:%d\n",priv->start_len,fb->data[priv->start_len]&0x1f); // os_printf("pps:%d\tsps:%d\n",pps_buf[0]&0x1f,sps_buf[0]&0x1f); } else { struct fb_h264_s *priv = (struct fb_h264_s *) STREAM_LIBC_ZALLOC(sizeof(struct fb_h264_s)); uint32_t pos; uint8_t h264_nal_size = get_nal_size(fb->data, 16, &pos); priv->type = 2; fb->priv = (void *) priv; priv->count = count; priv->start_len = pos + h264_nal_size; priv->w = w; priv->h = h; } //_os_printf("H%d", h264_type_frame); // 在msi_output_fb后,不要继续调用其他和msi有关的东西,因为有可能在这个之后,会释放对应的内存 msi_output_fb(video_h264->msi, fb); ret = RET_OK; } h264_output_msi_end: return ret; } static int32 h264_msi_work(struct os_work *work) { struct video_h264_msi_s *video_h264 = (struct video_h264_msi_s *) work; uint32_t flags; h264_msi_work_again: flags = disable_irq(); struct list_head *get_f = (void *) get_h264_frame(); enable_irq(flags); if (get_f) { h264_output_msi(get_f, video_h264); del_h264_frame(get_f); goto h264_msi_work_again; } os_run_work_delay(work, 1); // os_event_set(&video_h264->evt, VIDEO_APP_H264_EXIT, NULL); return 0; } static int32_t h264_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2) { int32_t ret = RET_OK; struct video_h264_msi_s *video_h264 = (struct video_h264_msi_s *) msi->priv; switch (cmd_id) { case MSI_CMD_POST_DESTROY: { h264_mem_init(0, 0, 0, 0, 0); fbpool_destroy(&video_h264->tx_pool); // 等待线程退出 // os_event_wait(&video_h264->evt, VIDEO_APP_H264_EXIT, NULL, OS_EVENT_WMODE_CLEAR, -1); os_event_del(&video_h264->evt); STREAM_LIBC_FREE(video_h264); } break; case MSI_CMD_PRE_DESTROY: { os_work_cancle2(&video_h264->work, 1); h264_close(video_h264->h264_dev); if (video_h264->sub_stream_en) { unregister_gen420_queue(GEN420_QUEUE_H264); } uint8_t scale1 = 0; // 把检查数据源是否为scale1,如果是scale1,同时把scale1一起关闭了 if (video_h264->drv1_from == SCALER_DATA) { scale1 = 1; } if (video_h264->drv2_from == SCALER_DATA) { scale1 = 1; } if (scale1) { struct scale_device *scale_dev = (struct scale_device *) dev_get(HG_SCALE1_DEVID); // 关闭scale1 scale_close(scale_dev); } } break; case MSI_CMD_FREE_FB: { struct framebuff *fb = (struct framebuff *) param1; if (fb->data) { STREAM_FREE(fb->data); fb->data = NULL; } if (fb->priv) { STREAM_LIBC_FREE(fb->priv); fb->priv = NULL; } fbpool_put(&video_h264->tx_pool, fb); // 不需要内核去释放fb ret = RET_OK + 1; } break; default: break; } return ret; } struct msi *h264_msi_init_with_mode(uint32_t drv1_from, uint16_t drv1_w, uint16_t drv1_h, uint16_t drv2_from, uint16_t drv2_w, uint16_t drv2_h) { int ret = 0; uint8_t isnew; struct msi *msi = msi_new(S_H264, 0, &isnew); struct video_h264_msi_s *video_h264; if (msi && isnew) { video_h264 = (struct video_h264_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct video_h264_msi_s)); ASSERT(video_h264); msi->priv = (void *) video_h264; msi->enable = 1; video_h264->h264_dev = (struct h264_device *) dev_get(HG_H264_DEVID); if (video_h264->h264_dev) { if (drv1_from == GEN420_DATA || drv2_from == GEN420_DATA) { extern int32_t h264_gen420_kick(); uint32_t gen_w = drv1_from == GEN420_DATA ? drv1_w : drv2_w; uint32_t gen_h = drv1_from == GEN420_DATA ? drv1_h : drv2_h; ret = register_gen420_queue(GEN420_QUEUE_H264, gen_w, gen_h, h264_gen420_kick, NULL, (uint32) NULL); if (!ret) { video_h264->sub_stream_en = 1; } else { video_h264->sub_stream_en = 0; if (drv1_from == GEN420_DATA) { drv1_from = 0; drv1_w = 0; drv1_h = 0; } if (drv2_from == GEN420_DATA) { drv2_from = 0; drv2_w = 0; drv2_h = 0; } } } // 自己适应w和h if (drv1_from == VPP_DATA0) { get_vpp_w_h(&drv1_w, &drv1_h); } else if (drv1_from == VPP_DATA1) { get_vpp1_w_h(&drv1_w, &drv1_h); } else if (drv1_from == SCALER_DATA) { ret = get_vpp_scale_w_h(&drv1_w, &drv1_h); if (!ret) { set_vpp_scale_w_h(1, drv1_w, drv1_h); } else { drv1_from = ~0; } } if (drv2_from == VPP_DATA0) { get_vpp_w_h(&drv2_w, &drv2_h); } else if (drv2_from == VPP_DATA1) { get_vpp1_w_h(&drv2_w, &drv2_h); } else if (drv2_from == SCALER_DATA) { ret = get_vpp_scale_w_h(&drv2_w, &drv2_h); if (!ret) { set_vpp_scale_w_h(1, drv2_w, drv2_h); } else { drv2_from = ~0; } } video_h264->drv1_from = drv1_from; video_h264->drv2_from = drv2_from; h264_set_oe_select(video_h264->h264_dev, 0, 0); ret = h264_enc(drv1_from, drv1_w, drv1_h, drv2_from, drv2_w, drv2_h); if (ret) { if (video_h264) { STREAM_LIBC_FREE(video_h264); } msi_destroy(msi); msi = NULL; goto h264_msi_init_with_mode_end; } else { h264_open(video_h264->h264_dev); h264_set_sw_ready(video_h264->h264_dev); msi->action = h264_msi_action; video_h264->msi = msi; fbpool_init(&video_h264->tx_pool, MAX_VIDEO_APP_264); os_event_init(&video_h264->evt); } } // 启动workqueue // 创建一个任务去做h264的工作 // OS_TASK_INIT("video_h264", &video_h264->task, h264_msi_thread, (void*)video_h264, OS_TASK_PRIORITY_ABOVE_NORMAL, NULL, 1024); OS_WORK_INIT(&video_h264->work, h264_msi_work, 0); os_run_work_delay(&video_h264->work, 1); } h264_msi_init_with_mode_end: return msi; } struct msi *h264_msi_init_with_timeLapse(uint32_t drv1_from, uint16_t drv1_w, uint16_t drv1_h, uint32_t timer) { int ret = 0; uint8_t isnew = 0; struct msi *msi = msi_new(S_H264, 0, &isnew); struct video_h264_msi_s *video_h264; if (msi && isnew) { video_h264 = (struct video_h264_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct video_h264_msi_s)); ASSERT(video_h264); msi->priv = (void *) video_h264; msi->enable = 1; video_h264->h264_dev = (struct h264_device *) dev_get(HG_H264_DEVID); if (video_h264->h264_dev) { // 自己适应w和h if (drv1_from == VPP_DATA0) { get_vpp_w_h(&drv1_w, &drv1_h); } else if (drv1_from == VPP_DATA1) { get_vpp1_w_h(&drv1_w, &drv1_h); } else if (drv1_from == SCALER_DATA) { ret = get_vpp_scale_w_h(&drv1_w, &drv1_h); if (!ret) { set_vpp_scale_w_h(1, drv1_w, drv1_h); } else { drv1_from = ~0; } } video_h264->drv1_from = drv1_from; video_h264->drv2_from = ~0; h264_set_oe_select(video_h264->h264_dev, 0, 0); ret = h264_enc_with_timeLapse(drv1_from, drv1_w, drv1_h, timer); if (ret) { if (video_h264) { STREAM_LIBC_FREE(video_h264); } msi_destroy(msi); msi = NULL; goto h264_msi_init_with_mode_end; } else { h264_open(video_h264->h264_dev); h264_set_sw_ready(video_h264->h264_dev); msi->action = h264_msi_action; video_h264->msi = msi; fbpool_init(&video_h264->tx_pool, MAX_VIDEO_APP_264); os_event_init(&video_h264->evt); } } // 启动workqueue // 创建一个任务去做h264的工作 // OS_TASK_INIT("video_h264", &video_h264->task, h264_msi_thread, (void*)video_h264, OS_TASK_PRIORITY_ABOVE_NORMAL, NULL, 1024); OS_WORK_INIT(&video_h264->work, h264_msi_work, 0); os_run_work_delay(&video_h264->work, 1); } h264_msi_init_with_mode_end: return msi; } struct msi *h264_msi_init_with_mode_for_264wq(uint32_t drv1_from, uint16_t drv1_w, uint16_t drv1_h) { int ret = 0; uint8_t isnew; struct msi *msi = msi_new(S_H264, 0, &isnew); struct video_h264_msi_s *video_h264; if (msi && isnew) { video_h264 = (struct video_h264_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct video_h264_msi_s)); ASSERT(video_h264); msi->priv = (void *) video_h264; msi->enable = 1; video_h264->h264_dev = (struct h264_device *) dev_get(HG_H264_DEVID); if (video_h264->h264_dev) { if (drv1_from == GEN420_DATA) { extern int32_t h264_gen420_kick(); uint32_t gen_w = drv1_w; uint32_t gen_h = drv1_h; ret = register_gen420_queue(GEN420_QUEUE_H264, gen_w, gen_h, h264_gen420_kick, NULL, (uint32) NULL); if (!ret) { video_h264->sub_stream_en = 1; } else { video_h264->sub_stream_en = 0; if (drv1_from == GEN420_DATA) { drv1_from = 0; drv1_w = 0; drv1_h = 0; } } } // 自己适应w和h if (drv1_from == VPP_DATA0) { get_vpp_w_h(&drv1_w, &drv1_h); } else if (drv1_from == VPP_DATA1) { get_vpp1_w_h(&drv1_w, &drv1_h); } h264_set_oe_select(video_h264->h264_dev, 0, 0); ret = h264_enc(drv1_from, drv1_w, drv1_h, -1, 0, 0); if (ret) { if (video_h264) { STREAM_LIBC_FREE(video_h264); } msi_destroy(msi); msi = NULL; goto h264_msi_init_with_mode_end; } else { h264_open(video_h264->h264_dev); msi->action = h264_msi_action; video_h264->msi = msi; fbpool_init(&video_h264->tx_pool, MAX_VIDEO_APP_264); os_event_init(&video_h264->evt); } } // 启动workqueue // 创建一个任务去做h264的工作 // OS_TASK_INIT("video_h264", &video_h264->task, h264_msi_thread, (void*)video_h264, OS_TASK_PRIORITY_ABOVE_NORMAL, NULL, 1024); OS_WORK_INIT(&video_h264->work, h264_msi_work, 0); os_run_work_delay(&video_h264->work, 1); } h264_msi_init_with_mode_end: return msi; } #endif