背景
需要将h264/h265编码转成svac
思路
先将采集过来的h264或者h265进行分析,看是否可以进行转码,如果可行,则交由中星微的转码设备进行svac转码,如果是rockship平台的话,因为要求性能要尽可能的高,就不经由ffmpeg来调用mpp了,而是直接调用mpp接口来进行h264->h265的转码,如果是普通的x86平台的话,考虑到通用性,用的ffmpg的库来进行的转码,调用svac的软库来进行转码,需要将这两种平台的差异抽象出来,方便上层应用来调用
代码
//lencoder.h #ifndef _LENCODER_H_ #define _LENCODER_H_ #include <stdint.h> #ifdef __cplusplus extern "C" { #endif enum H265_FRAME_TYPE { TRAIL_R = 1, BLA_W_LP = 16, BLA_W_RADL = 17, BLA_N_LP = 18, IDR_W_RADL = 19, IDR_N_LP = 20, VPS_NUT = 32, SPS_NUT = 33, PPS_NUT = 34, PREFIX_SEI_NUT = 39, SUFFIX_SEI_NUT = 40, }; enum H264_FRAME_TYPE { NAL_SLICE = 1, NAL_SLIC_DPA = 2, NAL_SLICE_DPB = 3, NAL_SLICE_DPC = 4, NAL_SLICE_IDR = 5, NAL_SEI = 6, NAL_SPS = 7, NAL_PPS = 8, NAL_AUD = 9, NAL_FILLER = 12, }; #define LENCODER_STATUS_OK 0X00000000 #define LENCODER_STATUS_ERR 0X01000000 #define LENCODER_NO_SPACE_ERR LENCODER_STATUS_ERR + 1 #define LENCODER_CREATE_ENCODER_ERR LENCODER_STATUS_ERR + 2 #define LENCODER_PREFETCH_ENCODER_ERR LENCODER_STATUS_ERR + 3 #define LENCODER_NO_IDLE_ERR LENCODER_STATUS_ERR + 4 #define LENCODER_TRANSOCER_ERR LENCODER_STATUS_ERR + 5 #define LENCODER_UNKNOWN_NALU_TYPE LENCODER_STATUS_ERR + 6 #define LENCODER_CONTINUING LENCODER_STATUS_ERR + 7 #define LENCODER_LOST_DEVICE LENCODER_STATUS_ERR + 8 #define LENCODER_OPEN_FAILED_DEVICE LENCODER_STATUS_ERR + 9 #define LENCODER_UNKNOWN_ENCODER_TYPE LENCODER_STATUS_ERR + 10 #define LENCODER_EMPTY_FRAME_TYPE LENCODER_STATUS_ERR + 11 #define LENCODER_MPP_CREATE_ERR LENCODER_STATUS_ERR + 12 #define LENCODER_MPP_WRITE_ERR LENCODER_STATUS_ERR + 13 #define LENCODER_MPP_GET_NULL LENCODER_STATUS_ERR + 14 #define LENCODER_MPP_WRITE_FULL LENCODER_STATUS_ERR + 15 #define LENCODER_LOAD_CFG_ERR LENCODER_STATUS_ERR + 16 #define LENCODER_LOAD_CFG_VALUE_ERR LENCODER_STATUS_ERR + 17 typedef struct lencoder_context_t * lencoder_context; typedef struct lencoder_mpp_context_t * lencoder_mpp_context; typedef struct lencoder_t * lencoder; /* typedef struct lencoder_frame_t { int frame_type; //1 I帧 2 P帧 unsigned char *frame; int frame_len; }lencoder_frame; */ int lencoder_init(lencoder *coder); int lencoder_maxnum_get(lencoder coder); int lencoder_open_ext(lencoder coder, lencoder_context *context, int fmt, unsigned char *pbuf, int buflen, int bit_rate, int fps, int gop); int lencoder_open(lencoder coder, lencoder_context *context, int fmt, unsigned char *pbuf, int buflen); //1 I帧 2 P帧 //while(lencoder_get_frame(ctx, &frame, &frame_len, &frame_type) == LENCODER_STATUS_OK) { // //free(frame); //} int lencoder_get_frame_ext(lencoder_context context, unsigned char **frame, unsigned int *frame_len, int *frame_type, int *iskey, int64_t *pts, int64_t *dts); int lencoder_get_frame(lencoder_context context, unsigned char **frame, unsigned int *frame_len, int *frame_type); int lencoder_transcode(lencoder_context context, unsigned char *in, int in_len); void lencoder_close(lencoder coder, lencoder_context *context); int lencoder_mpp_open(lencoder coder, lencoder_mpp_context *context); // @brief 指定待转码的编码格式以及解码后再编码格式 // @param coder 句柄 // @param context 转码会话 // @param src_fmt 1.H264 2.H265 // @param dst_fmt 1.H264 2.H265 // @return LENCODER_STATUS_OK 否则返回错误码 int lencoder_mpp_open_ext(lencoder coder, lencoder_mpp_context *context, int src_fmt, int dst_fmt); int lencoder_mpp_get_frame(lencoder_mpp_context context, unsigned char **out, int *out_len); int lencoder_mpp_transcode(lencoder_mpp_context context, unsigned char *in, int in_len); void lencoder_mpp_close(lencoder_mpp_context *context); int lencoder_decoder_get(lencoder coder); void lencoder_destroy(lencoder *coder); #ifdef __cplusplus } #endif #endif#include <lencoder.h>
#include <locale.h>
#define LENCODER_CFG_FILE “/usr/local/media_encoder.json”
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include “x86/soft_encoder.h”
#else
#include “arm/vimicro_encoder.h”
#include “arm/mpp_encoder.h”
#include <json-c/json.h>
#endif
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
struct lencoder_t {
encoder_soft s;
unsigned char *resive;
};
struct lencoder_context_t {
int first;
encoder_soft_context ctx;
};
#else
struct lencoder_context_t {
usb_list *usb;
int first;
int current_num;
int hasread;
svac_frame *frameout;
int frame_count;
USBDEC_VIDEO_FORMAT srcfmt;
};
struct lencoder_mpp_context_t {
mpp_encoder encoder;
int first;
int nobuffer;
mpp_encoder decoder;
};
struct lencoder_t {
encoder_usb vimirco;
int maxnum;
};
#endif
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
#else
int load_media_cfg(mpp_encoder coder)
{
int ret = LENCODER_STATUS_OK;
struct json_object *cfg = NULL, *watertext = NULL;
uint8_t text_water_enable, text_r, text_g, text_b;
uint8_t text_fontsize, text_x, text_y;
char text_context[128];
char text_font_file[64];
cfg = json_object_from_file(LENCODER_CFG_FILE); if(!cfg) { return LENCODER_LOAD_CFG_ERR; } if(json_object_get_type(cfg) != json_type_object) { json_object_put(cfg); return LENCODER_LOAD_CFG_VALUE_ERR; } watertext = json_object_object_get(cfg, "text"); if(json_object_get_type(watertext) != json_type_object) { json_object_put(watertext); json_object_put(cfg); return LENCODER_LOAD_CFG_VALUE_ERR; } json_object_object_foreach(watertext, key, val) { if(!key) { continue; } if(strcmp(key, "enable") == 0) { text_water_enable = (uint8_t)json_object_get_int(val); } if(strcmp(key, "color_r") == 0) { text_r = (uint8_t)json_object_get_int(val); } if(strcmp(key, "color_g") == 0) { text_g = (uint8_t)json_object_get_int(val); } if(strcmp(key, "color_b") == 0) { text_b = (uint8_t)json_object_get_int(val); } if(strcmp(key, "content") == 0) { snprintf(text_context, 128, "%s", json_object_get_string(val)); } if(strcmp(key, "position_x") == 0) { text_x = (uint8_t)json_object_get_int(val); } if(strcmp(key, "position_y") == 0) { text_y = (uint8_t)json_object_get_int(val); } if(strcmp(key, "font_size") == 0) { text_fontsize = (uint8_t)json_object_get_int(val); } if(strcmp(key, "font_file") == 0) { strcpy(text_font_file, json_object_get_string(val)); } } encoder_mpp_set_text_watertext(coder, text_context, text_r, text_g, text_b, text_fontsize, text_x, text_y, text_font_file, text_water_enable); json_object_put(cfg); return ret;}
#endif
int lencoder_init(lencoder *coder)
{
int ret = LENCODER_STATUS_OK, max_num = 0;
lencoder t;
setlocale(LC_CTYPE, ""); t = (lencoder)malloc(sizeof(struct lencoder_t)); if (!t) { ret = LENCODER_NO_SPACE_ERR; goto EndP; } memset(t, 0, sizeof(struct lencoder_t));#if defined(i386) || defined(_M_IX86) || defined(x86_64)
ret = soft_encoder_create(&t->s);
if(ret != SOFT_ENCODER_OK) {
ret = LENCODER_CREATE_ENCODER_ERR;
free(t);
goto EndP;
}
#else
ret = vimicro_encoder_create(&(t->vimirco), &max_num);
if (ret < 1) {
free(t);
ret = LENCODER_CREATE_ENCODER_ERR;
goto EndP;
}
t->maxnum = max_num;#endif
*coder = t; ret = LENCODER_STATUS_OK;EndP:
return ret;
}
int lencoder_maxnum_get(lencoder coder)
{
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
return 65535;
#else
return coder->maxnum;
#endif
};
int lencoder_mpp_open(lencoder coder, lencoder_mpp_context *context)
{
int ret = LENCODER_STATUS_OK;
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
#else
MPP_RET err;
lencoder_mpp_context ctx;
ctx = (lencoder_mpp_context)malloc(sizeof(struct lencoder_mpp_context_t)); if(!ctx) { ret = LENCODER_NO_SPACE_ERR; goto EndP; } memset(ctx, 0, sizeof(struct lencoder_mpp_context_t)); err = encoder_mpp_create(&(ctx->decoder), MPP_CTX_DEC, MPP_VIDEO_CodingAVC); if(err != MPP_OK) { ret = LENCODER_MPP_CREATE_ERR; free(ctx); goto EndP; } err = encoder_mpp_create(&(ctx->encoder), MPP_CTX_ENC, MPP_VIDEO_CodingHEVC); if(err != MPP_OK) { ret = LENCODER_MPP_CREATE_ERR; free(ctx); encoder_mpp_destroy(&(ctx->decoder)); goto EndP; } ret = load_media_cfg(ctx->encoder); if(ret != LENCODER_STATUS_OK) { goto EndP; } ctx->first = 1; *context = ctx; printf("context is %p, *context is %p\n", context, *context); ret = LENCODER_STATUS_OK;EndP:
#endif
return ret;
}
// src_fmt 1 h264 dst_fmt 2 h265
int lencoder_mpp_open_ext(lencoder coder, lencoder_mpp_context *context, int src_fmt, int dst_fmt)
{
int ret = LENCODER_STATUS_OK;
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
#else
MPP_RET err;
lencoder_mpp_context ctx;
MppCodingType decode_type, encode_type;
ctx = (lencoder_mpp_context)malloc(sizeof(struct lencoder_mpp_context_t)); if(!ctx) { ret = LENCODER_NO_SPACE_ERR; goto EndP; } memset(ctx, 0, sizeof(struct lencoder_mpp_context_t)); if(src_fmt == 1) { decode_type = MPP_VIDEO_CodingAVC; } else { decode_type = MPP_VIDEO_CodingHEVC; } if(dst_fmt == 1) { encode_type = MPP_VIDEO_CodingAVC; } else { encode_type = MPP_VIDEO_CodingHEVC; } err = encoder_mpp_create(&(ctx->decoder), MPP_CTX_DEC, decode_type); if(err != MPP_OK) { ret = LENCODER_MPP_CREATE_ERR; free(ctx); goto EndP; } err = encoder_mpp_create(&(ctx->encoder), MPP_CTX_ENC, encode_type); if(err != MPP_OK) { ret = LENCODER_MPP_CREATE_ERR; free(ctx); encoder_mpp_destroy(&(ctx->decoder)); goto EndP; } ret = load_media_cfg(ctx->encoder); if(ret != LENCODER_STATUS_OK) { goto EndP; } ctx->first = 1; *context = ctx; printf("context is %p, *context is %p\n", context, *context); ret = LENCODER_STATUS_OK;EndP:
#endif
return ret;
}
int lencoder_mpp_get_frame(lencoder_mpp_context context, unsigned char **out, int *out_len)
{
int ret = LENCODER_STATUS_OK;
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
#else
MPP_RET err = MPP_OK;
MppFrame frame = NULL; err = encoder_mpp_get_frame(context->decoder, &frame); if(frame == NULL) { ret = LENCODER_MPP_GET_NULL; goto EndP; } if(context->first) { err = encoder_mpp_cfg_setup(context->encoder, frame); if(err != MPP_OK) { ret = LENCODER_MPP_WRITE_ERR; encoder_mpp_destroy(&(context->encoder)); encoder_mpp_destroy(&(context->decoder)); goto EndP; } context->first = 0; } err = encoder_mpp_encode(context->encoder, frame, out, out_len); if(err != MPP_OK) { ret = LENCODER_MPP_WRITE_ERR; goto EndP; }EndP:
if(frame) {
mpp_frame_deinit(&frame);
}
#endif
return ret;
}
int lencoder_mpp_transcode(lencoder_mpp_context context, unsigned char *in, int in_len)
{
int ret = LENCODER_STATUS_OK;
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
#else
MPP_RET err;
err = encoder_mpp_write_data(context->decoder, in, in_len); if(err != MPP_OK) { if(err == MPP_ERR_BUFFER_FULL) { ret = LENCODER_MPP_WRITE_FULL; } else { ret = LENCODER_MPP_WRITE_ERR; } }#endif
return ret;
}
void lencoder_mpp_close(lencoder_mpp_context *context)
{
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
#else
encoder_mpp_destroy(&(*context)->decoder);
encoder_mpp_destroy(&(*context)->encoder);
free(*context);
#endif
}
//0 转码棒 1转码卡 2 CPU软解
int lencoder_decoder_get(lencoder coder)
{
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
return 2;
#else
return vimicro_encoder_device_type(coder->vimirco);
#endif
}
// fmt 3.SVAC2转H264
int lencoder_open_ext(lencoder coder, lencoder_context *context, int fmt, unsigned char *pbuf, int buflen, int bit_rate, int fps, int gop)
{
int ret = LENCODER_STATUS_OK, idle_num;
lencoder_context ctx;
ctx = (lencoder_context)malloc(sizeof(struct lencoder_context_t)); if(!ctx) { ret = LENCODER_NO_SPACE_ERR; goto EndP; } memset(ctx, 0, sizeof(struct lencoder_context_t));#if defined(i386) || defined(_M_IX86) || defined(x86_64)
switch(fmt) {
case 3:
ret = soft_encoder_open(&ctx->ctx, bit_rate, fps, gop, pbuf, buflen);
if(ret != SOFT_ENCODER_OK) {
printf(“open soft encoder failed, %02x\n”, ret);
ret = LENCODER_OPEN_FAILED_DEVICE;
goto EndP;
}
break;
default:
ret = LENCODER_UNKNOWN_ENCODER_TYPE;
}
#else
#endif
*context = ctx;
EndP:
if(ret != LENCODER_STATUS_OK) {
free(ctx);
}
return ret;
}
// fmt 1.H264转SVAC2 2.H265转SVAC2 3.SVAC2转H264
int lencoder_open(lencoder coder, lencoder_context *context, int fmt, unsigned char *pbuf, int buflen)
{
int ret = LENCODER_STATUS_OK, idle_num;
lencoder_context ctx;
ctx = (lencoder_context)malloc(sizeof(struct lencoder_context_t)); if(!ctx) { ret = LENCODER_NO_SPACE_ERR; goto EndP; } memset(ctx, 0, sizeof(struct lencoder_context_t));#if defined(i386) || defined(_M_IX86) || defined(x86_64)
switch(fmt) {
case 3:
ret = soft_encoder_open(&ctx->ctx, 512 * 1024, 25, 25, pbuf, buflen);
if(ret != SOFT_ENCODER_OK) {
printf(“open soft encoder failed, %02x\n”, ret);
ret = LENCODER_OPEN_FAILED_DEVICE;
goto EndP;
}
break;
default:
printf(“Unsuport fmt %d\n”, fmt);
ret = LENCODER_UNKNOWN_ENCODER_TYPE;
}
#else
vimicro_encoder_idle_get(coder->vimirco, &idle_num);
if (idle_num < 1) {
ret = LENCODER_NO_IDLE_ERR;
goto EndP;
}
ret = vimicro_encoder_prefetch(coder->vimirco, idle_num, pbuf, buflen); if (ret) { ret = LENCODER_PREFETCH_ENCODER_ERR; goto EndP; } switch(fmt) { case 1: //H264 ctx->usb = vimicro_encoder_open(coder->vimirco, idle_num, USBDEC_VIDEO_H264); ctx->srcfmt = USBDEC_VIDEO_H264; break; case 2: //H265 ctx->usb = vimicro_encoder_open(coder->vimirco, idle_num, USBDEC_VIDEO_H265); ctx->srcfmt = USBDEC_VIDEO_H265; break; default: printf("err video format:%d\n", fmt); ret = LENCODER_UNKNOWN_ENCODER_TYPE; goto EndP; } if(ctx->usb == NULL) { ret = LENCODER_OPEN_FAILED_DEVICE; goto EndP; } ctx->frameout = (svac_frame *)malloc(sizeof(svac_frame) * 1024); ctx->current_num = idle_num; ctx->hasread = -1;#endif
*context = ctx;
EndP:
if(ret != LENCODER_STATUS_OK) {
free(ctx);
*context = NULL;
}
return ret;
}
int lencoder_transcode(lencoder_context context, unsigned char *in, int in_len)
{
int ret = LENCODER_STATUS_OK, frame_type, i = 0;
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
ret = soft_encoder_decode_svac(context->ctx, in, in_len);
if(ret != SOFT_ENCODER_OK) {
printf(“soft_encoder_decode failed %02x\n”, ret);
ret = LENCODER_TRANSOCER_ERR;
goto EndP;
}
#else
unsigned char c;
c = (in[2] == 1 ? in[3] : in[4]); switch(context->srcfmt) { case USBDEC_VIDEO_H264: switch(c & 0x1f) { case NAL_SLICE: frame_type = 2; break; case NAL_SLICE_IDR: case NAL_SEI: case NAL_SPS: case NAL_PPS: frame_type = 1; break; default: ret = LENCODER_UNKNOWN_NALU_TYPE; goto EndP; } break; case USBDEC_VIDEO_H265: switch((c & 0x7e) >> 1) { case TRAIL_R: frame_type = 2; break; case BLA_W_LP: case BLA_W_RADL: case BLA_N_LP: case IDR_W_RADL: case IDR_N_LP: case VPS_NUT: case SPS_NUT: case PPS_NUT: case PREFIX_SEI_NUT: case SUFFIX_SEI_NUT: frame_type = 1; break; default: ret = LENCODER_UNKNOWN_NALU_TYPE; goto EndP; } break; default: break; } for(i = 0; i < context->frame_count; i ++) { free(context->frameout[i].buf); context->frameout[i].buf = NULL; } ret = vimicro_encoder_transcode(context->usb, context->current_num, in, in_len, frame_type, context->frameout, &(context->frame_count)); if(context->frame_count > 0) { context->hasread = 0; } else { context->hasread = -1; } if(ret == VMUSB_DEVICE_LOST) { ret = LENCODER_LOST_DEVICE; goto EndP; }#endif
ret = LENCODER_STATUS_OK;
EndP:
return ret;
}
void lencoder_close(lencoder coder, lencoder_context *context)
{
lencoder_context ctx = *context;
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
soft_encoder_close(ctx->ctx);
#else
vimicro_encoder_close(ctx->usb, ctx->current_num);
if(ctx->frameout) {
free(ctx->frameout);
}
#endif
if(ctx) free(ctx);
*context = NULL;
}
void lencoder_destroy(lencoder *coder)
{
lencoder tt = *coder;
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
soft_encoder_destroy(tt->s);
#else
vimicro_encoder_destroy(&(tt->vimirco));
tt->maxnum = 0;
#endif
free(tt);
*coder = NULL;}
int lencoder_get_frame_ext(lencoder_context context, unsigned char **frame, unsigned int *frame_len, int *frame_type, int *iskey, int64_t *pts, int64_t *dts)
{
int ret = LENCODER_STATUS_OK;
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
ret = soft_encoder_get_h264(context->ctx, frame, (size_t *)frame_len, iskey, pts, dts);
if(ret != SOFT_ENCODER_OK) {
//printf(“soft encoder get h264 failed %02x\n”, ret);
ret = LENCODER_CONTINUING;
}
else {
ret = LENCODER_STATUS_OK;
}
#else
#endif
return ret;
}
int lencoder_get_frame(lencoder_context context, unsigned char **frame, unsigned int *frame_len, int *frame_type)
{
int ret = LENCODER_STATUS_OK;
#if defined(i386) || defined(_M_IX86) || defined(x86_64)
int iskey;
int64_t pts, dts;
ret = soft_encoder_get_h264(context->ctx, frame, (size_t *)frame_len, &iskey, &pts, &dts);
if(ret != SOFT_ENCODER_OK) {
//printf(“soft encoder get h264 failed %02x\n”, ret);
ret = LENCODER_CONTINUING;
}
else {
ret = LENCODER_STATUS_OK;
}
#else
svac_frame *p;
if(context->hasread == -1) { return LENCODER_EMPTY_FRAME_TYPE; } //printf("has read before change is %d\n", context->hasread); p = &context->frameout[context->hasread]; *frame = (unsigned char *)malloc(p->buf_len); if(*frame == NULL) { return LENCODER_NO_SPACE_ERR; } //printf("frame is %p, p is %p, buflen is %d, count is %d, base is %p\n", *frame, p, p->buf_len, context->frame_count, context->frameout); memcpy(*frame, p->buf, p->buf_len); //printf("end-------------------\n"); *frame_len = p->buf_len; *frame_type = p->frame_type; if(context->hasread < (context->frame_count - 1)) { context->hasread ++; } else { context->hasread = -1; }#endif
return ret;
}