rpcsx/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp

908 lines
25 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/SysCalls/Modules.h"
#include "Emu/SysCalls/CB_FUNC.h"
std::mutex g_mutex_avcodec_open2;
extern "C"
{
2014-03-06 12:50:45 +01:00
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
}
2014-08-26 01:55:37 +02:00
#include "Emu/CPU/CPUThreadManager.h"
2014-08-23 22:40:04 +02:00
#include "cellPamf.h"
2014-02-27 19:25:32 +01:00
#include "cellVdec.h"
Module *cellVdec = nullptr;
2014-12-12 01:21:34 +01:00
#define VDEC_ERROR(...) { cellVdec->Error(__VA_ARGS__); Emu.Pause(); return; } // only for decoder thread
2014-09-12 23:14:48 +02:00
VideoDecoder::VideoDecoder(CellVdecCodecType type, u32 profile, u32 addr, u32 size, vm::ptr<CellVdecCbMsg> func, u32 arg)
2014-08-23 22:40:04 +02:00
: type(type)
, profile(profile)
, memAddr(addr)
, memSize(size)
, memBias(0)
, cbFunc(func)
, cbArg(arg)
, is_finished(false)
2014-10-18 00:20:03 +02:00
, is_closed(false)
2014-08-23 22:40:04 +02:00
, just_started(false)
, just_finished(false)
2014-12-09 17:13:03 +01:00
, frc_set(0)
, codec(nullptr)
, input_format(nullptr)
2014-08-23 22:40:04 +02:00
, ctx(nullptr)
, vdecCb(nullptr)
{
2014-08-26 18:45:43 +02:00
av_register_all();
avcodec_register_all();
2014-12-09 17:13:03 +01:00
switch (type)
{
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:
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("VideoDecoder(): unknown type (0x%x)", type);
2014-12-09 17:13:03 +01:00
}
}
2014-08-23 22:40:04 +02:00
if (!codec)
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("VideoDecoder(): avcodec_find_decoder() failed");
2014-12-09 17:13:03 +01:00
}
if (!input_format)
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("VideoDecoder(): av_find_input_format() failed");
2014-08-23 22:40:04 +02:00
}
fmt = avformat_alloc_context();
if (!fmt)
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("VideoDecoder(): avformat_alloc_context() failed");
2014-08-23 22:40:04 +02:00
}
io_buf = (u8*)av_malloc(4096);
fmt->pb = avio_alloc_context(io_buf, 4096, 0, this, vdecRead, NULL, NULL);
if (!fmt->pb)
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("VideoDecoder(): avio_alloc_context() failed");
2014-08-23 22:40:04 +02:00
}
}
VideoDecoder::~VideoDecoder()
{
// TODO: check finalization
2014-10-18 19:00:21 +02:00
VdecFrame vf;
2014-12-24 23:24:17 +01:00
while (frames.try_pop(vf))
2014-10-18 00:20:03 +02:00
{
av_frame_unref(vf.data);
av_frame_free(&vf.data);
}
2014-08-23 22:40:04 +02:00
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);
}
}
2014-03-02 00:50:47 +01:00
int vdecRead(void* opaque, u8* buf, int buf_size)
{
VideoDecoder& vdec = *(VideoDecoder*)opaque;
2014-03-03 00:02:42 +01:00
int res = 0;
2014-03-13 10:17:45 +01:00
next:
if (vdec.reader.size < (u32)buf_size /*&& !vdec.just_started*/)
2014-03-03 00:02:42 +01:00
{
2014-10-18 19:00:21 +02:00
VdecTask task;
2014-12-24 23:24:17 +01:00
if (!vdec.job.peek(task, 0, &vdec.is_closed))
{
2014-10-18 19:00:21 +02:00
if (Emu.IsStopped()) cellVdec->Warning("vdecRead() aborted");
return 0;
}
2014-10-18 19:00:21 +02:00
switch (task.type)
{
case vdecEndSeq:
2014-06-29 05:21:57 +02:00
case vdecClose:
2014-10-18 00:20:03 +02:00
{
buf_size = vdec.reader.size;
}
break;
case vdecDecodeAu:
2014-10-18 00:20:03 +02:00
{
memcpy(buf, vm::get_ptr<void>(vdec.reader.addr), vdec.reader.size);
2014-10-18 00:20:03 +02:00
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);
2014-12-24 23:24:17 +01:00
vdec.job.pop(vdec.task);
2014-10-18 00:20:03 +02:00
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:
2014-10-18 00:20:03 +02:00
{
2014-10-18 19:00:21 +02:00
cellVdec->Error("vdecRead(): unknown task (%d)", task.type);
2014-10-18 00:20:03 +02:00
Emu.Pause();
return -1;
}
}
2014-03-13 10:17:45 +01:00
goto next;
2014-03-07 22:31:08 +01:00
}
else if (vdec.reader.size < (u32)buf_size)
{
buf_size = vdec.reader.size;
2014-03-03 00:02:42 +01:00
}
2014-03-02 00:50:47 +01:00
if (!buf_size)
{
2014-03-03 00:02:42 +01:00
return res;
2014-03-02 00:50:47 +01:00
}
else
{
2014-09-05 22:26:36 +02:00
memcpy(buf, vm::get_ptr<void>(vdec.reader.addr), buf_size);
2014-03-02 00:50:47 +01:00
vdec.reader.addr += buf_size;
vdec.reader.size -= buf_size;
2014-03-03 00:02:42 +01:00
return res + buf_size;
2014-03-02 00:50:47 +01:00
}
}
2014-09-02 03:05:13 +02:00
u32 vdecQueryAttr(CellVdecCodecType type, u32 profile, u32 spec_addr /* may be 0 */, vm::ptr<CellVdecAttr> attr)
{
2014-02-27 19:25:32 +01:00
switch (type) // TODO: check profile levels
{
case CELL_VDEC_CODEC_TYPE_AVC: cellVdec->Warning("cellVdecQueryAttr: AVC (profile=%d)", profile); break;
2014-12-09 17:13:03 +01:00
case CELL_VDEC_CODEC_TYPE_MPEG2: cellVdec->Warning("cellVdecQueryAttr: MPEG2 (profile=%d)", profile); break;
case CELL_VDEC_CODEC_TYPE_DIVX: cellVdec->Warning("cellVdecQueryAttr: DivX (profile=%d)", profile); break;
2014-02-27 19:25:32 +01:00
default: return CELL_VDEC_ERROR_ARG;
}
// TODO: check values
attr->decoderVerLower = 0x280000; // from dmux
attr->decoderVerUpper = 0x260000;
attr->memSize = 4 * 1024 * 1024; // 4 MB
2014-03-01 09:38:50 +01:00
attr->cmdDepth = 16;
return CELL_OK;
}
2014-12-28 16:53:02 +01:00
u32 vdecOpen(VideoDecoder* vdec_ptr)
2014-02-27 19:25:32 +01:00
{
2014-12-28 16:53:02 +01:00
std::shared_ptr<VideoDecoder> sptr(vdec_ptr);
VideoDecoder& vdec = *vdec_ptr;
2014-02-27 19:25:32 +01:00
2014-12-28 16:53:02 +01:00
u32 vdec_id = cellVdec->GetNewId(sptr);
2014-02-27 19:25:32 +01:00
vdec.id = vdec_id;
2014-09-12 23:14:48 +02:00
vdec.vdecCb = (PPUThread*)&Emu.GetCPU().AddThread(CPU_THREAD_PPU);
2015-01-16 15:36:53 +01:00
vdec.vdecCb->SetName(fmt::format("VideoDecoder[%d] Callback", vdec_id));
2014-09-13 16:25:02 +02:00
vdec.vdecCb->SetEntry(0);
2014-09-12 23:14:48 +02:00
vdec.vdecCb->SetPrio(1001);
vdec.vdecCb->SetStackSize(0x10000);
2014-09-13 16:25:02 +02:00
vdec.vdecCb->InitStack();
vdec.vdecCb->InitRegs();
vdec.vdecCb->DoRun();
2014-03-07 22:31:08 +01:00
2015-01-16 15:36:53 +01:00
thread_t t(fmt::format("VideoDecoder[%d] Thread", vdec_id), [vdec_ptr, sptr]()
2014-02-27 19:25:32 +01:00
{
2014-12-28 16:53:02 +01:00
VideoDecoder& vdec = *vdec_ptr;
VdecTask& task = vdec.task;
2014-02-27 19:25:32 +01:00
while (true)
{
2014-10-18 00:20:03 +02:00
if (Emu.IsStopped() || vdec.is_closed)
2014-02-27 19:25:32 +01:00
{
break;
}
2014-12-24 23:24:17 +01:00
if (!vdec.job.pop(task, &vdec.is_closed))
2014-02-27 19:25:32 +01:00
{
break;
}
switch (task.type)
{
case vdecStartSeq:
2014-08-23 22:40:04 +02:00
{
// TODO: reset data
cellVdec->Warning("vdecStartSeq:");
2014-03-03 00:02:42 +01:00
2014-10-23 00:53:44 +02:00
vdec.reader = {};
2014-12-09 17:13:03 +01:00
vdec.frc_set = 0;
2014-08-23 22:40:04 +02:00
vdec.just_started = true;
2014-12-09 17:13:03 +01:00
break;
2014-08-23 22:40:04 +02:00
}
2014-02-27 19:25:32 +01:00
case vdecEndSeq:
2014-08-23 22:40:04 +02:00
{
// TODO: finalize
cellVdec->Warning("vdecEndSeq:");
2014-03-03 00:02:42 +01:00
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_SEQDONE, CELL_OK, vdec.cbArg);
2014-03-03 00:02:42 +01:00
2014-08-23 22:40:04 +02:00
vdec.just_finished = true;
2014-12-09 17:13:03 +01:00
break;
2014-08-23 22:40:04 +02:00
}
2014-02-27 19:25:32 +01:00
case vdecDecodeAu:
2014-08-23 22:40:04 +02:00
{
int err;
if (task.mode != CELL_VDEC_DEC_MODE_NORMAL)
2014-02-27 19:25:32 +01:00
{
2014-08-23 22:40:04 +02:00
cellVdec->Error("vdecDecodeAu: unsupported decoding mode(%d)", task.mode);
break;
}
2014-03-02 00:50:47 +01:00
2014-08-23 22:40:04 +02:00
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);
2014-02-27 19:25:32 +01:00
2014-08-23 22:40:04 +02:00
if (vdec.just_started)
{
vdec.first_pts = task.pts;
2014-12-09 17:13:03 +01:00
vdec.last_pts = -1;
2014-08-23 22:40:04 +02:00
vdec.first_dts = task.dts;
}
2014-03-02 00:50:47 +01:00
2014-08-23 22:40:04 +02:00
struct AVPacketHolder : AVPacket
{
AVPacketHolder(u32 size)
{
2014-08-23 22:40:04 +02:00
av_init_packet(this);
2014-03-03 00:02:42 +01:00
2014-08-23 22:40:04 +02:00
if (size)
2014-03-03 00:02:42 +01:00
{
2014-08-23 22:40:04 +02:00
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;
2014-03-03 00:02:42 +01:00
}
2014-08-23 22:40:04 +02:00
else
2014-03-03 00:02:42 +01:00
{
2014-08-23 22:40:04 +02:00
data = NULL;
size = 0;
2014-03-03 00:02:42 +01:00
}
2014-08-23 22:40:04 +02:00
}
2014-03-03 00:02:42 +01:00
2014-08-23 22:40:04 +02:00
~AVPacketHolder()
{
av_free(data);
//av_free_packet(this);
}
2014-02-27 19:25:32 +01:00
2014-08-23 22:40:04 +02:00
} au(0);
if (vdec.just_started && vdec.just_finished)
{
avcodec_flush_buffers(vdec.ctx);
vdec.just_started = false;
vdec.just_finished = false;
}
else if (vdec.just_started) // deferred initialization
{
2014-12-12 01:21:34 +01:00
AVDictionary* opts = nullptr;
av_dict_set(&opts, "probesize", "4096", 0);
err = avformat_open_input(&vdec.fmt, NULL, NULL, &opts);
if (err || opts)
2014-06-29 05:21:57 +02:00
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: avformat_open_input() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
2014-06-29 05:21:57 +02:00
}
2014-12-09 17:13:03 +01:00
if (vdec.type == CELL_VDEC_CODEC_TYPE_DIVX)
2014-02-27 19:25:32 +01:00
{
2014-12-09 17:13:03 +01:00
err = avformat_find_stream_info(vdec.fmt, NULL);
if (err || !vdec.fmt->nb_streams)
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: avformat_find_stream_info() failed (err=0x%x, nb_streams=%d)", err, vdec.fmt->nb_streams);
2014-12-09 17:13:03 +01:00
}
2014-08-23 22:40:04 +02:00
}
2014-12-09 17:13:03 +01:00
else
2014-08-23 22:40:04 +02:00
{
2014-12-09 17:13:03 +01:00
if (!avformat_new_stream(vdec.fmt, vdec.codec))
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: avformat_new_stream() failed");
2014-12-09 17:13:03 +01:00
}
2014-08-23 22:40:04 +02:00
}
vdec.ctx = vdec.fmt->streams[0]->codec; // TODO: check data
2014-03-13 10:17:45 +01:00
2014-12-12 01:21:34 +01:00
opts = nullptr;
2014-08-23 22:40:04 +02:00
av_dict_set(&opts, "refcounted_frames", "1", 0);
{
std::lock_guard<std::mutex> lock(g_mutex_avcodec_open2);
// not multithread-safe (???)
2014-12-09 17:13:03 +01:00
err = avcodec_open2(vdec.ctx, vdec.codec, &opts);
2014-08-23 22:40:04 +02:00
}
2014-12-12 01:21:34 +01:00
if (err || opts)
2014-08-23 22:40:04 +02:00
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: avcodec_open2() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
2014-02-27 19:25:32 +01:00
}
2014-12-09 17:13:03 +01:00
2014-08-23 22:40:04 +02:00
vdec.just_started = false;
}
2014-03-01 09:38:50 +01:00
2014-08-23 22:40:04 +02:00
bool last_frame = false;
2014-08-23 22:40:04 +02:00
while (true)
{
2014-10-18 00:20:03 +02:00
if (Emu.IsStopped() || vdec.is_closed)
2014-03-01 09:38:50 +01:00
{
2014-10-18 00:20:03 +02:00
if (Emu.IsStopped()) cellVdec->Warning("vdecDecodeAu: aborted");
break;
2014-08-23 22:40:04 +02:00
}
last_frame = av_read_frame(vdec.fmt, &au) < 0;
if (last_frame)
{
//break;
av_free(au.data);
au.data = NULL;
au.size = 0;
}
2014-03-13 10:17:45 +01:00
2014-08-23 22:40:04 +02:00
struct VdecFrameHolder : VdecFrame
{
VdecFrameHolder()
{
2014-08-23 22:40:04 +02:00
data = av_frame_alloc();
}
2014-08-23 22:40:04 +02:00
~VdecFrameHolder()
2014-03-03 00:02:42 +01:00
{
2014-08-23 22:40:04 +02:00
if (data)
2014-03-03 00:02:42 +01:00
{
2014-08-23 22:40:04 +02:00
av_frame_unref(data);
av_frame_free(&data);
2014-03-03 00:02:42 +01:00
}
2014-08-23 22:40:04 +02:00
}
2014-03-03 00:02:42 +01:00
2014-08-23 22:40:04 +02:00
} frame;
2014-03-03 00:02:42 +01:00
2014-08-23 22:40:04 +02:00
if (!frame.data)
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: av_frame_alloc() failed");
2014-08-23 22:40:04 +02:00
}
2014-03-01 09:38:50 +01:00
2014-08-23 22:40:04 +02:00
int got_picture = 0;
2014-03-01 09:38:50 +01:00
2014-08-23 22:40:04 +02:00
int decode = avcodec_decode_video2(vdec.ctx, frame.data, &got_picture, &au);
2014-03-01 09:38:50 +01:00
2014-08-23 22:40:04 +02:00
if (decode <= 0)
{
2014-12-12 01:21:34 +01:00
if (decode < 0)
2014-03-03 00:02:42 +01:00
{
2014-08-23 22:40:04 +02:00
cellVdec->Error("vdecDecodeAu: AU decoding error(0x%x)", decode);
2014-03-03 00:02:42 +01:00
}
2014-08-23 22:40:04 +02:00
if (!got_picture && vdec.reader.size == 0) break; // video end?
}
2014-03-03 00:02:42 +01:00
2014-08-23 22:40:04 +02:00
if (got_picture)
{
2014-12-09 17:13:03 +01:00
if (frame.data->interlaced_frame)
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: interlaced frames not supported (0x%x)", frame.data->interlaced_frame);
2014-12-09 17:13:03 +01:00
}
if (frame.data->repeat_pict)
2014-03-03 00:02:42 +01:00
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: repeated frames not supported (0x%x)", frame.data->repeat_pict);
2014-12-09 17:13:03 +01:00
}
if (vdec.frc_set)
{
if (vdec.last_pts == -1)
{
2015-01-16 15:36:53 +01:00
u64 ts = av_frame_get_best_effort_timestamp(frame.data);
if (ts != AV_NOPTS_VALUE)
{
vdec.last_pts = ts;
}
else
{
vdec.last_pts = 0;
}
2014-12-09 17:13:03 +01:00
}
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;
default:
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: invalid frame rate code set (0x%x)", vdec.frc_set);
2014-12-09 17:13:03 +01:00
}
}
frame.frc = vdec.frc_set;
2014-08-23 22:40:04 +02:00
}
else
{
2014-12-09 17:13:03 +01:00
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;
}
if (vdec.ctx->time_base.num == 1)
{
switch ((u64)vdec.ctx->time_base.den + (u64)(vdec.ctx->ticks_per_frame - 1) * 0x100000000ull)
{
2014-12-11 20:25:11 +01:00
case 24: case 0x100000000ull + 48: frame.frc = CELL_VDEC_FRC_24; break;
case 25: case 0x100000000ull + 50: frame.frc = CELL_VDEC_FRC_25; break;
case 30: case 0x100000000ull + 60: frame.frc = CELL_VDEC_FRC_30; break;
case 50: case 0x100000000ull + 100: frame.frc = CELL_VDEC_FRC_50; break;
case 60: case 0x100000000ull + 120: frame.frc = CELL_VDEC_FRC_60; break;
2014-12-09 17:13:03 +01:00
default:
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: unsupported time_base.den (%d/1, tpf=%d)", vdec.ctx->time_base.den, vdec.ctx->ticks_per_frame);
2014-12-09 17:13:03 +01:00
}
}
}
else if (vdec.ctx->time_base.num == 1001)
{
if (vdec.ctx->time_base.den / vdec.ctx->ticks_per_frame == 24000)
{
frame.frc = CELL_VDEC_FRC_24000DIV1001;
}
else if (vdec.ctx->time_base.den / vdec.ctx->ticks_per_frame == 30000)
{
frame.frc = CELL_VDEC_FRC_30000DIV1001;
}
2014-12-09 18:24:50 +01:00
else if (vdec.ctx->time_base.den / vdec.ctx->ticks_per_frame == 60000)
{
frame.frc = CELL_VDEC_FRC_60000DIV1001;
}
2014-12-09 17:13:03 +01:00
else
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: unsupported time_base.den (%d/1001, tpf=%d)", vdec.ctx->time_base.den, vdec.ctx->ticks_per_frame);
2014-12-09 17:13:03 +01:00
}
}
else
{
2014-12-12 01:21:34 +01:00
VDEC_ERROR("vdecDecodeAu: unsupported time_base.num (%d)", vdec.ctx->time_base.num);
2014-12-09 17:13:03 +01:00
}
2014-08-23 22:40:04 +02:00
}
2014-12-09 17:13:03 +01:00
frame.pts = vdec.last_pts;
2014-08-23 22:40:04 +02:00
frame.dts = (frame.pts - vdec.first_pts) + vdec.first_dts;
frame.userdata = task.userData;
2014-03-19 01:32:23 +01:00
2014-08-23 22:40:04 +02:00
//LOG_NOTICE(HLE, "got picture (pts=0x%llx, dts=0x%llx)", frame.pts, frame.dts);
2014-03-19 01:32:23 +01:00
2014-12-24 23:24:17 +01:00
if (vdec.frames.push(frame, &vdec.is_closed))
2014-10-18 00:20:03 +02:00
{
frame.data = nullptr; // to prevent destruction
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_PICOUT, CELL_OK, vdec.cbArg);
2014-10-18 00:20:03 +02:00
}
2014-03-01 09:38:50 +01:00
}
2014-02-27 19:25:32 +01:00
}
2014-08-23 22:40:04 +02:00
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_AUDONE, CELL_OK, vdec.cbArg);
2014-12-09 17:13:03 +01:00
break;
2014-08-23 22:40:04 +02:00
}
2014-02-27 19:25:32 +01:00
case vdecSetFrameRate:
2014-08-23 22:40:04 +02:00
{
2014-12-11 20:25:11 +01:00
cellVdec->Warning("vdecSetFrameRate(0x%x)", task.frc);
2014-12-09 17:13:03 +01:00
vdec.frc_set = task.frc;
break;
2014-08-23 22:40:04 +02:00
}
2014-02-27 19:25:32 +01:00
2014-12-12 01:21:34 +01:00
case vdecClose:
{
break;
}
2014-10-18 00:20:03 +02:00
2014-02-27 19:25:32 +01:00
default:
2014-10-18 00:20:03 +02:00
{
2015-01-16 15:36:53 +01:00
VDEC_ERROR("VideoDecoder thread error: unknown task(%d)", task.type);
2014-10-18 00:20:03 +02:00
}
2014-02-27 19:25:32 +01:00
}
}
vdec.is_finished = true;
2014-02-27 19:25:32 +01:00
});
return vdec_id;
}
int cellVdecQueryAttr(vm::ptr<const CellVdecType> type, vm::ptr<CellVdecAttr> attr)
2014-02-27 19:25:32 +01:00
{
2014-09-02 03:05:13 +02:00
cellVdec->Warning("cellVdecQueryAttr(type_addr=0x%x, attr_addr=0x%x)", type.addr(), attr.addr());
2014-02-27 19:25:32 +01:00
return vdecQueryAttr(type->codecType, type->profileLevel, 0, attr);
}
int cellVdecQueryAttrEx(vm::ptr<const CellVdecTypeEx> type, vm::ptr<CellVdecAttr> attr)
{
2014-09-02 03:05:13 +02:00
cellVdec->Warning("cellVdecQueryAttrEx(type_addr=0x%x, attr_addr=0x%x)", type.addr(), attr.addr());
2014-02-27 19:25:32 +01:00
return vdecQueryAttr(type->codecType, type->profileLevel, type->codecSpecificInfo_addr, attr);
}
2014-10-11 19:20:01 +02:00
int cellVdecOpen(vm::ptr<const CellVdecType> type, vm::ptr<const CellVdecResource> res, vm::ptr<const CellVdecCb> cb, vm::ptr<u32> handle)
{
cellVdec->Warning("cellVdecOpen(type_addr=0x%x, res_addr=0x%x, cb_addr=0x%x, handle_addr=0x%x)",
2014-09-02 03:05:13 +02:00
type.addr(), res.addr(), cb.addr(), handle.addr());
2014-02-27 19:25:32 +01:00
*handle = vdecOpen(new VideoDecoder(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc.to_le(), cb->cbArg));
2014-02-27 19:25:32 +01:00
return CELL_OK;
}
2014-10-11 19:20:01 +02:00
int cellVdecOpenEx(vm::ptr<const CellVdecTypeEx> type, vm::ptr<const CellVdecResourceEx> res, vm::ptr<const CellVdecCb> cb, vm::ptr<u32> handle)
{
cellVdec->Warning("cellVdecOpenEx(type_addr=0x%x, res_addr=0x%x, cb_addr=0x%x, handle_addr=0x%x)",
2014-09-02 03:05:13 +02:00
type.addr(), res.addr(), cb.addr(), handle.addr());
2014-02-27 19:25:32 +01:00
*handle = vdecOpen(new VideoDecoder(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc.to_le(), cb->cbArg));
2014-02-27 19:25:32 +01:00
return CELL_OK;
}
int cellVdecClose(u32 handle)
{
cellVdec->Warning("cellVdecClose(handle=%d)", handle);
2014-02-27 19:25:32 +01:00
2014-12-24 00:38:13 +01:00
std::shared_ptr<VideoDecoder> vdec;
2014-02-27 19:25:32 +01:00
if (!Emu.GetIdManager().GetIDData(handle, vdec))
{
return CELL_VDEC_ERROR_ARG;
}
2014-10-18 00:20:03 +02:00
vdec->is_closed = true;
2014-12-24 23:24:17 +01:00
vdec->job.try_push(VdecTask(vdecClose));
2014-02-27 19:25:32 +01:00
2014-06-29 05:21:57 +02:00
while (!vdec->is_finished)
2014-02-27 19:25:32 +01:00
{
if (Emu.IsStopped())
{
2014-08-23 22:40:04 +02:00
cellVdec->Warning("cellVdecClose(%d) aborted", handle);
2014-02-27 19:25:32 +01:00
break;
}
2014-12-23 00:31:11 +01:00
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
2014-02-27 19:25:32 +01:00
}
if (vdec->vdecCb) Emu.GetCPU().RemoveThread(vdec->vdecCb->GetId());
2014-02-27 19:25:32 +01:00
Emu.GetIdManager().RemoveID(handle);
return CELL_OK;
}
int cellVdecStartSeq(u32 handle)
{
cellVdec->Log("cellVdecStartSeq(handle=%d)", handle);
2014-02-27 19:25:32 +01:00
2014-12-24 00:38:13 +01:00
std::shared_ptr<VideoDecoder> vdec;
2014-02-27 19:25:32 +01:00
if (!Emu.GetIdManager().GetIDData(handle, vdec))
{
return CELL_VDEC_ERROR_ARG;
}
2014-12-24 23:24:17 +01:00
vdec->job.push(VdecTask(vdecStartSeq), &vdec->is_closed);
return CELL_OK;
}
int cellVdecEndSeq(u32 handle)
{
cellVdec->Warning("cellVdecEndSeq(handle=%d)", handle);
2014-02-27 19:25:32 +01:00
2014-12-24 00:38:13 +01:00
std::shared_ptr<VideoDecoder> vdec;
2014-02-27 19:25:32 +01:00
if (!Emu.GetIdManager().GetIDData(handle, vdec))
{
return CELL_VDEC_ERROR_ARG;
}
2014-12-24 23:24:17 +01:00
vdec->job.push(VdecTask(vdecEndSeq), &vdec->is_closed);
return CELL_OK;
}
int cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, vm::ptr<const CellVdecAuInfo> auInfo)
{
2014-09-02 03:05:13 +02:00
cellVdec->Log("cellVdecDecodeAu(handle=%d, mode=0x%x, auInfo_addr=0x%x)", handle, mode, auInfo.addr());
2014-02-27 19:25:32 +01:00
2014-12-24 00:38:13 +01:00
std::shared_ptr<VideoDecoder> vdec;
2014-02-27 19:25:32 +01:00
if (!Emu.GetIdManager().GetIDData(handle, vdec))
{
return CELL_VDEC_ERROR_ARG;
}
// TODO: check info
VdecTask task(vdecDecodeAu);
task.mode = mode;
task.addr = auInfo->startAddr;
task.size = auInfo->size;
task.dts = (u64)auInfo->dts.lower | ((u64)auInfo->dts.upper << 32);
task.pts = (u64)auInfo->pts.lower | ((u64)auInfo->pts.upper << 32);
task.userData = auInfo->userData;
task.specData = auInfo->codecSpecificData;
2014-12-24 23:24:17 +01:00
vdec->job.push(task, &vdec->is_closed);
return CELL_OK;
}
int cellVdecGetPicture(u32 handle, vm::ptr<const CellVdecPicFormat> format, vm::ptr<u8> outBuff)
{
cellVdec->Log("cellVdecGetPicture(handle=%d, format_addr=0x%x, outBuff_addr=0x%x)", handle, format.addr(), outBuff.addr());
2014-03-01 09:38:50 +01:00
2014-12-24 00:38:13 +01:00
std::shared_ptr<VideoDecoder> vdec;
2014-03-01 09:38:50 +01:00
if (!Emu.GetIdManager().GetIDData(handle, vdec))
{
return CELL_VDEC_ERROR_ARG;
}
2014-10-18 19:00:21 +02:00
VdecFrame vf;
2014-12-24 23:24:17 +01:00
if (!vdec->frames.try_pop(vf))
2014-03-01 09:38:50 +01:00
{
2014-10-23 00:53:44 +02:00
//std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
2014-03-01 09:38:50 +01:00
return CELL_VDEC_ERROR_EMPTY;
}
2014-10-18 19:00:21 +02:00
if (!vf.data)
{
// hack
return CELL_OK;
}
if (outBuff)
2014-03-01 09:38:50 +01:00
{
2015-01-11 13:13:18 +01:00
const u32 buf_size = align(av_image_get_buffer_size(vdec->ctx->pix_fmt, vdec->ctx->width, vdec->ctx->height, 1), 128);
2014-03-03 00:02:42 +01:00
2014-03-01 09:38:50 +01:00
if (format->formatType != CELL_VDEC_PICFMT_YUV420_PLANAR)
{
cellVdec->Todo("cellVdecGetPicture: unknown formatType(%d)", (u32)format->formatType);
2014-03-01 09:38:50 +01:00
return CELL_OK;
}
2014-03-03 00:02:42 +01:00
2014-03-01 09:38:50 +01:00
if (format->colorMatrixType != CELL_VDEC_COLOR_MATRIX_TYPE_BT709)
{
cellVdec->Todo("cellVdecGetPicture: unknown colorMatrixType(%d)", (u32)format->colorMatrixType);
2014-03-01 09:38:50 +01:00
return CELL_OK;
}
2014-03-03 00:02:42 +01:00
AVFrame& frame = *vf.data;
2014-03-01 09:38:50 +01:00
// TODO: zero padding bytes
int err = av_image_copy_to_buffer(outBuff.get_ptr(), buf_size, frame.data, frame.linesize, vdec->ctx->pix_fmt, frame.width, frame.height, 1);
2014-03-01 09:38:50 +01:00
if (err < 0)
{
2014-12-12 01:21:34 +01:00
cellVdec->Error("cellVdecGetPicture: av_image_copy_to_buffer failed (err=0x%x)", err);
2014-03-01 09:38:50 +01:00
Emu.Pause();
}
}
2014-10-18 19:00:21 +02:00
av_frame_unref(vf.data);
av_frame_free(&vf.data);
return CELL_OK;
}
2014-10-11 19:20:01 +02:00
int cellVdecGetPicItem(u32 handle, vm::ptr<u32> picItem_ptr)
{
2014-09-01 02:51:48 +02:00
cellVdec->Log("cellVdecGetPicItem(handle=%d, picItem_ptr_addr=0x%x)", handle, picItem_ptr.addr());
2014-03-01 09:38:50 +01:00
2014-12-24 00:38:13 +01:00
std::shared_ptr<VideoDecoder> vdec;
2014-03-01 09:38:50 +01:00
if (!Emu.GetIdManager().GetIDData(handle, vdec))
{
return CELL_VDEC_ERROR_ARG;
}
2014-10-18 19:00:21 +02:00
VdecFrame vf;
2014-12-24 23:24:17 +01:00
if (!vdec->frames.try_peek(vf))
2014-03-01 09:38:50 +01:00
{
2014-10-23 00:53:44 +02:00
//std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
2014-03-01 09:38:50 +01:00
return CELL_VDEC_ERROR_EMPTY;
}
2014-03-03 00:02:42 +01:00
AVFrame& frame = *vf.data;
2014-09-02 03:05:13 +02:00
auto info = vm::ptr<CellVdecPicItem>::make(vdec->memAddr + vdec->memBias);
vdec->memBias += 512;
if (vdec->memBias + 512 > vdec->memSize)
{
vdec->memBias = 0;
}
2014-03-01 09:38:50 +01:00
info->codecType = vdec->type;
info->startAddr = 0x00000123; // invalid value (no address for picture)
2015-01-11 13:13:18 +01:00
info->size = align(av_image_get_buffer_size(vdec->ctx->pix_fmt, vdec->ctx->width, vdec->ctx->height, 1), 128);
2014-03-01 09:38:50 +01:00
info->auNum = 1;
2014-08-30 22:41:01 +02:00
info->auPts[0].lower = (u32)vf.pts;
2014-03-03 00:02:42 +01:00
info->auPts[0].upper = vf.pts >> 32;
2014-12-12 01:21:34 +01:00
info->auPts[1].lower = (u32)CODEC_TS_INVALID;
info->auPts[1].upper = (u32)CODEC_TS_INVALID;
2014-08-30 22:41:01 +02:00
info->auDts[0].lower = (u32)vf.dts;
2014-03-03 00:02:42 +01:00
info->auDts[0].upper = vf.dts >> 32;
2014-12-12 01:21:34 +01:00
info->auDts[1].lower = (u32)CODEC_TS_INVALID;
info->auDts[1].upper = (u32)CODEC_TS_INVALID;
2014-03-03 00:02:42 +01:00
info->auUserData[0] = vf.userdata;
2014-03-01 09:38:50 +01:00
info->auUserData[1] = 0;
info->status = CELL_OK;
info->attr = CELL_VDEC_PICITEM_ATTR_NORMAL;
2014-09-02 03:05:13 +02:00
info->picInfo_addr = info.addr() + sizeof(CellVdecPicItem);
2014-03-01 09:38:50 +01:00
2014-12-09 17:13:03 +01:00
if (vdec->type == CELL_VDEC_CODEC_TYPE_AVC)
{
auto avc = vm::ptr<CellVdecAvcInfo>::make(info.addr() + sizeof(CellVdecPicItem));
avc->horizontalSize = frame.width;
avc->verticalSize = frame.height;
switch (frame.pict_type)
{
2014-12-09 17:13:03 +01:00
case AV_PICTURE_TYPE_I: avc->pictureType[0] = CELL_VDEC_AVC_PCT_I; break;
case AV_PICTURE_TYPE_P: avc->pictureType[0] = CELL_VDEC_AVC_PCT_P; break;
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);
}
2014-12-09 17:13:03 +01:00
avc->pictureType[1] = CELL_VDEC_AVC_PCT_UNKNOWN; // ???
avc->idrPictureFlag = false; // ???
avc->aspect_ratio_idc = CELL_VDEC_AVC_ARI_SAR_UNSPECIFIED; // ???
avc->sar_height = 0;
avc->sar_width = 0;
avc->pic_struct = CELL_VDEC_AVC_PSTR_FRAME; // ???
avc->picOrderCount[0] = 0; // ???
avc->picOrderCount[1] = 0;
avc->vui_parameters_present_flag = true; // ???
avc->frame_mbs_only_flag = true; // ??? progressive
avc->video_signal_type_present_flag = true; // ???
avc->video_format = CELL_VDEC_AVC_VF_COMPONENT; // ???
avc->video_full_range_flag = false; // ???
avc->colour_description_present_flag = true;
avc->colour_primaries = CELL_VDEC_AVC_CP_ITU_R_BT_709_5; // ???
avc->transfer_characteristics = CELL_VDEC_AVC_TC_ITU_R_BT_709_5;
avc->matrix_coefficients = CELL_VDEC_AVC_MXC_ITU_R_BT_709_5; // important
avc->timing_info_present_flag = true;
switch (vf.frc)
{
2014-12-09 17:13:03 +01:00
case CELL_VDEC_FRC_24000DIV1001: avc->frameRateCode = CELL_VDEC_AVC_FRC_24000DIV1001; break;
case CELL_VDEC_FRC_24: avc->frameRateCode = CELL_VDEC_AVC_FRC_24; break;
case CELL_VDEC_FRC_25: avc->frameRateCode = CELL_VDEC_AVC_FRC_25; break;
case CELL_VDEC_FRC_30000DIV1001: avc->frameRateCode = CELL_VDEC_AVC_FRC_30000DIV1001; break;
case CELL_VDEC_FRC_30: avc->frameRateCode = CELL_VDEC_AVC_FRC_30; break;
case CELL_VDEC_FRC_50: avc->frameRateCode = CELL_VDEC_AVC_FRC_50; break;
case CELL_VDEC_FRC_60000DIV1001: avc->frameRateCode = CELL_VDEC_AVC_FRC_60000DIV1001; break;
case CELL_VDEC_FRC_60: avc->frameRateCode = CELL_VDEC_AVC_FRC_60; break;
2014-12-12 01:21:34 +01:00
default: cellVdec->Error("cellVdecGetPicItem(AVC): unknown frc value (0x%x)", vf.frc);
}
2014-12-09 17:13:03 +01:00
avc->fixed_frame_rate_flag = true;
avc->low_delay_hrd_flag = true; // ???
avc->entropy_coding_mode_flag = true; // ???
avc->nalUnitPresentFlags = 0; // ???
avc->ccDataLength[0] = 0;
avc->ccDataLength[1] = 0;
avc->reserved[0] = 0;
avc->reserved[1] = 0;
}
else if (vdec->type == CELL_VDEC_CODEC_TYPE_DIVX)
{
auto dvx = vm::ptr<CellVdecDivxInfo>::make(info.addr() + sizeof(CellVdecPicItem));
switch (frame.pict_type)
{
2014-12-09 17:13:03 +01:00
case AV_PICTURE_TYPE_I: dvx->pictureType = CELL_VDEC_DIVX_VCT_I; break;
case AV_PICTURE_TYPE_P: dvx->pictureType = CELL_VDEC_DIVX_VCT_P; break;
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; // ???
dvx->parHeight = 0;
dvx->parWidth = 0;
dvx->colourDescription = false; // ???
dvx->colourPrimaries = CELL_VDEC_DIVX_CP_ITU_R_BT_709; // ???
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;
case CELL_VDEC_FRC_24: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_24; break;
case CELL_VDEC_FRC_25: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_25; break;
case CELL_VDEC_FRC_30000DIV1001: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_30000DIV1001; break;
case CELL_VDEC_FRC_30: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_30; break;
case CELL_VDEC_FRC_50: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_50; break;
case CELL_VDEC_FRC_60000DIV1001: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_60000DIV1001; break;
case CELL_VDEC_FRC_60: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_60; break;
default: cellVdec->Error("cellVdecGetPicItem(DivX): unknown frc value (0x%x)", vf.frc);
}
}
2014-12-09 17:13:03 +01:00
else if (vdec->type == CELL_VDEC_CODEC_TYPE_MPEG2)
{
2014-12-09 17:13:03 +01:00
auto mp2 = vm::ptr<CellVdecMpeg2Info>::make(info.addr() + sizeof(CellVdecPicItem));
cellVdec->Todo("cellVdecGetPicItem(MPEG2)");
Emu.Pause();
}
2014-03-01 09:38:50 +01:00
2014-12-09 17:13:03 +01:00
*picItem_ptr = info.addr();
return CELL_OK;
}
int cellVdecSetFrameRate(u32 handle, CellVdecFrameRate frc)
{
cellVdec->Log("cellVdecSetFrameRate(handle=%d, frc=0x%x)", handle, frc);
2014-02-27 19:25:32 +01:00
2014-12-24 00:38:13 +01:00
std::shared_ptr<VideoDecoder> vdec;
2014-02-27 19:25:32 +01:00
if (!Emu.GetIdManager().GetIDData(handle, vdec))
{
return CELL_VDEC_ERROR_ARG;
}
// TODO: check frc value and set frame rate
VdecTask task(vdecSetFrameRate);
task.frc = frc;
2014-12-24 23:24:17 +01:00
vdec->job.push(task, &vdec->is_closed);
return CELL_OK;
}
void cellVdec_init(Module *pxThis)
{
cellVdec = pxThis;
cellVdec->AddFunc(0xff6f6ebe, cellVdecQueryAttr);
cellVdec->AddFunc(0xc982a84a, cellVdecQueryAttrEx);
cellVdec->AddFunc(0xb6bbcd5d, cellVdecOpen);
cellVdec->AddFunc(0x0053e2d8, cellVdecOpenEx);
cellVdec->AddFunc(0x16698e83, cellVdecClose);
cellVdec->AddFunc(0xc757c2aa, cellVdecStartSeq);
cellVdec->AddFunc(0x824433f0, cellVdecEndSeq);
cellVdec->AddFunc(0x2bf4ddd2, cellVdecDecodeAu);
cellVdec->AddFunc(0x807c861a, cellVdecGetPicture);
cellVdec->AddFunc(0x17c702b9, cellVdecGetPicItem);
cellVdec->AddFunc(0xe13ef6fc, cellVdecSetFrameRate);
}