cellVdec fixed (requires LLE dmux)

Basic MPEG2 support
This commit is contained in:
Nekotekina 2016-07-22 15:54:35 +03:00
parent 74fec275c7
commit 5eb98b745f
2 changed files with 131 additions and 286 deletions

View file

@ -8,7 +8,6 @@ std::mutex g_mutex_avcodec_open2;
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"
}
@ -32,14 +31,10 @@ VideoDecoder::VideoDecoder(s32 type, u32 profile, u32 addr, u32 size, vm::ptr<Ce
, cbArg(arg)
, is_finished(false)
, is_closed(false)
, just_started(false)
, just_finished(false)
, frc_set(0)
, codec(nullptr)
, input_format(nullptr)
, ctx(nullptr)
{
av_register_all();
avcodec_register_all();
switch (type)
@ -47,143 +42,61 @@ VideoDecoder::VideoDecoder(s32 type, u32 profile, u32 addr, u32 size, vm::ptr<Ce
case CELL_VDEC_CODEC_TYPE_MPEG2:
{
codec = avcodec_find_decoder(AV_CODEC_ID_MPEG2VIDEO);
input_format = av_find_input_format("mpeg");
break;
}
case CELL_VDEC_CODEC_TYPE_AVC:
{
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
input_format = av_find_input_format("mpeg");
break;
}
case CELL_VDEC_CODEC_TYPE_DIVX:
{
codec = avcodec_find_decoder(AV_CODEC_ID_MPEG4);
input_format = av_find_input_format("mpeg");
break;
}
default:
{
throw EXCEPTION("Unknown type (0x%x)", type);
throw fmt::exception("Unknown video decoder type (0x%x)" HERE, type);
}
}
if (!codec)
{
throw EXCEPTION("avcodec_find_decoder() failed");
throw fmt::exception("avcodec_find_decoder() failed (type=0x%x)" HERE, type);
}
if (!input_format)
ctx = avcodec_alloc_context3(codec);
if (!ctx)
{
throw EXCEPTION("av_find_input_format() failed");
throw fmt::exception("avcodec_alloc_context3() failed (type=0x%x)" HERE, type);
}
fmt = avformat_alloc_context();
if (!fmt)
AVDictionary* opts{};
av_dict_set(&opts, "refcounted_frames", "1", 0);
std::lock_guard<std::mutex> lock(g_mutex_avcodec_open2);
int err = avcodec_open2(ctx, codec, &opts);
if (err || opts)
{
throw EXCEPTION("avformat_alloc_context() failed");
}
io_buf = (u8*)av_malloc(4096);
fmt->pb = avio_alloc_context(io_buf, 4096, 0, this, vdecRead, NULL, NULL);
if (!fmt->pb)
{
throw EXCEPTION("avio_alloc_context() failed");
throw fmt::exception("avcodec_open2() failed (err=0x%x, opts=%d)" HERE, err, opts ? 1 : 0);
}
}
VideoDecoder::~VideoDecoder()
{
// TODO: check finalization
VdecFrame vf;
while (frames.try_pop(vf))
{
av_frame_unref(vf.data);
av_frame_free(&vf.data);
}
if (ctx)
{
avcodec_close(ctx);
avformat_close_input(&fmt);
}
if (fmt)
{
if (io_buf)
{
av_free(io_buf);
}
if (fmt->pb) av_free(fmt->pb);
avformat_free_context(fmt);
}
}
int vdecRead(void* opaque, u8* buf, int buf_size)
{
VideoDecoder& vdec = *(VideoDecoder*)opaque;
int res = 0;
next:
if (vdec.reader.size < (u32)buf_size /*&& !vdec.just_started*/)
{
VdecTask task;
if (!vdec.job.peek(task, 0, &vdec.is_closed))
{
if (Emu.IsStopped()) cellVdec.warning("vdecRead() aborted");
return 0;
}
switch (task.type)
{
case vdecEndSeq:
case vdecClose:
{
buf_size = vdec.reader.size;
}
break;
case vdecDecodeAu:
{
std::memcpy(buf, vm::base(vdec.reader.addr), vdec.reader.size);
buf += vdec.reader.size;
buf_size -= vdec.reader.size;
res += vdec.reader.size;
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_AUDONE, CELL_OK, vdec.cbArg);
vdec.job.pop(vdec.task);
vdec.reader.addr = vdec.task.addr;
vdec.reader.size = vdec.task.size;
//LOG_NOTICE(HLE, "Video AU: size = 0x%x, pts = 0x%llx, dts = 0x%llx", vdec.task.size, vdec.task.pts, vdec.task.dts);
}
break;
default:
{
cellVdec.error("vdecRead(): unknown task (%d)", task.type);
Emu.Pause();
return -1;
}
}
goto next;
}
// TODO:: Syphurith: Orz. The if condition above is same with this one, so this would not be executed.
else if (vdec.reader.size < (u32)buf_size)
{
buf_size = vdec.reader.size;
}
if (!buf_size)
{
return res;
}
else
{
std::memcpy(buf, vm::base(vdec.reader.addr), buf_size);
vdec.reader.addr += buf_size;
vdec.reader.size -= buf_size;
return res + buf_size;
avcodec_free_context(&ctx);
}
}
@ -237,134 +150,39 @@ void vdecOpen(u32 vdec_id) // TODO: call from the constructor
{
case vdecStartSeq:
{
// TODO: reset data
cellVdec.warning("vdecStartSeq:");
avcodec_flush_buffers(vdec.ctx);
vdec.reader = {};
vdec.frc_set = 0;
vdec.just_started = true;
break;
}
case vdecEndSeq:
{
// TODO: finalize
cellVdec.warning("vdecEndSeq:");
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_SEQDONE, CELL_OK, vdec.cbArg);
vdec.just_finished = true;
vdec.frc_set = 0; // TODO: ???
vdec.last_pts = 0;
vdec.last_dts = 0;
break;
}
case vdecDecodeAu:
case vdecEndSeq:
{
int err;
AVPacket packet{};
vdec.reader.addr = task.addr;
vdec.reader.size = task.size;
//LOG_NOTICE(HLE, "Video AU: size = 0x%x, pts = 0x%llx, dts = 0x%llx", task.size, task.pts, task.dts);
if (vdec.just_started)
if (task.type == vdecDecodeAu)
{
vdec.first_pts = task.pts;
vdec.last_pts = -1;
vdec.first_dts = task.dts;
packet.pts = vdec.task.pts != -1 ? vdec.task.pts : AV_NOPTS_VALUE;
packet.dts = vdec.task.dts != -1 ? vdec.task.dts : AV_NOPTS_VALUE;
packet.data = vm::_ptr<u8>(vdec.task.addr);
packet.size = vdec.task.size;
cellVdec.trace("vdecDecodeAu: size = 0x%x, pts = 0x%llx, dts = 0x%llx", task.size, task.pts, task.dts);
}
struct AVPacketHolder : AVPacket
else
{
AVPacketHolder(u32 size)
{
av_init_packet(this);
if (size)
{
data = (u8*)av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
this->size = size + FF_INPUT_BUFFER_PADDING_SIZE;
}
else
{
data = NULL;
size = 0;
}
}
~AVPacketHolder()
{
av_free(data);
//av_free_packet(this);
}
} au(0);
if (vdec.just_started && vdec.just_finished)
{
avcodec_flush_buffers(vdec.ctx);
vdec.just_started = false;
vdec.just_finished = false;
packet.pts = AV_NOPTS_VALUE;
packet.dts = AV_NOPTS_VALUE;
packet.data = nullptr;
packet.size = 0;
cellVdec.warning("vdecEndSeq");
}
else if (vdec.just_started) // deferred initialization
{
AVDictionary* opts = nullptr;
av_dict_set(&opts, "probesize", "4096", 0);
err = avformat_open_input(&vdec.fmt, NULL, NULL, &opts);
if (err || opts)
{
throw EXCEPTION("avformat_open_input() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
}
if (vdec.type == CELL_VDEC_CODEC_TYPE_DIVX)
{
err = avformat_find_stream_info(vdec.fmt, NULL);
if (err || !vdec.fmt->nb_streams)
{
throw EXCEPTION("avformat_find_stream_info() failed (err=0x%x, nb_streams=%d)", err, vdec.fmt->nb_streams);
}
}
else
{
if (!avformat_new_stream(vdec.fmt, vdec.codec))
{
throw EXCEPTION("avformat_new_stream() failed");
}
}
vdec.ctx = vdec.fmt->streams[0]->codec; // TODO: check data
opts = nullptr;
av_dict_set(&opts, "refcounted_frames", "1", 0);
{
std::lock_guard<std::mutex> lock(g_mutex_avcodec_open2);
// not multithread-safe (???)
err = avcodec_open2(vdec.ctx, vdec.codec, &opts);
}
if (err || opts)
{
throw EXCEPTION("avcodec_open2() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
}
vdec.just_started = false;
}
bool last_frame = false;
while (true)
{
if (Emu.IsStopped() || vdec.is_closed)
{
if (Emu.IsStopped()) cellVdec.warning("vdecDecodeAu: aborted");
break;
}
last_frame = av_read_frame(vdec.fmt, &au) < 0;
if (last_frame)
{
//break;
av_free(au.data);
au.data = NULL;
au.size = 0;
}
struct VdecFrameHolder : VdecFrame
{
VdecFrameHolder()
@ -385,22 +203,28 @@ void vdecOpen(u32 vdec_id) // TODO: call from the constructor
if (!frame.data)
{
throw EXCEPTION("av_frame_alloc() failed");
throw fmt::exception("av_frame_alloc() failed" HERE);
}
int got_picture = 0;
int decode = avcodec_decode_video2(vdec.ctx, frame.data, &got_picture, &au);
int decode = avcodec_decode_video2(vdec.ctx, frame.data, &got_picture, &packet);
if (decode <= 0)
if (decode < 0)
{
if (decode < 0)
{
cellVdec.error("vdecDecodeAu: AU decoding error(0x%x)", decode);
}
if (!got_picture && vdec.reader.size == 0) break; // video end?
throw fmt::exception("vdecDecodeAu: AU decoding error(0x%x)" HERE, decode);
}
if (got_picture == 0)
{
break;
}
if (decode != packet.size)
{
cellVdec.error("vdecDecodeAu: incorrect AU size (0x%x, decoded 0x%x)", packet.size, decode);
}
if (got_picture)
{
if (frame.data->interlaced_frame)
@ -415,51 +239,33 @@ void vdecOpen(u32 vdec_id) // TODO: call from the constructor
if (vdec.frc_set)
{
if (vdec.last_pts == -1)
u64 amend = 0;
switch (vdec.frc_set)
{
u64 ts = av_frame_get_best_effort_timestamp(frame.data);
if (ts != AV_NOPTS_VALUE)
{
vdec.last_pts = ts;
}
else
{
vdec.last_pts = 0;
}
}
else switch (vdec.frc_set)
{
case CELL_VDEC_FRC_24000DIV1001: vdec.last_pts += 1001 * 90000 / 24000; break;
case CELL_VDEC_FRC_24: vdec.last_pts += 90000 / 24; break;
case CELL_VDEC_FRC_25: vdec.last_pts += 90000 / 25; break;
case CELL_VDEC_FRC_30000DIV1001: vdec.last_pts += 1001 * 90000 / 30000; break;
case CELL_VDEC_FRC_30: vdec.last_pts += 90000 / 30; break;
case CELL_VDEC_FRC_50: vdec.last_pts += 90000 / 50; break;
case CELL_VDEC_FRC_60000DIV1001: vdec.last_pts += 1001 * 90000 / 60000; break;
case CELL_VDEC_FRC_60: vdec.last_pts += 90000 / 60; break;
case CELL_VDEC_FRC_24000DIV1001: amend = 1001 * 90000 / 24000; break;
case CELL_VDEC_FRC_24: amend = 90000 / 24; break;
case CELL_VDEC_FRC_25: amend = 90000 / 25; break;
case CELL_VDEC_FRC_30000DIV1001: amend = 1001 * 90000 / 30000; break;
case CELL_VDEC_FRC_30: amend = 90000 / 30; break;
case CELL_VDEC_FRC_50: amend = 90000 / 50; break;
case CELL_VDEC_FRC_60000DIV1001: amend = 1001 * 90000 / 60000; break;
case CELL_VDEC_FRC_60: amend = 90000 / 60; break;
default:
{
throw EXCEPTION("Invalid frame rate code set (0x%x)", vdec.frc_set);
}
}
vdec.last_pts += amend;
vdec.last_dts += amend;
frame.frc = vdec.frc_set;
}
else
{
u64 ts = av_frame_get_best_effort_timestamp(frame.data);
if (ts != AV_NOPTS_VALUE)
{
vdec.last_pts = ts;
}
else if (vdec.last_pts == -1)
{
vdec.last_pts = 0;
}
else
{
vdec.last_pts += vdec.ctx->time_base.num * 90000 * vdec.ctx->ticks_per_frame / vdec.ctx->time_base.den;
}
const u64 amend = vdec.ctx->time_base.num * 90000 * vdec.ctx->ticks_per_frame / vdec.ctx->time_base.den;
vdec.last_pts += amend;
vdec.last_dts += amend;
if (vdec.ctx->time_base.num == 1)
{
@ -501,11 +307,11 @@ void vdecOpen(u32 vdec_id) // TODO: call from the constructor
}
}
frame.pts = vdec.last_pts;
frame.dts = (frame.pts - vdec.first_pts) + vdec.first_dts;
frame.pts = vdec.last_pts = frame.data->pkt_pts != AV_NOPTS_VALUE ? frame.data->pkt_pts : vdec.last_pts;
frame.dts = vdec.last_dts = frame.data->pkt_dts != AV_NOPTS_VALUE ? frame.data->pkt_dts : vdec.last_dts;
frame.userdata = task.userData;
//LOG_NOTICE(HLE, "got picture (pts=0x%llx, dts=0x%llx)", frame.pts, frame.dts);
cellVdec.trace("got picture (pts=0x%llx, dts=0x%llx)", frame.pts, frame.dts);
if (vdec.frames.push(frame, &vdec.is_closed))
{
@ -513,9 +319,22 @@ void vdecOpen(u32 vdec_id) // TODO: call from the constructor
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_PICOUT, CELL_OK, vdec.cbArg);
}
}
if (task.type == vdecDecodeAu)
{
break;
}
}
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_AUDONE, CELL_OK, vdec.cbArg);
if (task.type == vdecDecodeAu)
{
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_AUDONE, CELL_OK, vdec.cbArg);
}
else
{
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_SEQDONE, CELL_OK, vdec.cbArg);
}
break;
}
@ -533,7 +352,7 @@ void vdecOpen(u32 vdec_id) // TODO: call from the constructor
default:
{
throw EXCEPTION("Unknown task(%d)", task.type);
throw fmt::exception("Unknown task(%d)" HERE, task.type);
}
}
}
@ -840,6 +659,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
avc->horizontalSize = frame.width;
avc->verticalSize = frame.height;
switch (frame.pict_type)
{
case AV_PICTURE_TYPE_I: avc->pictureType[0] = CELL_VDEC_AVC_PCT_I; break;
@ -847,6 +667,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
case AV_PICTURE_TYPE_B: avc->pictureType[0] = CELL_VDEC_AVC_PCT_B; break;
default: cellVdec.error("cellVdecGetPicItem(AVC): unknown pict_type value (0x%x)", frame.pict_type);
}
avc->pictureType[1] = CELL_VDEC_AVC_PCT_UNKNOWN; // ???
avc->idrPictureFlag = false; // ???
avc->aspect_ratio_idc = CELL_VDEC_AVC_ARI_SAR_UNSPECIFIED; // ???
@ -890,7 +711,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
}
else if (vdec->type == CELL_VDEC_CODEC_TYPE_DIVX)
{
auto dvx = vm::ptr<CellVdecDivxInfo>::make(info.addr() + SIZE_32(CellVdecPicItem));
const vm::ptr<CellVdecDivxInfo> dvx = vm::cast(info.addr() + SIZE_32(CellVdecPicItem));
switch (frame.pict_type)
{
@ -899,6 +720,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
case AV_PICTURE_TYPE_B: dvx->pictureType = CELL_VDEC_DIVX_VCT_B; break;
default: cellVdec.error("cellVdecGetPicItem(DivX): unknown pict_type value (0x%x)", frame.pict_type);
}
dvx->horizontalSize = frame.width;
dvx->verticalSize = frame.height;
dvx->pixelAspectRatio = CELL_VDEC_DIVX_ARI_PAR_1_1; // ???
@ -909,6 +731,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
dvx->transferCharacteristics = CELL_VDEC_DIVX_TC_ITU_R_BT_709; // ???
dvx->matrixCoefficients = CELL_VDEC_DIVX_MXC_ITU_R_BT_709; // ???
dvx->pictureStruct = CELL_VDEC_DIVX_PSTR_FRAME; // ???
switch (vf.frc)
{
case CELL_VDEC_FRC_24000DIV1001: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_24000DIV1001; break;
@ -924,9 +747,44 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
}
else if (vdec->type == CELL_VDEC_CODEC_TYPE_MPEG2)
{
auto mp2 = vm::ptr<CellVdecMpeg2Info>::make(info.addr() + SIZE_32(CellVdecPicItem));
const vm::ptr<CellVdecMpeg2Info> mp2 = vm::cast(info.addr() + SIZE_32(CellVdecPicItem));
throw EXCEPTION("MPEG2");
std::memset(mp2.get_ptr(), 0, sizeof(CellVdecMpeg2Info));
mp2->horizontal_size = frame.width;
mp2->vertical_size = frame.height;
mp2->aspect_ratio_information = CELL_VDEC_MPEG2_ARI_SAR_1_1; // ???
switch (vf.frc)
{
case CELL_VDEC_FRC_24000DIV1001: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_24000DIV1001; break;
case CELL_VDEC_FRC_24: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_24; break;
case CELL_VDEC_FRC_25: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_25; break;
case CELL_VDEC_FRC_30000DIV1001: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_30000DIV1001; break;
case CELL_VDEC_FRC_30: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_30; break;
case CELL_VDEC_FRC_50: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_50; break;
case CELL_VDEC_FRC_60000DIV1001: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_60000DIV1001; break;
case CELL_VDEC_FRC_60: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_60; break;
default: cellVdec.error("cellVdecGetPicItem(MPEG2): unknown frc value (0x%x)", vf.frc);
}
mp2->progressive_sequence = true; // ???
mp2->low_delay = true; // ???
mp2->video_format = CELL_VDEC_MPEG2_VF_UNSPECIFIED; // ???
mp2->colour_description = false; // ???
switch (frame.pict_type)
{
case AV_PICTURE_TYPE_I: mp2->picture_coding_type[0] = CELL_VDEC_MPEG2_PCT_I; break;
case AV_PICTURE_TYPE_P: mp2->picture_coding_type[0] = CELL_VDEC_MPEG2_PCT_P; break;
case AV_PICTURE_TYPE_B: mp2->picture_coding_type[0] = CELL_VDEC_MPEG2_PCT_B; break;
default: cellVdec.error("cellVdecGetPicItem(MPEG2): unknown pict_type value (0x%x)", frame.pict_type);
}
mp2->picture_coding_type[1] = CELL_VDEC_MPEG2_PCT_FORBIDDEN; // ???
mp2->picture_structure[0] = CELL_VDEC_MPEG2_PSTR_FRAME;
mp2->picture_structure[1] = CELL_VDEC_MPEG2_PSTR_FRAME;
// ...
}
*picItem = info;

View file

@ -690,8 +690,6 @@ struct VdecFrame
u32 frc;
};
int vdecRead(void* opaque, u8* buf, int buf_size);
class VideoDecoder
{
public:
@ -699,22 +697,11 @@ public:
u32 id;
volatile bool is_closed;
volatile bool is_finished;
bool just_started;
bool just_finished;
AVCodec* codec;
AVInputFormat* input_format;
AVCodecContext* ctx;
AVFormatContext* fmt;
u8* io_buf;
struct AVCodec* codec;
struct AVCodecContext* ctx;
struct VideoReader
{
u32 addr;
u32 size;
} reader;
squeue_t<VdecFrame> frames;
squeue_t<VdecFrame, 64> frames;
const s32 type;
const u32 profile;
@ -725,9 +712,9 @@ public:
u32 memBias;
VdecTask task; // current task variable
u64 last_pts, first_pts, first_dts;
u32 frc_set; // frame rate overwriting
AVRational rfr, afr;
u64 last_pts;
u64 last_dts;
std::shared_ptr<PPUThread> vdecCb;