You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
943 lines
30 KiB
943 lines
30 KiB
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Copyright (C) 2022 Sophgo Technologies Inc. All rights reserved.
|
|
//
|
|
// SOPHON-DEMO is licensed under the 2-Clause BSD License except for the
|
|
// third-party components.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
/*
|
|
* This is a wrapper header of BMruntime & BMCV, aiming to simplify user's program.
|
|
*/
|
|
#include "ff_decode.hpp"
|
|
#include <unistd.h>
|
|
#include <iostream>
|
|
#include <thread>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <fstream>
|
|
#include <cstring>
|
|
|
|
|
|
using namespace std;
|
|
|
|
enum ff_decode_pic_types{
|
|
FF_DEC_PIC_JPEG=0,
|
|
FF_DEC_PIC_PNG,
|
|
FF_DEC_PIC_BMP
|
|
};
|
|
|
|
typedef struct ff_pic_signature{
|
|
uint8_t data[8];
|
|
int sig_len;
|
|
} signature;
|
|
|
|
const signature pic_signatures[] = {
|
|
{{0xff, 0xd8}, 2}, // jpg
|
|
{{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}, 8}, // png
|
|
{{0x42, 0x4d}, 2} // bmp
|
|
};
|
|
|
|
const int hw_jpeg_header_fmt_words[] = {
|
|
0x221111, // yuv420
|
|
0x211111, // yuv422
|
|
0x111111 // yuv444
|
|
};
|
|
|
|
int read_buffer(void* opaque, uint8_t* buf, int buf_size) {
|
|
bs_buffer_t* bs = (bs_buffer_t*)opaque;
|
|
|
|
int r = bs->size - bs->pos;
|
|
if (r <= 0) {
|
|
return AVERROR_EOF;
|
|
}
|
|
|
|
uint8_t* p = bs->start + bs->pos;
|
|
int len = (r >= buf_size) ? buf_size : r;
|
|
memcpy(buf, p, len);
|
|
|
|
bs->pos += len;
|
|
|
|
return len;
|
|
}
|
|
|
|
int check_signature(uint8_t* data)
|
|
{
|
|
int ret = -1;
|
|
int sigs_num = sizeof(pic_signatures)/sizeof(signature);
|
|
for(int i=0; i<sigs_num; i++)
|
|
{
|
|
bool is_current_sig = true;
|
|
int sig_len = pic_signatures[i].sig_len;
|
|
for(int j=0; j<sig_len; j++)
|
|
{
|
|
if(data[j]!=pic_signatures[i].data[j])
|
|
{
|
|
is_current_sig = false;
|
|
break;
|
|
}
|
|
}
|
|
if(is_current_sig)
|
|
{
|
|
ret = i;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool determine_hardware_decode(uint8_t* buffer) {
|
|
int ret = 0;
|
|
int offset = 2;
|
|
int SOF0_Marker = 0xFFC0;
|
|
bool is_hw = false;
|
|
while (1) {
|
|
uint8_t flag_high = *(uint8_t*)(buffer + offset);
|
|
uint8_t flag_low = *(uint8_t*)(buffer + offset + 1);
|
|
|
|
int word = (flag_high << 8) + flag_low;
|
|
|
|
if (SOF0_Marker == word) {
|
|
// gray
|
|
if (1 == (*(buffer + offset + 9) & 255)) {
|
|
return true;
|
|
}
|
|
// color
|
|
offset += 11;
|
|
int ret1 = *(buffer + offset) & 255;
|
|
offset += 3;
|
|
int ret2 = *(buffer + offset) & 255;
|
|
offset += 3;
|
|
int ret3 = *(buffer + offset) & 255;
|
|
|
|
ret = (ret1 << 16) + (ret2 << 8) + ret3;
|
|
break;
|
|
}
|
|
|
|
else {
|
|
offset += 2;
|
|
uint8_t offset_high = *(uint8_t*)(buffer + offset);
|
|
uint8_t offset_low = *(uint8_t*)(buffer + offset + 1);
|
|
offset += (offset_high << 8) + offset_low;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
is_hw = (hw_jpeg_header_fmt_words[i] == ret) ? true : false;
|
|
if (is_hw)
|
|
break;
|
|
}
|
|
return is_hw;
|
|
}
|
|
|
|
VideoDecFFM::VideoDecFFM() {
|
|
ifmt_ctx = NULL;
|
|
video_dec_ctx = NULL;
|
|
video_dec_par = NULL;
|
|
decoder = NULL;
|
|
|
|
is_rtsp = 0;
|
|
width = 0;
|
|
height = 0;
|
|
pix_fmt = 0;
|
|
|
|
video_stream_idx = -1;
|
|
refcount = 1;
|
|
|
|
av_init_packet(&pkt);
|
|
pkt.data = NULL;
|
|
pkt.size = 0;
|
|
|
|
frame = av_frame_alloc();
|
|
}
|
|
|
|
VideoDecFFM::~VideoDecFFM() {
|
|
closeDec();
|
|
printf("#VideoDecFFM exit \n");
|
|
}
|
|
|
|
bool string_start_with(const string& s, const string& prefix) {
|
|
return (s.compare(0, prefix.size(), prefix) == 0);
|
|
}
|
|
|
|
int map_avformat_to_bmformat(int avformat) {
|
|
int format;
|
|
switch (avformat) {
|
|
case AV_PIX_FMT_RGB24:
|
|
format = FORMAT_RGB_PACKED;
|
|
break;
|
|
case AV_PIX_FMT_BGR24:
|
|
format = FORMAT_BGR_PACKED;
|
|
break;
|
|
case AV_PIX_FMT_YUV420P:
|
|
case AV_PIX_FMT_YUVJ420P:
|
|
format = FORMAT_YUV420P;
|
|
break;
|
|
case AV_PIX_FMT_YUV422P:
|
|
case AV_PIX_FMT_YUVJ422P:
|
|
format = FORMAT_YUV422P;
|
|
break;
|
|
case AV_PIX_FMT_YUV444P:
|
|
case AV_PIX_FMT_YUVJ444P:
|
|
format = FORMAT_YUV444P;
|
|
break;
|
|
case AV_PIX_FMT_NV12:
|
|
format = FORMAT_NV12;
|
|
break;
|
|
case AV_PIX_FMT_NV16:
|
|
format = FORMAT_NV16;
|
|
break;
|
|
case AV_PIX_FMT_GRAY8:
|
|
format = FORMAT_GRAY;
|
|
break;
|
|
case AV_PIX_FMT_GBRP:
|
|
format = FORMAT_RGBP_SEPARATE;
|
|
break;
|
|
default:
|
|
printf("unsupported av_pix_format %d\n", avformat);
|
|
return -1;
|
|
}
|
|
|
|
return format;
|
|
}
|
|
|
|
bm_status_t avframe_to_bm_image(bm_handle_t& handle, AVFrame* in, bm_image* out, bool is_jpeg, bool data_on_device_mem, int coded_width, int coded_height) {
|
|
int plane = 0;
|
|
int data_four_denominator = -1;
|
|
int data_five_denominator = -1;
|
|
int data_six_denominator = -1;
|
|
static int mem_flags = USEING_MEM_HEAP1;
|
|
|
|
switch (in->format) {
|
|
case AV_PIX_FMT_RGB24:
|
|
case AV_PIX_FMT_BGR24:
|
|
plane = 1;
|
|
data_four_denominator = 1;
|
|
data_five_denominator = -1;
|
|
data_six_denominator = -1;
|
|
break;
|
|
case AV_PIX_FMT_GRAY8:
|
|
plane = 1;
|
|
data_four_denominator = -1;
|
|
data_five_denominator = -1;
|
|
data_six_denominator = -1;
|
|
break;
|
|
case AV_PIX_FMT_YUV420P:
|
|
case AV_PIX_FMT_YUVJ420P:
|
|
plane = 3;
|
|
data_four_denominator = -1;
|
|
data_five_denominator = 2;
|
|
data_six_denominator = 2;
|
|
break;
|
|
case AV_PIX_FMT_NV12:
|
|
plane = 2;
|
|
data_four_denominator = -1;
|
|
data_five_denominator = 1;
|
|
data_six_denominator = -1;
|
|
break;
|
|
case AV_PIX_FMT_YUV422P:
|
|
case AV_PIX_FMT_YUVJ422P:
|
|
plane = 3;
|
|
data_four_denominator = -1;
|
|
data_five_denominator = 1;
|
|
data_six_denominator = 1;
|
|
break;
|
|
// case AV_PIX_FMT_YUV440P:
|
|
// case AV_PIX_FMT_YUVJ440P:
|
|
// plane = 3;
|
|
// data_four_denominator = -1;
|
|
// data_five_denominator = 1;
|
|
// data_six_denominator = 4;
|
|
// break;
|
|
case AV_PIX_FMT_NV16:
|
|
plane = 2;
|
|
data_four_denominator = -1;
|
|
data_five_denominator = 2;
|
|
data_six_denominator = -1;
|
|
break;
|
|
case AV_PIX_FMT_YUV444P:
|
|
case AV_PIX_FMT_YUVJ444P:
|
|
case AV_PIX_FMT_GBRP:
|
|
plane = 3;
|
|
data_four_denominator = -1;
|
|
data_five_denominator = 1;
|
|
data_six_denominator = 1;
|
|
break;
|
|
default:
|
|
printf("unsupported format, only yuv420,yuvj420,yuv422,yuvj422,yuv444,yuvj444,nv12,nv16,gray,rgb_packed,bgr_packed supported\n");
|
|
break;
|
|
}
|
|
|
|
if (in->channel_layout == 101) { /* COMPRESSED NV12 FORMAT */
|
|
if ((0 == in->height) || (0 == in->width) || (0 == in->linesize[4]) || (0 == in->linesize[5]) ||
|
|
(0 == in->linesize[6]) || (0 == in->linesize[7]) || (0 == in->data[4]) || (0 == in->data[5]) ||
|
|
(0 == in->data[6]) || (0 == in->data[7])) {
|
|
printf("bm_image_from_frame: get yuv failed!!");
|
|
return BM_ERR_PARAM;
|
|
}
|
|
bm_image cmp_bmimg;
|
|
bm_image_create(handle, coded_height, coded_width, FORMAT_COMPRESSED, DATA_TYPE_EXT_1N_BYTE, &cmp_bmimg);
|
|
|
|
bm_device_mem_t input_addr[4];
|
|
int size = in->height * in->linesize[4];
|
|
input_addr[0] = bm_mem_from_device((unsigned long long)in->data[6], size);
|
|
size = (in->height / 2) * in->linesize[5];
|
|
input_addr[1] = bm_mem_from_device((unsigned long long)in->data[4], size);
|
|
size = in->linesize[6];
|
|
input_addr[2] = bm_mem_from_device((unsigned long long)in->data[7], size);
|
|
size = in->linesize[7];
|
|
input_addr[3] = bm_mem_from_device((unsigned long long)in->data[5], size);
|
|
bm_image_attach(cmp_bmimg, input_addr);
|
|
bm_image_create(handle, in->height, in->width, FORMAT_YUV420P, DATA_TYPE_EXT_1N_BYTE, out);
|
|
if (mem_flags == USEING_MEM_HEAP1 && bm_image_alloc_dev_mem_heap_mask(*out, mem_flags) != BM_SUCCESS) {
|
|
mem_flags = USEING_MEM_HEAP2;
|
|
}
|
|
if (mem_flags == USEING_MEM_HEAP2 && bm_image_alloc_dev_mem_heap_mask(*out, mem_flags) != BM_SUCCESS) {
|
|
printf("bmcv allocate mem failed!!!");
|
|
}
|
|
bmcv_rect_t crop_rect = {0, 0, in->width, in->height};
|
|
bmcv_image_vpp_convert(handle, 1, cmp_bmimg, out, &crop_rect);
|
|
bm_image_destroy(cmp_bmimg);
|
|
} else {
|
|
int stride[3];
|
|
bm_image_format_ext bm_format;
|
|
bm_device_mem_t input_addr[3] = {0};
|
|
|
|
data_on_device_mem ? stride[0] = in->linesize[4] : stride[0] = in->linesize[0];
|
|
|
|
if (plane > 1) {
|
|
data_on_device_mem ? stride[1] = in->linesize[5] : stride[1] = in->linesize[1];
|
|
}
|
|
if (plane > 2) {
|
|
data_on_device_mem ? stride[2] = in->linesize[6] : stride[2] = in->linesize[2];
|
|
}
|
|
bm_image tmp;
|
|
bm_format = (bm_image_format_ext)map_avformat_to_bmformat(in->format);
|
|
bm_image_create(handle, in->height, in->width, bm_format, DATA_TYPE_EXT_1N_BYTE, &tmp, stride);
|
|
bm_image_create(handle, in->height, in->width, FORMAT_BGR_PACKED, DATA_TYPE_EXT_1N_BYTE, out);
|
|
if (mem_flags == USEING_MEM_HEAP1 && bm_image_alloc_dev_mem_heap_mask(*out, mem_flags) != BM_SUCCESS) {
|
|
mem_flags = USEING_MEM_HEAP2;
|
|
}
|
|
if (mem_flags == USEING_MEM_HEAP2 && bm_image_alloc_dev_mem_heap_mask(*out, mem_flags) != BM_SUCCESS) {
|
|
printf("bmcv allocate mem failed!!!");
|
|
}
|
|
|
|
int size = in->height * stride[0];
|
|
if (data_four_denominator != -1) {
|
|
size = in->height * stride[0] * 3;
|
|
}
|
|
if (data_on_device_mem) {
|
|
input_addr[0] = bm_mem_from_device((unsigned long long)in->data[4], size);
|
|
} else {
|
|
bm_malloc_device_byte(handle, &input_addr[0], size);
|
|
bm_memcpy_s2d_partial(handle, input_addr[0], in->data[0], size);
|
|
}
|
|
|
|
if (data_five_denominator != -1) {
|
|
size = FF_ALIGN(in->height,2) * stride[1] / data_five_denominator;
|
|
if (data_on_device_mem) {
|
|
input_addr[1] = bm_mem_from_device((unsigned long long)in->data[5], size);
|
|
} else {
|
|
bm_malloc_device_byte(handle, &input_addr[1], size);
|
|
bm_memcpy_s2d_partial(handle, input_addr[1], in->data[1], size);
|
|
}
|
|
}
|
|
|
|
if (data_six_denominator != -1) {
|
|
size = FF_ALIGN(in->height,2) * stride[2] / data_six_denominator;
|
|
if (data_on_device_mem) {
|
|
input_addr[2] = bm_mem_from_device((unsigned long long)in->data[6], size);
|
|
} else {
|
|
bm_malloc_device_byte(handle, &input_addr[2], size);
|
|
bm_memcpy_s2d_partial(handle, input_addr[2], in->data[2], size);
|
|
}
|
|
}
|
|
|
|
bm_image_attach(tmp, input_addr);
|
|
if (is_jpeg) {
|
|
csc_type_t csc_type = CSC_YPbPr2RGB_BT601;
|
|
bmcv_image_vpp_csc_matrix_convert(handle, 1, tmp, out, csc_type, NULL, BMCV_INTER_NEAREST, NULL);
|
|
} else {
|
|
bmcv_rect_t crop_rect = {0, 0, in->width, in->height};
|
|
bmcv_image_vpp_convert(handle, 1, tmp, out, &crop_rect);
|
|
}
|
|
bm_image_destroy(tmp);
|
|
|
|
if (!data_on_device_mem) {
|
|
bm_free_device(handle, input_addr[0]);
|
|
if (data_five_denominator != -1)
|
|
bm_free_device(handle, input_addr[1]);
|
|
if (data_six_denominator != -1)
|
|
bm_free_device(handle, input_addr[2]);
|
|
}
|
|
}
|
|
return BM_SUCCESS;
|
|
}
|
|
|
|
int VideoDecFFM::openDec(bm_handle_t* dec_handle, const char* input) {
|
|
if (strstr(input, "rtsp://"))
|
|
this->is_rtsp = 1;
|
|
this->handle = dec_handle;
|
|
int ret = 0;
|
|
AVDictionary* dict = NULL;
|
|
av_dict_set(&dict, "rtsp_flags", "prefer_tcp", 0);
|
|
ret = avformat_open_input(&ifmt_ctx, input, NULL, &dict);
|
|
if (ret < 0) {
|
|
av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = avformat_find_stream_info(ifmt_ctx, NULL);
|
|
if (ret < 0) {
|
|
av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = openCodecContext(&video_stream_idx, &video_dec_ctx, ifmt_ctx, AVMEDIA_TYPE_VIDEO, bm_get_devid(*dec_handle));
|
|
|
|
if (ret >= 0) {
|
|
width = video_dec_ctx->width;
|
|
height = video_dec_ctx->height;
|
|
pix_fmt = video_dec_ctx->pix_fmt;
|
|
}
|
|
av_log(video_dec_ctx, AV_LOG_INFO, "openDec video_stream_idx = %d, pix_fmt = %d\n", video_stream_idx, pix_fmt);
|
|
|
|
thread push(&VideoDecFFM::vidPushImage, this);
|
|
push.detach();
|
|
|
|
av_dict_free(&dict);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void VideoDecFFM::closeDec() {
|
|
if (video_dec_ctx) {
|
|
avcodec_free_context(&video_dec_ctx);
|
|
video_dec_ctx = NULL;
|
|
}
|
|
if (ifmt_ctx) {
|
|
avformat_close_input(&ifmt_ctx);
|
|
ifmt_ctx = NULL;
|
|
}
|
|
if (frame) {
|
|
av_frame_free(&frame);
|
|
frame = NULL;
|
|
}
|
|
}
|
|
|
|
int VideoDecFFM::openCodecContext(int* stream_idx,
|
|
AVCodecContext** dec_ctx,
|
|
AVFormatContext* fmt_ctx,
|
|
enum AVMediaType type,
|
|
int sophon_idx) {
|
|
int ret, stream_index;
|
|
AVStream* st;
|
|
AVCodec* dec = NULL;
|
|
AVDictionary* opts = NULL;
|
|
|
|
ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
|
|
if (ret < 0) {
|
|
av_log(NULL, AV_LOG_ERROR, "Could not find %s stream \n", av_get_media_type_string(type));
|
|
return ret;
|
|
}
|
|
|
|
stream_index = ret;
|
|
st = fmt_ctx->streams[stream_index];
|
|
|
|
if (st->codecpar->codec_id != AV_CODEC_ID_H264 && st->codecpar->codec_id != AV_CODEC_ID_HEVC) {
|
|
this->data_on_device_mem = false;
|
|
}
|
|
|
|
/* find decoder for the stream */
|
|
decoder = const_cast<AVCodec*>(avcodec_find_decoder(st->codecpar->codec_id));
|
|
|
|
if (!decoder) {
|
|
av_log(NULL, AV_LOG_FATAL, "Failed to find %s codec\n", av_get_media_type_string(type));
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
/* Allocate a codec context for the decoder */
|
|
*dec_ctx = avcodec_alloc_context3(decoder);
|
|
if (!*dec_ctx) {
|
|
av_log(NULL, AV_LOG_FATAL, "Failed to allocate the %s codec context\n", av_get_media_type_string(type));
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
|
|
/* Copy codec parameters from input stream to output codec context */
|
|
ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar);
|
|
if (ret < 0) {
|
|
av_log(NULL, AV_LOG_FATAL, "Failed to copy %s codec parameters to decoder context\n",
|
|
av_get_media_type_string(type));
|
|
return ret;
|
|
}
|
|
|
|
video_dec_par = st->codecpar;
|
|
/* Init the decoders, with or without reference counting */
|
|
av_dict_set(&opts, "refcounted_frames", refcount ? "1" : "0", 0);
|
|
av_dict_set_int(&opts, "sophon_idx", sophon_idx, 0);
|
|
av_dict_set_int(&opts, "extra_frame_buffer_num", EXTRA_FRAME_BUFFER_NUM, 0); // if we use dma_buffer mode
|
|
av_dict_set_int(&opts, "output_format", output_format, 18);
|
|
ret = avcodec_open2(*dec_ctx, dec, &opts);
|
|
if (ret < 0) {
|
|
av_log(NULL, AV_LOG_FATAL, "Failed to open %s codec\n", av_get_media_type_string(type));
|
|
return ret;
|
|
}
|
|
*stream_idx = stream_index;
|
|
|
|
av_dict_free(&opts);
|
|
|
|
return 0;
|
|
}
|
|
|
|
AVFrame* VideoDecFFM::grabFrame() {
|
|
int ret = 0;
|
|
int got_frame = 0;
|
|
struct timeval tv1, tv2;
|
|
gettimeofday(&tv1, NULL);
|
|
|
|
while (1) {
|
|
av_packet_unref(&pkt);
|
|
ret = av_read_frame(ifmt_ctx, &pkt);
|
|
if (ret < 0) {
|
|
if (ret == AVERROR(EAGAIN)) {
|
|
gettimeofday(&tv2, NULL);
|
|
if (((tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000) > 1000 * 60) {
|
|
av_log(video_dec_ctx, AV_LOG_WARNING, "av_read_frame failed ret(%d) retry time >60s.\n", ret);
|
|
break;
|
|
}
|
|
usleep(10 * 1000);
|
|
continue;
|
|
}
|
|
AVFrame* flush_frame = flushDecoder();
|
|
if(flush_frame)
|
|
return flush_frame;
|
|
av_log(video_dec_ctx, AV_LOG_ERROR, "av_read_frame ret(%d) maybe eof...\n", ret);
|
|
quit_flag = true;
|
|
return NULL;
|
|
}
|
|
|
|
if (pkt.stream_index != video_stream_idx) {
|
|
continue;
|
|
}
|
|
|
|
if (!frame) {
|
|
av_log(video_dec_ctx, AV_LOG_ERROR, "Could not allocate frame\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (refcount)
|
|
av_frame_unref(frame);
|
|
gettimeofday(&tv1, NULL);
|
|
ret = avcodec_decode_video2(video_dec_ctx, frame, &got_frame, &pkt);
|
|
if (ret < 0) {
|
|
av_log(video_dec_ctx, AV_LOG_ERROR, "Error decoding video frame (%d)\n", ret);
|
|
continue;
|
|
}
|
|
|
|
if (!got_frame) {
|
|
continue;
|
|
}
|
|
|
|
width = video_dec_ctx->width;
|
|
height = video_dec_ctx->height;
|
|
pix_fmt = video_dec_ctx->pix_fmt;
|
|
if (frame->width != width || frame->height != height || frame->format != pix_fmt) {
|
|
av_log(video_dec_ctx, AV_LOG_ERROR,
|
|
"Error: Width, height and pixel format have to be "
|
|
"constant in a rawvideo file, but the width, height or "
|
|
"pixel format of the input video changed:\n"
|
|
"old: width = %d, height = %d, format = %s\n"
|
|
"new: width = %d, height = %d, format = %s\n",
|
|
width, height, av_get_pix_fmt_name((AVPixelFormat)pix_fmt), frame->width, frame->height,
|
|
av_get_pix_fmt_name((AVPixelFormat)frame->format));
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
return frame;
|
|
}
|
|
|
|
AVFrame* VideoDecFFM::flushDecoder()
|
|
{
|
|
av_frame_unref(frame);
|
|
int ret = avcodec_send_packet(video_dec_ctx, NULL);
|
|
ret = avcodec_receive_frame(video_dec_ctx, frame);
|
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF || ret < 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
return frame;
|
|
}
|
|
|
|
void* VideoDecFFM::vidPushImage() {
|
|
while (1) {
|
|
while (queue.size() == QUEUE_MAX_SIZE) {
|
|
if (is_rtsp) {
|
|
std::lock_guard<std::mutex> my_lock_guard(lock);
|
|
bm_image* img = queue.front();
|
|
bm_image_destroy(*img);
|
|
queue.pop();
|
|
} else {
|
|
usleep(2000);
|
|
}
|
|
}
|
|
|
|
bm_image* img = new bm_image;
|
|
AVFrame* avframe = grabFrame();
|
|
if (quit_flag)
|
|
break;
|
|
coded_width = video_dec_ctx->coded_width;
|
|
coded_height = video_dec_ctx->coded_height;
|
|
avframe_to_bm_image(*(this->handle), avframe, img, false, this->data_on_device_mem,
|
|
coded_width, coded_height);
|
|
|
|
std::lock_guard<std::mutex> my_lock_guard(lock);
|
|
queue.push(img);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bm_image* VideoDecFFM::grab() {
|
|
while (queue.empty()) {
|
|
if (quit_flag)
|
|
return nullptr;
|
|
usleep(500);
|
|
}
|
|
bm_image* bm_img;
|
|
{
|
|
std::lock_guard<std::mutex> my_lock_guard(lock);
|
|
bm_img = queue.front();
|
|
queue.pop();
|
|
}
|
|
return bm_img;
|
|
}
|
|
|
|
bm_status_t picDec(bm_handle_t& handle, const char* path, bm_image& img) {
|
|
bm_status_t ret = BM_ERR_FAILURE;
|
|
|
|
FILE* infile = fopen(path, "rb+");
|
|
if (infile == nullptr) {
|
|
av_log(NULL, AV_LOG_ERROR, "Open %s failed, please check if you have enough rights.\n", path);
|
|
return BM_ERR_FAILURE;
|
|
}
|
|
fseek(infile, 0, SEEK_END);
|
|
int numBytes = ftell(infile);
|
|
fseek(infile, 0, SEEK_SET);
|
|
uint8_t* buffer = (uint8_t*)av_malloc(numBytes);
|
|
fread(buffer, sizeof(uint8_t), numBytes, infile);
|
|
fclose(infile);
|
|
|
|
int type = check_signature(buffer);
|
|
if(type<0)
|
|
{
|
|
av_free(buffer);
|
|
av_log(NULL, AV_LOG_ERROR, "ff_decode only support jpg, png, bmp. unknown pic signature.\n");
|
|
return BM_NOT_SUPPORTED;
|
|
}
|
|
|
|
if(FF_DEC_PIC_JPEG==type)
|
|
ret = jpgDec(handle, buffer, numBytes, img);
|
|
else
|
|
ret = miscDec(handle, buffer, numBytes, type, img);
|
|
av_free(buffer);
|
|
return ret;
|
|
}
|
|
|
|
bm_status_t miscDec(bm_handle_t& handle, uint8_t* bs_buffer, int numBytes, int type, bm_image& img) {
|
|
bm_status_t ret;
|
|
const AVCodec* codec;
|
|
AVCodecContext* dec_ctx = NULL;
|
|
AVPacket* pkt;
|
|
AVFrame* frame;
|
|
|
|
AVCodecID codec_id = AV_CODEC_ID_NONE;
|
|
switch(type){
|
|
case FF_DEC_PIC_PNG:
|
|
codec_id = AV_CODEC_ID_PNG;
|
|
break;
|
|
case FF_DEC_PIC_BMP:
|
|
codec_id = AV_CODEC_ID_BMP;
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
|
|
pkt = av_packet_alloc();
|
|
if (!pkt) {
|
|
av_log(NULL, AV_LOG_ERROR, "could not alloc av packet.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
codec = avcodec_find_decoder(codec_id);
|
|
if (!codec) {
|
|
av_log(NULL, AV_LOG_ERROR, "Codec not found.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
dec_ctx = avcodec_alloc_context3(codec);
|
|
if (!dec_ctx) {
|
|
av_log(NULL, AV_LOG_ERROR, "Could not allocate video codec context.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
if (avcodec_open2(dec_ctx, codec, NULL) < 0) {
|
|
av_log(NULL, AV_LOG_ERROR, "Could not open codec.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
frame = av_frame_alloc();
|
|
if(!frame){
|
|
av_log(NULL, AV_LOG_ERROR, "Could not allocate video frame.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
pkt->size = numBytes;
|
|
pkt->data = (unsigned char*)bs_buffer;
|
|
if (pkt->size) {
|
|
int ret;
|
|
ret = avcodec_send_packet(dec_ctx, pkt);
|
|
|
|
if (ret < 0) {
|
|
av_log(NULL, AV_LOG_ERROR, "Error sending a packet for decoding.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
ret = avcodec_receive_frame(dec_ctx, frame);
|
|
|
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
|
av_log(NULL, AV_LOG_ERROR, "Error could not receive frame.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
} else if (ret < 0) {
|
|
av_log(NULL, AV_LOG_ERROR, "Error during decoding.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
avframe_to_bm_image(handle, frame, &img, false, false);
|
|
avcodec_free_context(&dec_ctx);
|
|
av_frame_free(&frame);
|
|
av_packet_free(&pkt);
|
|
return BM_SUCCESS;
|
|
} else {
|
|
av_log(NULL, AV_LOG_ERROR, "Error decode png, can not read file size.\n");
|
|
}
|
|
|
|
Func_Exit:
|
|
if(dec_ctx)
|
|
avcodec_free_context(&dec_ctx);
|
|
if(frame)
|
|
av_frame_free(&frame);
|
|
if(pkt)
|
|
av_packet_free(&pkt);
|
|
return ret;
|
|
}
|
|
|
|
bm_status_t jpgDec(bm_handle_t& handle, uint8_t* bs_buffer, int numBytes, bm_image& img) {
|
|
AVInputFormat* iformat = nullptr;
|
|
AVFormatContext* pFormatCtx = nullptr;
|
|
AVCodecContext* dec_ctx = nullptr;
|
|
AVCodec* pCodec = nullptr;
|
|
AVDictionary* dict = nullptr;
|
|
AVIOContext* avio_ctx = nullptr;
|
|
AVFrame* pFrame = nullptr;
|
|
AVFrame* I420Frame = nullptr;
|
|
AVPacket pkt;
|
|
|
|
int got_picture;
|
|
uint8_t* aviobuffer = nullptr;
|
|
int aviobuf_size = 32 * 1024; // 32K
|
|
int bs_size;
|
|
bs_buffer_t bs_obj = {0, 0, 0};
|
|
int tmp = 0;
|
|
bm_status_t ret;
|
|
bool data_on_device_mem = true;
|
|
|
|
bool hardware_decode = determine_hardware_decode(bs_buffer);
|
|
|
|
aviobuffer = (uint8_t*)av_malloc(aviobuf_size); // 32k
|
|
if (aviobuffer == nullptr) {
|
|
av_log(NULL, AV_LOG_ERROR, "av malloc for avio failed.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
bs_obj.start = bs_buffer;
|
|
bs_obj.size = numBytes;
|
|
bs_obj.pos = 0;
|
|
avio_ctx = avio_alloc_context(aviobuffer, aviobuf_size, 0, (void*)(&bs_obj), read_buffer, NULL, NULL);
|
|
if (avio_ctx == NULL) {
|
|
av_log(NULL, AV_LOG_ERROR, "avio_alloc_context failed.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
pFormatCtx = avformat_alloc_context();
|
|
pFormatCtx->pb = avio_ctx;
|
|
|
|
/* mjpeg demuxer */
|
|
iformat = const_cast<AVInputFormat*>(av_find_input_format("mjpeg"));
|
|
if (iformat == NULL) {
|
|
av_log(NULL, AV_LOG_ERROR, "av_find_input_format failed.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
/* Open an input stream */
|
|
tmp = avformat_open_input(&pFormatCtx, NULL, iformat, NULL);
|
|
if (tmp != 0) {
|
|
av_log(NULL, AV_LOG_ERROR, "Couldn't open input stream.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
/* HW JPEG decoder: jpeg_bm */
|
|
pCodec = hardware_decode ? const_cast<AVCodec*>(avcodec_find_decoder_by_name("jpeg_bm"))
|
|
: const_cast<AVCodec*>(avcodec_find_decoder_by_name("mjpeg"));
|
|
if (pCodec == NULL) {
|
|
av_log(NULL, AV_LOG_ERROR, "Codec not found.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
dec_ctx = avcodec_alloc_context3(pCodec);
|
|
if (dec_ctx == NULL) {
|
|
av_log(NULL, AV_LOG_ERROR, "Could not allocate video codec context.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
av_dict_set_int(&dict, "chroma_interleave", 0, 0);
|
|
#define BS_MASK (1024 * 16 - 1)
|
|
bs_size = (numBytes + BS_MASK) & (~BS_MASK);
|
|
#undef BS_MASK
|
|
#define JPU_PAGE_UNIT_SIZE 256
|
|
/* Avoid the false alarm that bs buffer is empty (SA3SW-252) */
|
|
if (bs_size - numBytes < JPU_PAGE_UNIT_SIZE)
|
|
bs_size += 16 * 1024;
|
|
#undef JPU_PAGE_UNIT_SIZE
|
|
bs_size /= 1024;
|
|
av_dict_set_int(&dict, "bs_buffer_size", bs_size, 0);
|
|
/* Extra frame buffers: "0" for still jpeg, at least "2" for mjpeg */
|
|
av_dict_set_int(&dict, "num_extra_framebuffers", 0, 0);
|
|
av_dict_set_int(&dict, "zero_copy", 0, 0);
|
|
av_dict_set_int(&dict, "sophon_idx", bm_get_devid(handle), 0);
|
|
tmp = avcodec_open2(dec_ctx, pCodec, &dict);
|
|
if (tmp < 0) {
|
|
av_log(NULL, AV_LOG_ERROR, "Could not open codec.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
pFrame = av_frame_alloc();
|
|
if (pFrame == nullptr) {
|
|
av_log(NULL, AV_LOG_ERROR, "av frame malloc failed.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
av_read_frame(pFormatCtx, &pkt);
|
|
tmp = avcodec_decode_video2(dec_ctx, pFrame, &got_picture, &pkt);
|
|
if (tmp < 0) {
|
|
av_log(NULL, AV_LOG_ERROR, "avcodec_decode_video2 decode error.\n");
|
|
ret = BM_ERR_FAILURE;
|
|
goto Func_Exit;
|
|
}
|
|
|
|
// filter convert format to I420
|
|
// TODO
|
|
if (!hardware_decode) {
|
|
int height = pFrame->height;
|
|
int width = pFrame->width;
|
|
uchar bgr_buffer[height * width * 3];
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
int indexY = y * pFrame->linesize[0] + x;
|
|
int indexU = (y / 2) * pFrame->linesize[1] + x;
|
|
int indexV = (y / 2) * pFrame->linesize[2] + x;
|
|
|
|
uchar Y = pFrame->data[0][indexY];
|
|
uchar U = pFrame->data[1][indexU];
|
|
uchar V = pFrame->data[2][indexV];
|
|
|
|
double M[12] = {1.0000000000000000, 0.0000000000000000, 1.4018863751529200, -179.4414560195740000,
|
|
1.0000000000000000, -0.3458066722146720, -0.7149028511111540, 135.7708189857060000,
|
|
1.0000000000000000, 1.7709825540494100, 0.0000000000000000, -226.6857669183240000};
|
|
|
|
int R = M[0] * Y + M[1] * (U) + M[2] * (V) + M[3];
|
|
int G = M[4] * Y + M[5] * (U) + M[6] * (V) + M[7];
|
|
int B = M[8] * Y + M[9] * (U) + M[10] * (V) + M[11];
|
|
|
|
R = (R < 0) ? 0 : R;
|
|
G = (G < 0) ? 0 : G;
|
|
B = (B < 0) ? 0 : B;
|
|
R = (R > 255) ? 255 : R;
|
|
G = (G > 255) ? 255 : G;
|
|
B = (B > 255) ? 255 : B;
|
|
|
|
bgr_buffer[(y * width + x) * 3 + 0] = uchar(B);
|
|
bgr_buffer[(y * width + x) * 3 + 1] = uchar(G);
|
|
bgr_buffer[(y * width + x) * 3 + 2] = uchar(R);
|
|
}
|
|
}
|
|
|
|
bm_image_create(handle, height, width, FORMAT_BGR_PACKED, DATA_TYPE_EXT_1N_BYTE, &img);
|
|
void* buffers[1] = {bgr_buffer};
|
|
bm_image_copy_host_to_device(img, buffers);
|
|
goto Func_Exit;
|
|
}
|
|
// vpp_convert do not support YUV422P, use libyuv to filter
|
|
if (AV_PIX_FMT_YUVJ422P == pFrame->format) {
|
|
I420Frame = av_frame_alloc();
|
|
|
|
I420Frame->width = pFrame->width;
|
|
I420Frame->height = pFrame->height;
|
|
I420Frame->format = AV_PIX_FMT_YUV420P;
|
|
|
|
I420Frame->linesize[0] = pFrame->linesize[0];
|
|
I420Frame->linesize[1] = pFrame->linesize[1];
|
|
I420Frame->linesize[2] = pFrame->linesize[2];
|
|
|
|
I420Frame->data[0] = (uint8_t*)malloc(pFrame->linesize[0] * I420Frame->height * 2);
|
|
I420Frame->data[1] = (uint8_t*)malloc(pFrame->linesize[1] * FF_ALIGN(I420Frame->height,2) / 2);
|
|
I420Frame->data[2] = (uint8_t*)malloc(pFrame->linesize[2] * FF_ALIGN(I420Frame->height,2) / 2);
|
|
libyuv::I422ToI420(pFrame->data[0], pFrame->linesize[0], pFrame->data[1], pFrame->linesize[1], pFrame->data[2],
|
|
pFrame->linesize[2], I420Frame->data[0], I420Frame->linesize[0], I420Frame->data[1],
|
|
I420Frame->linesize[1], I420Frame->data[2], I420Frame->linesize[2], I420Frame->width,
|
|
I420Frame->height);
|
|
|
|
av_frame_free(&pFrame);
|
|
pFrame = I420Frame;
|
|
data_on_device_mem = false;
|
|
}
|
|
avframe_to_bm_image(handle, pFrame, &img, true, data_on_device_mem);
|
|
|
|
Func_Exit:
|
|
av_packet_unref(&pkt);
|
|
|
|
if (pFrame) {
|
|
av_frame_free(&pFrame);
|
|
}
|
|
|
|
avformat_close_input(&pFormatCtx);
|
|
|
|
if (avio_ctx) {
|
|
av_freep(&avio_ctx->buffer);
|
|
av_freep(&avio_ctx);
|
|
}
|
|
|
|
if (dict) {
|
|
av_dict_free(&dict);
|
|
}
|
|
|
|
if (dec_ctx) {
|
|
avcodec_free_context(&dec_ctx);
|
|
}
|
|
|
|
return ret;
|
|
}
|