/*
* Video Acceleration (shared data between FFmpeg and the video player)
* HW decode acceleration for MPEG-4, H.264, H263 and VC-1
* Using Samsung Multi-Format Codec API
*
* Copyright(C) 2012 TuYuanDong
* author: tuyaundong
* email: tuyuandong@gmail.com
* Date: 2012-02-27
*
*/
#include "s3c_va.h"
#include "./libavcodec/avcodec.h"
#include "./libavcodec/mpeg4video.h"
#include "./libavcodec/h264.h"
#include "SsbSipH264Decode.h"
#include "SsbSipLogMsg.h"
#include "vaapi.h"
#define NAL_UNIT_TYPE_TYPE(n) ((0x001F) & (n))
static const uint8_t delimiter_h264[4] = {0x00, 0x00, 0x00, 0x01};
/** Initialize and start decoding a frame with S3CMFC API. */
static int start_frame(AVCodecContext *avctx,
av_unused const uint8_t *buffer,
av_unused uint32_t size)
{
int is_avc = 0;
SSBSIP_H264_STREAM_INFO stream_info;
//如果decode_slice函数解码失败,则需停止硬解,转向软解
if(bDecSuc == -1)
{
avctx->hwaccel = NULL;
LOG_MSG(LOG_TRACE,__FUNCTION__,"Hardware decode video failed,being used soft decoder\r\n");
return -1;
}
if(ResetVideoAccelerator(avctx,SsbSipH264DecodeDeInit) < 0)
goto fail;
if(avctx->extradata_size > 0)
{
uint8_t * p = avctx->extradata;
if(p[0] == 0x01)
is_avc = 1;
}
if(bFistFrame && size >0) //初始化硬解加速器
{
// 1. Create new instance
handle = SsbSipH264DecodeInit();
if (handle == NULL) {
LOG_MSG(LOG_ERROR, __FUNCTION__, "Decoder Init Failed\n");
goto fail;
}
// 2. Obtaining the Input Buffer
pStrmBuf = SsbSipH264DecodeGetInBuf(handle,BUFFER_SIZE);
if (pStrmBuf == NULL) {
LOG_MSG(LOG_ERROR, __FUNCTION__, "SsbSipH264DecodeGetInBuf Failed.\n");
goto fail;
}
}
BufSize = 0;
if(is_avc)
{
uint8_t* buf = buffer;
int tot_size = 0;
if(bFistFrame && avctx->extradata_size > 0)
{
uint8_t* p = avctx->extradata;
int i, cnt, nalsize;
// Decode sps from avcC
cnt = *(p+5) & 0x1f; // Number of sps
p += 6;
for (i = 0; i < cnt; i++) {
nalsize = AV_RB16(p);
if((nalsize+2) > avctx->extradata_size - (p-avctx->extradata))
return -1;
//copy h264 data
memcpy(pStrmBuf+BufSize,delimiter_h264,sizeof(delimiter_h264));
BufSize += sizeof(delimiter_h264);
p += 2;
memcpy(pStrmBuf+BufSize,p,nalsize);
BufSize += nalsize;
p += nalsize;
}
// Decode pps from avcC
cnt = *(p++); // Number of pps
for (i = 0; i < cnt; i++) {
nalsize = AV_RB16(p);
if((nalsize+2) > avctx->extradata_size - (p-avctx->extradata))
return -1;
//copy h264 data
memcpy(pStrmBuf+BufSize,delimiter_h264,sizeof(delimiter_h264));
BufSize += sizeof(delimiter_h264);
p += 2;
memcpy(pStrmBuf+BufSize,p,nalsize);
BufSize += nalsize;
p += nalsize;
}
//fwrite(avctx->extradata, 1, avctx->extradata_size, fp1);
}
unsigned char nal_type = 255;
int num_iframe = 0;
do
{
int slice_size = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
nal_type = NAL_UNIT_TYPE_TYPE(buf[4]);
//printf("slice size=%d nal_type=%d\n",slice_size,nal_type);
if(nal_type == 5)
num_iframe ++;
memcpy(pStrmBuf+BufSize,delimiter_h264,sizeof(delimiter_h264));
BufSize += sizeof(delimiter_h264);
memcpy(pStrmBuf+BufSize,buf+4,slice_size);
BufSize += slice_size;
buf += slice_size+4;
tot_size += slice_size+4;
}while(tot_size < size);
if(num_iframe > 1)
goto fail;
}
else
{
if (bFistFrame && avctx->extradata_size != 0)
{
memcpy(pStrmBuf+BufSize,avctx->extradata,avctx->extradata_size);
BufSize += avctx->extradata_size;
}
memcpy(pStrmBuf+BufSize,buffer,size);
BufSize += size;
}
if(bFistFrame)
{
// 3. Configuring the instance with the config stream SsbSipXXXDecodeExe
int ret =SsbSipH264DecodeExe(handle, BufSize);
if (ret != SSBSIP_H264_DEC_RET_OK) {
LOG_MSG(LOG_ERROR, __FUNCTION__, "Decoder SsbSipH264DecodeExe Failed err=%d.\n",ret);
goto fail;
}
/// 4. Get stream information
SsbSipH264DecodeGetConfig(handle, H264_DEC_GETCONF_STREAMINFO, &(stream_info));
LOG_MSG(LOG_TRACE, __FUNCTION__,"STREAMINFO width=%d height=%d\r\n",stream_info.width, stream_info.height);
bFistFrame = 0;
}
BufSize = size;
return 0;
fail:
LOG_MSG(LOG_TRACE,__FUNCTION__,"Hardware decode video failed,being used soft decoder\r\n");
if(handle)
{
SsbSipH264DecodeDeInit(handle);
handle = NULL;
}
avctx->hwaccel = NULL; //用于跳转到软解方案中
return -1;
}
/** End a hardware decoding based frame. */
static int end_frame(AVCodecContext *avctx)
{
struct H264Context *h = avctx->priv_data;
struct MpegEncContext *s = &h->s;
Picture* pic = s->current_picture_ptr;
uint8_t* pYUVBuf = NULL;
long size;
if(bDecSuc > 0 && handle)
{
pYUVBuf = SsbSipH264DecodeGetOutBuf(handle,&size);
if(pYUVBuf)
{
avpicture_fill(pic,pYUVBuf,pic->format,pic->width,pic->height);
//pYUVBuf 中的YUV420在内存中不是连续的
pic->data[1] = (int8_t*)(pYUVBuf+4*size/6);
pic->data[2] = (int8_t*)(pYUVBuf+5*size/6);
}
// LOG_MSG(LOG_ERROR, __FUNCTION__,"width=%d height=%d size[%d] data[%p][%p][%p] data2[%p][%p][%p]\r\n",
// pic->width,pic->height,size,
// pic->data[0],pic->data[1],pic->data[2],pYUVBuf,pYUVBuf+4*size/6,pYUVBuf+5*size/6);
}
return 0;
}
/** Decode the given h264 slice with S3CMFC API. */
static int decode_slice(AVCodecContext *avctx,
const uint8_t *buffer,
uint32_t size)
{
int ret = 0;
bDecSuc = 0;
ret =SsbSipH264DecodeExe(handle, BufSize);
if (ret != SSBSIP_H264_DEC_RET_OK) {
bDecSuc = -1;
return -1;
}
else
bDecSuc = 1;
return 0;
}
AVHWAccel ff_h264_s3c_hwaccel = {
"h264_s3c",
AVMEDIA_TYPE_VIDEO,
CODEC_ID_H264,
PIX_FMT_YUV420P,
0,
NULL,
start_frame,
decode_slice,
end_frame,
0,
};