mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
Use ffmpeg for Atrac3+ decoding during video playback as well.
This commit is contained in:
parent
3cefa1ad79
commit
be40289d6c
@ -911,6 +911,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||
Core/HLE/sceNp.h
|
||||
Core/HLE/scePauth.cpp
|
||||
Core/HLE/scePauth.h
|
||||
Core/HW/SimpleAT3Dec.cpp
|
||||
Core/HW/SimpleAT3Dec.h
|
||||
Core/HW/atrac3plus.cpp
|
||||
Core/HW/atrac3plus.h
|
||||
Core/HW/AsyncIOManager.cpp
|
||||
|
@ -249,6 +249,7 @@
|
||||
<ClCompile Include="HLE\sceVaudio.cpp" />
|
||||
<ClCompile Include="HLE\__sceAudio.cpp" />
|
||||
<ClCompile Include="Host.cpp" />
|
||||
<ClCompile Include="HW\SimpleAT3Dec.cpp" />
|
||||
<ClCompile Include="HW\atrac3plus.cpp" />
|
||||
<ClCompile Include="HW\MediaEngine.cpp" />
|
||||
<ClCompile Include="HW\MemoryStick.cpp" />
|
||||
@ -490,6 +491,7 @@
|
||||
<ClInclude Include="HLE\sceVaudio.h" />
|
||||
<ClInclude Include="HLE\__sceAudio.h" />
|
||||
<ClInclude Include="Host.h" />
|
||||
<ClInclude Include="HW\SimpleAT3Dec.h" />
|
||||
<ClInclude Include="HW\atrac3plus.h" />
|
||||
<ClInclude Include="HW\MediaEngine.h" />
|
||||
<ClInclude Include="HW\MpegDemux.h" />
|
||||
|
@ -430,6 +430,9 @@
|
||||
<ClCompile Include="Cwcheat.cpp">
|
||||
<Filter>Core</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\SimpleAT3Dec.cpp">
|
||||
<Filter>HW</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\atrac3plus.cpp">
|
||||
<Filter>HW</Filter>
|
||||
</ClCompile>
|
||||
@ -855,6 +858,9 @@
|
||||
<ClInclude Include="Cwcheat.h">
|
||||
<Filter>Core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\SimpleAT3Dec.h">
|
||||
<Filter>HW</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\atrac3plus.h">
|
||||
<Filter>HW</Filter>
|
||||
</ClInclude>
|
||||
|
@ -253,6 +253,7 @@ struct Atrac {
|
||||
SwrContext *pSwrCtx;
|
||||
AVFrame *pFrame;
|
||||
int audio_stream_index;
|
||||
|
||||
void ReleaseFFMPEGContext() {
|
||||
if (pFrame)
|
||||
av_free(pFrame);
|
||||
@ -313,7 +314,6 @@ void __AtracInit() {
|
||||
avcodec_register_all();
|
||||
av_register_all();
|
||||
#endif // USE_FFMPEG
|
||||
|
||||
}
|
||||
|
||||
void __AtracDoState(PointerWrap &p) {
|
||||
@ -340,7 +340,6 @@ void __AtracShutdown() {
|
||||
delete atracIDs[i];
|
||||
atracIDs[i] = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Atrac *getAtrac(int atracID) {
|
||||
@ -954,7 +953,7 @@ u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFirstBuf,
|
||||
}
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
int _AtracReadbuffer(void *opaque, uint8_t *buf, int buf_size)
|
||||
static int _AtracReadbuffer(void *opaque, uint8_t *buf, int buf_size)
|
||||
{
|
||||
Atrac *atrac = (Atrac *)opaque;
|
||||
if (atrac->decodePos > atrac->first.filesize)
|
||||
@ -967,7 +966,7 @@ int _AtracReadbuffer(void *opaque, uint8_t *buf, int buf_size)
|
||||
return size;
|
||||
}
|
||||
|
||||
int64_t _AtracSeekbuffer(void *opaque, int64_t offset, int whence)
|
||||
static int64_t _AtracSeekbuffer(void *opaque, int64_t offset, int whence)
|
||||
{
|
||||
Atrac *atrac = (Atrac*)opaque;
|
||||
if (offset > atrac->first.filesize)
|
||||
@ -1656,7 +1655,8 @@ int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesConsumedA
|
||||
int forceseekSample = 0x200000;
|
||||
atrac->SeekToSample(forceseekSample);
|
||||
atrac->SeekToSample(atrac->currentSample);
|
||||
AVPacket packet;
|
||||
AVPacket packet = {0};
|
||||
av_init_packet(&packet);
|
||||
int got_frame, avret;
|
||||
while (av_read_frame(atrac->pFormatCtx, &packet) >= 0) {
|
||||
if (packet.stream_index == atrac->audio_stream_index) {
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
#include "Core/HW/atrac3plus.h"
|
||||
#include "Core/HW/SimpleAT3Dec.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -101,11 +101,11 @@ void ffmpeg_logger(void *, int level, const char *format, va_list va_args) {
|
||||
|
||||
// Let's color the log line appropriately.
|
||||
if (level <= AV_LOG_PANIC) {
|
||||
ERROR_LOG(ME, "%s", tmp);
|
||||
ERROR_LOG(ME, "FF: %s", tmp);
|
||||
} else if (level >= AV_LOG_VERBOSE) {
|
||||
DEBUG_LOG(ME, "%s", tmp);
|
||||
DEBUG_LOG(ME, "FF: %s", tmp);
|
||||
} else {
|
||||
INFO_LOG(ME, "%s", tmp);
|
||||
INFO_LOG(ME, "FF: %s", tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,7 +172,7 @@ void MediaEngine::closeMedia() {
|
||||
m_pFormatCtx = 0;
|
||||
m_pdata = 0;
|
||||
m_demux = 0;
|
||||
Atrac3plus_Decoder::CloseContext(&m_audioContext);
|
||||
AT3Close(&m_audioContext);
|
||||
m_isVideoEnd = false;
|
||||
m_noAudioData = false;
|
||||
}
|
||||
@ -243,12 +243,12 @@ bool MediaEngine::openContext() {
|
||||
m_pFormatCtx = avformat_alloc_context();
|
||||
m_pIOContext = avio_alloc_context(tempbuf, m_bufSize, 0, (void*)this, _MpegReadbuffer, NULL, 0);
|
||||
m_pFormatCtx->pb = m_pIOContext;
|
||||
|
||||
|
||||
// Open video file
|
||||
if(avformat_open_input((AVFormatContext**)&m_pFormatCtx, NULL, NULL, NULL) != 0)
|
||||
if (avformat_open_input((AVFormatContext**)&m_pFormatCtx, NULL, NULL, NULL) != 0)
|
||||
return false;
|
||||
|
||||
if(avformat_find_stream_info(m_pFormatCtx, NULL) < 0)
|
||||
if (avformat_find_stream_info(m_pFormatCtx, NULL) < 0)
|
||||
return false;
|
||||
|
||||
if (m_videoStream >= (int)m_pFormatCtx->nb_streams) {
|
||||
@ -270,19 +270,19 @@ bool MediaEngine::openContext() {
|
||||
|
||||
// Get a pointer to the codec context for the video stream
|
||||
m_pCodecCtx = m_pFormatCtx->streams[m_videoStream]->codec;
|
||||
|
||||
|
||||
// Find the decoder for the video stream
|
||||
AVCodec *pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id);
|
||||
if(pCodec == NULL)
|
||||
return false;
|
||||
|
||||
|
||||
// Open codec
|
||||
AVDictionary *optionsDict = 0;
|
||||
if(avcodec_open2(m_pCodecCtx, pCodec, &optionsDict)<0)
|
||||
return false; // Could not open codec
|
||||
|
||||
setVideoDim();
|
||||
m_audioContext = Atrac3plus_Decoder::OpenContext();
|
||||
m_audioContext = AT3Create();
|
||||
m_isVideoEnd = false;
|
||||
m_noAudioData = false;
|
||||
m_mpegheaderReadPos++;
|
||||
@ -627,8 +627,11 @@ int MediaEngine::getAudioSamples(u8* buffer) {
|
||||
}
|
||||
int outbytes = 0;
|
||||
|
||||
if(m_audioContext != NULL)
|
||||
Atrac3plus_Decoder::Decode(m_audioContext, audioFrame, frameSize, &outbytes, buffer);
|
||||
if (m_audioContext != NULL) {
|
||||
if (!AT3Decode(m_audioContext, audioFrame, frameSize, &outbytes, buffer)) {
|
||||
ERROR_LOG(ME, "AT3 decode failed during video playback");
|
||||
}
|
||||
}
|
||||
|
||||
if (headerCode1 == 0x24) {
|
||||
// it a mono atrac3plus, convert it to stereo
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include "ChunkFile.h"
|
||||
#include "Core/HW/MpegDemux.h"
|
||||
|
||||
struct SimpleAT3;
|
||||
|
||||
struct SwsContext;
|
||||
struct AVFrame;
|
||||
struct AVIOContext;
|
||||
@ -106,7 +108,7 @@ public: // TODO: Very little of this below should be public.
|
||||
BufferQueue *m_pdata;
|
||||
|
||||
MpegDemux *m_demux;
|
||||
void *m_audioContext;
|
||||
SimpleAT3 *m_audioContext;
|
||||
s64 m_audiopts;
|
||||
|
||||
s64 m_firstTimeStamp;
|
||||
|
223
Core/HW/SimpleAT3Dec.cpp
Normal file
223
Core/HW/SimpleAT3Dec.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
// Copyright (c) 2013- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
|
||||
// Urgh! Why is this needed?
|
||||
#ifdef ANDROID
|
||||
#ifndef UINT64_C
|
||||
#define UINT64_C(c) (c ## ULL)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavutil/samplefmt.h>
|
||||
}
|
||||
#endif // USE_FFMPEG
|
||||
|
||||
#include "Core/HW/SimpleAT3Dec.h"
|
||||
#include "Core/HW/MediaEngine.h"
|
||||
#include "Core/HW/BufferQueue.h"
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
|
||||
struct SimpleAT3 {
|
||||
public:
|
||||
SimpleAT3();
|
||||
~SimpleAT3();
|
||||
|
||||
bool Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbytes);
|
||||
|
||||
bool IsOK() const { return codec != 0; }
|
||||
|
||||
private:
|
||||
AVCodec *codec;
|
||||
AVCodecContext *codecCtx;
|
||||
SwrContext *swrCtx;
|
||||
|
||||
u8 *data_;
|
||||
int dataSize_;
|
||||
};
|
||||
|
||||
SimpleAT3::SimpleAT3()
|
||||
: codec(0),
|
||||
codecCtx(0),
|
||||
swrCtx(0),
|
||||
data_(0),
|
||||
dataSize_(0) {
|
||||
codec = avcodec_find_decoder(AV_CODEC_ID_ATRAC3P);
|
||||
if (!codec) {
|
||||
// Eh, we shouldn't even have managed to compile. But meh.
|
||||
ERROR_LOG(ME, "This version of FFMPEG does not support AV_CODEC_ID_ATRAC3P (Atrac3+). Update your submodule.");
|
||||
return;
|
||||
}
|
||||
|
||||
codecCtx = avcodec_alloc_context3(codec);
|
||||
if (!codecCtx) {
|
||||
ERROR_LOG(ME, "Failed to allocate a codec context");
|
||||
return;
|
||||
}
|
||||
|
||||
codecCtx->channels = 2;
|
||||
codecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
|
||||
AVDictionary *opts = 0;
|
||||
av_dict_set(&opts, "channels", "2", 0);
|
||||
av_dict_set(&opts, "sample_rate", "44100", 0);
|
||||
if (avcodec_open2(codecCtx, codec, &opts) < 0) {
|
||||
ERROR_LOG(ME, "Failed to open codec");
|
||||
return;
|
||||
}
|
||||
|
||||
av_dict_free(&opts);
|
||||
|
||||
// Initializing the sample rate convert. We only really use it to convert float output
|
||||
// into int.
|
||||
int wanted_channels = 2;
|
||||
int64_t wanted_channel_layout = av_get_default_channel_layout(wanted_channels);
|
||||
int64_t dec_channel_layout = av_get_default_channel_layout(2);
|
||||
|
||||
swrCtx = swr_alloc_set_opts(
|
||||
swrCtx,
|
||||
wanted_channel_layout,
|
||||
AV_SAMPLE_FMT_S16,
|
||||
codecCtx->sample_rate,
|
||||
dec_channel_layout,
|
||||
codecCtx->sample_fmt,
|
||||
codecCtx->sample_rate,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
if (!swrCtx || swr_init(swrCtx) < 0) {
|
||||
ERROR_LOG(ME, "swr_init: Failed to initialize the resampling context");
|
||||
avcodec_close(codecCtx);
|
||||
codec = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleAT3::~SimpleAT3() {
|
||||
if (codecCtx)
|
||||
avcodec_close(codecCtx);
|
||||
if (swrCtx)
|
||||
swr_free(&swrCtx);
|
||||
|
||||
codecCtx = 0;
|
||||
codec = 0;
|
||||
if (data_) {
|
||||
delete [] data_;
|
||||
dataSize_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Input is a single Atrac3+ packet.
|
||||
bool SimpleAT3::Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbytes) {
|
||||
#ifdef USE_FFMPEG
|
||||
AVPacket packet = {0};
|
||||
av_init_packet(&packet);
|
||||
packet.data = static_cast<uint8_t *>(inbuf);
|
||||
packet.size = inbytes;
|
||||
|
||||
AVFrame *decoded_frame = 0;
|
||||
|
||||
*outbytes = 0;
|
||||
|
||||
int got_frame = 0;
|
||||
if (!decoded_frame) {
|
||||
decoded_frame = avcodec_alloc_frame();
|
||||
} else {
|
||||
avcodec_get_frame_defaults(decoded_frame);
|
||||
}
|
||||
|
||||
int len = avcodec_decode_audio4(codecCtx, decoded_frame, &got_frame, &packet);
|
||||
if (len < 0) {
|
||||
ERROR_LOG(ME, "Error decoding Atrac3+ frame");
|
||||
// TODO: cleanup
|
||||
return false;
|
||||
}
|
||||
|
||||
if (got_frame) {
|
||||
int data_size = av_samples_get_buffer_size(
|
||||
NULL,
|
||||
codecCtx->channels,
|
||||
decoded_frame->nb_samples,
|
||||
codecCtx->sample_fmt, 1);
|
||||
|
||||
int numSamples = decoded_frame->nb_samples;
|
||||
u8 *out = outbuf;
|
||||
int swrRet = swr_convert(swrCtx, &out, numSamples,
|
||||
(const u8 **)decoded_frame->extended_data, numSamples);
|
||||
|
||||
if (swrRet < 0) {
|
||||
ERROR_LOG(ME, "swr_convert: Error while converting %d", swrRet);
|
||||
avcodec_free_frame(&decoded_frame);
|
||||
return false;
|
||||
}
|
||||
|
||||
outbytes += data_size;
|
||||
outbuf += data_size;
|
||||
}
|
||||
|
||||
if (decoded_frame)
|
||||
avcodec_free_frame(&decoded_frame);
|
||||
|
||||
return true;
|
||||
#else
|
||||
// Zero bytes output. No need to memset.
|
||||
*outbytes = 0;
|
||||
return true;
|
||||
#endif // USE_FFMPEG
|
||||
}
|
||||
|
||||
#endif // USE_FFMPEG
|
||||
|
||||
// "C" wrapper
|
||||
|
||||
SimpleAT3 *AT3Create() {
|
||||
#ifdef USE_FFMPEG
|
||||
avcodec_register_all();
|
||||
av_register_all();
|
||||
InitFFmpeg();
|
||||
|
||||
SimpleAT3 *at3 = new SimpleAT3();
|
||||
if (!at3->IsOK()) {
|
||||
delete at3;
|
||||
return 0;
|
||||
}
|
||||
return at3;
|
||||
#else
|
||||
return 0;
|
||||
#endif // USE_FFMPEG
|
||||
}
|
||||
|
||||
bool AT3Decode(SimpleAT3 *ctx, void* inbuf, int inbytes, int *outbytes, uint8_t *outbuf) {
|
||||
#ifdef USE_FFMPEG
|
||||
return ctx->Decode(inbuf, inbytes, outbuf, outbytes);
|
||||
#else
|
||||
*outbytes = 0;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AT3Close(SimpleAT3 **ctx) {
|
||||
#ifdef USE_FFMPEG
|
||||
delete *ctx;
|
||||
*ctx = 0;
|
||||
#endif // USE_FFMPEG
|
||||
}
|
37
Core/HW/SimpleAT3Dec.h
Normal file
37
Core/HW/SimpleAT3Dec.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2013- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
// Wraps FFMPEG in a nice interface that's drop-in compatible with
|
||||
// the old one. Decodes packet by packet - does NOT demux. That's done by
|
||||
// MpegDemux. Only decodes Atrac3+, not regular Atrac3.
|
||||
//
|
||||
// Based on http://ffmpeg.org/doxygen/trunk/doc_2examples_2decoding_encoding_8c-example.html#_a13
|
||||
|
||||
// Ideally, Maxim's AT3plug decoder would be available as a standalone library
|
||||
// that we could link, as that would be totally sufficient for the use case here.
|
||||
// However, it will be maintained as a part of FFMPEG so that's the way we'll go
|
||||
// for simplicity and sanity.
|
||||
|
||||
struct SimpleAT3;
|
||||
|
||||
SimpleAT3 *AT3Create();
|
||||
bool AT3Decode(SimpleAT3 *ctx, void* inbuf, int inbytes, int *outbytes, uint8_t* outbuf);
|
||||
void AT3Close(SimpleAT3 **ctx);
|
@ -1,5 +1,4 @@
|
||||
#ifndef _ATRAC3PLUS_DECODER_
|
||||
#define _ATRAC3PLUS_DECODER_
|
||||
#pragma once
|
||||
|
||||
#include "Core/HW/BufferQueue.h"
|
||||
|
||||
@ -19,5 +18,3 @@ namespace Atrac3plus_Decoder {
|
||||
int CloseContext(Context *context);
|
||||
bool Decode(Context context, void* inbuf, int inbytes, int *outbytes, void* outbuf);
|
||||
}
|
||||
|
||||
#endif // _ATRAC3PLUS_DECODER_
|
||||
|
@ -171,6 +171,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/Core/ELF/PBPReader.cpp \
|
||||
$(SRC)/Core/ELF/PrxDecrypter.cpp \
|
||||
$(SRC)/Core/ELF/ParamSFO.cpp \
|
||||
$(SRC)/Core/HW/SimpleAT3Dec.cpp \
|
||||
$(SRC)/Core/HW/atrac3plus.cpp \
|
||||
$(SRC)/Core/HW/AsyncIOManager.cpp \
|
||||
$(SRC)/Core/HW/MemoryStick.cpp \
|
||||
|
Loading…
Reference in New Issue
Block a user