int adecRead(void* opaque, u8* buf, int buf_size) { AudioDecoder& adec = *(AudioDecoder*)opaque; int res = 0; next: if (adecIsAtracX(adec.type) && adec.reader.has_ats) { u8 code1 = vm::read8(adec.reader.addr + 2); u8 code2 = vm::read8(adec.reader.addr + 3); adec.ch_cfg = (code1 >> 2) & 0x7; adec.frame_size = ((((u32)code1 & 0x3) << 8) | (u32)code2) * 8 + 8; adec.sample_rate = at3freq[code1 >> 5]; adec.reader.size -= 8; adec.reader.addr += 8; adec.reader.has_ats = false; }
virtual void cpu_task() override { while (true) { if (Emu.IsStopped() || is_closed) { break; } if (!job.pop(task, &is_closed)) { break; } switch (task.type) { case adecStartSeq: { // TODO: reset data cellAdec.warning("adecStartSeq:"); reader.addr = 0; reader.size = 0; reader.init = false; reader.has_ats = false; just_started = true; if (adecIsAtracX(type)) { ch_cfg = task.at3p.channel_config; ch_out = task.at3p.channels; frame_size = task.at3p.frame_size; sample_rate = task.at3p.sample_rate; use_ats_headers = task.at3p.ats_header == 1; } break; } case adecEndSeq: { // TODO: finalize cellAdec.warning("adecEndSeq:"); cbFunc(*this, id, CELL_ADEC_MSG_TYPE_SEQDONE, CELL_OK, cbArg); just_finished = true; break; } case adecDecodeAu: { int err = 0; reader.addr = task.au.addr; reader.size = task.au.size; reader.has_ats = use_ats_headers; //LOG_NOTICE(HLE, "Audio AU: size = 0x%x, pts = 0x%llx", task.au.size, task.au.pts); if (just_started) { first_pts = task.au.pts; last_pts = task.au.pts; if (adecIsAtracX(type)) last_pts -= 0x10000; // hack } struct AVPacketHolder : AVPacket { AVPacketHolder(u32 size) { av_init_packet(this); if (size) { data = (u8*)av_calloc(1, size + FF_INPUT_BUFFER_PADDING_SIZE); this->size = size + FF_INPUT_BUFFER_PADDING_SIZE; } else { data = NULL; size = 0; } } ~AVPacketHolder() { av_free(data); } } au(0); if (just_started && just_finished) { avcodec_flush_buffers(ctx); reader.init = true; // wrong just_finished = false; just_started = false; } else if (just_started) // deferred initialization { AVDictionary* opts = nullptr; av_dict_set(&opts, "probesize", "96", 0); err = avformat_open_input(&fmt, NULL, input_format, &opts); if (err || opts) { throw EXCEPTION("avformat_open_input() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0); } //err = avformat_find_stream_info(fmt, NULL); //if (err || !fmt->nb_streams) //{ // ADEC_ERROR("adecDecodeAu: avformat_find_stream_info() failed (err=0x%x, nb_streams=%d)", err, fmt->nb_streams); //} if (!avformat_new_stream(fmt, codec)) { throw EXCEPTION("avformat_new_stream() failed"); } ctx = 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(ctx, codec, &opts); } if (err || opts) { throw EXCEPTION("avcodec_open2() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0); } just_started = false; } bool last_frame = false; while (true) { if (Emu.IsStopped() || is_closed) { if (Emu.IsStopped()) cellAdec.warning("adecDecodeAu: aborted"); break; } last_frame = av_read_frame(fmt, &au) < 0; if (last_frame) { //break; av_free(au.data); au.data = NULL; au.size = 0; } struct AdecFrameHolder : AdecFrame { AdecFrameHolder() { data = av_frame_alloc(); } ~AdecFrameHolder() { if (data) { av_frame_unref(data); av_frame_free(&data); } } } frame; if (!frame.data) { throw EXCEPTION("av_frame_alloc() failed"); } int got_frame = 0; int decode = avcodec_decode_audio4(ctx, frame.data, &got_frame, &au); if (decode <= 0) { if (decode < 0) { cellAdec.error("adecDecodeAu: AU decoding error(0x%x)", decode); } if (!got_frame && reader.size == 0) break; } if (got_frame) { //u64 ts = av_frame_get_best_effort_timestamp(frame.data); //if (ts != AV_NOPTS_VALUE) //{ // frame.pts = ts/* - first_pts*/; // last_pts = frame.pts; //} last_pts += ((u64)frame.data->nb_samples) * 90000 / frame.data->sample_rate; frame.pts = last_pts; s32 nbps = av_get_bytes_per_sample((AVSampleFormat)frame.data->format); switch (frame.data->format) { case AV_SAMPLE_FMT_FLTP: break; case AV_SAMPLE_FMT_S16P: break; default: { throw EXCEPTION("Unsupported frame format(%d)", frame.data->format); } } frame.auAddr = task.au.addr; frame.auSize = task.au.size; frame.userdata = task.au.userdata; frame.size = frame.data->nb_samples * frame.data->channels * nbps; //LOG_NOTICE(HLE, "got audio frame (pts=0x%llx, nb_samples=%d, ch=%d, sample_rate=%d, nbps=%d)", //frame.pts, frame.data->nb_samples, frame.data->channels, frame.data->sample_rate, nbps); if (frames.push(frame, &is_closed)) { frame.data = nullptr; // to prevent destruction cbFunc(*this, id, CELL_ADEC_MSG_TYPE_PCMOUT, CELL_OK, cbArg); } } } cbFunc(*this, id, CELL_ADEC_MSG_TYPE_AUDONE, task.au.auInfo_addr, cbArg); break; } case adecClose: { break; } default: { throw EXCEPTION("Unknown task(%d)", (u32)task.type); } } } is_finished = true; }