Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

not Smooth with other processor for Android devices #1

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/audio_core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ elseif(ENABLE_FDK)
target_compile_definitions(audio_core PUBLIC HAVE_FDK)
endif()

if(ANDROID)
target_sources(audio_core PRIVATE
hle/mediandk_decoder.cpp
hle/mediandk_decoder.h
)
target_link_libraries(audio_core PRIVATE mediandk)
endif()

if(SDL2_FOUND)
target_link_libraries(audio_core PRIVATE SDL2)
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
Expand All @@ -80,4 +88,3 @@ if(ENABLE_CUBEB)
target_link_libraries(audio_core PRIVATE cubeb)
target_compile_definitions(audio_core PUBLIC HAVE_CUBEB)
endif()

4 changes: 4 additions & 0 deletions src/audio_core/hle/hle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "audio_core/hle/wmf_decoder.h"
#elif HAVE_FFMPEG
#include "audio_core/hle/ffmpeg_decoder.h"
#elif ANDROID
#include "audio_core/hle/mediandk_decoder.h"
#elif HAVE_FDK
#include "audio_core/hle/fdk_decoder.h"
#endif
Expand Down Expand Up @@ -99,6 +101,8 @@ DspHle::Impl::Impl(DspHle& parent_, Memory::MemorySystem& memory) : parent(paren
decoder = std::make_unique<HLE::WMFDecoder>(memory);
#elif defined(HAVE_FFMPEG)
decoder = std::make_unique<HLE::FFMPEGDecoder>(memory);
#elif ANDROID
decoder = std::make_unique<HLE::MediaNDKDecoder>(memory);
#elif defined(HAVE_FDK)
decoder = std::make_unique<HLE::FDKDecoder>(memory);
#else
Expand Down
241 changes: 241 additions & 0 deletions src/audio_core/hle/mediandk_decoder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include <media/NdkMediaCodec.h>
#include <media/NdkMediaError.h>
#include <media/NdkMediaFormat.h>

#include <memory>
#include <vector>

#include "audio_core/hle/adts.h"
#include "audio_core/hle/mediandk_decoder.h"

namespace AudioCore::HLE {

struct AMediaCodecRelease {
void operator()(AMediaCodec* codec) const {
AMediaCodec_stop(codec);
AMediaCodec_delete(codec);
};
};

class MediaNDKDecoder::Impl {
public:
explicit Impl(Memory::MemorySystem& memory);
~Impl();
std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request);

bool SetMediaType(const ADTSData& adts_data);

private:
std::optional<BinaryResponse> Initalize(const BinaryRequest& request);
std::optional<BinaryResponse> Decode(const BinaryRequest& request);

Memory::MemorySystem& mMemory;
std::unique_ptr<AMediaCodec, AMediaCodecRelease> mDecoder;
// default: 2 channles, 48000 samplerate
ADTSData mADTSData{/* MPEG2 */ false, /*profile*/ 2, /*channels*/ 2,
/*channel_idx*/ 2, /*framecount*/ 0, /*samplerate_idx*/ 3,
/*length*/ 0, /*samplerate*/ 48000};
};

MediaNDKDecoder::Impl::Impl(Memory::MemorySystem& memory) : mMemory(memory) {
SetMediaType(mADTSData);
}

MediaNDKDecoder::Impl::~Impl() = default;

std::optional<BinaryResponse> MediaNDKDecoder::Impl::Initalize(const BinaryRequest& request) {
BinaryResponse response;
std::memcpy(&response, &request, sizeof(response));
response.unknown1 = 0x0;
return response;
}

bool MediaNDKDecoder::Impl::SetMediaType(const ADTSData& adts_data) {
const char* mime = "audio/mp4a-latm";
if (mDecoder && mADTSData.profile == adts_data.profile &&
mADTSData.channel_idx == adts_data.channel_idx &&
mADTSData.samplerate_idx == adts_data.samplerate_idx) {
return true;
}
mDecoder.reset(AMediaCodec_createDecoderByType(mime));
if (mDecoder == nullptr) {
return false;
}

u8 csd_0[2];
csd_0[0] = static_cast<u8>((adts_data.profile << 3) | (adts_data.samplerate_idx >> 1));
csd_0[1] =
static_cast<u8>(((adts_data.samplerate_idx << 7) & 0x80) | (adts_data.channel_idx << 3));
AMediaFormat* format = AMediaFormat_new();
AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mime);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, adts_data.samplerate);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, adts_data.channels);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_IS_ADTS, 1);
AMediaFormat_setBuffer(format, "csd-0", csd_0, sizeof(csd_0));

media_status_t status = AMediaCodec_configure(mDecoder.get(), format, NULL, NULL, 0);
if (status != AMEDIA_OK) {
AMediaFormat_delete(format);
mDecoder.reset();
return false;
}

status = AMediaCodec_start(mDecoder.get());
if (status != AMEDIA_OK) {
AMediaFormat_delete(format);
mDecoder.reset();
return false;
}

AMediaFormat_delete(format);
mADTSData = adts_data;
return true;
}

std::optional<BinaryResponse> MediaNDKDecoder::Impl::ProcessRequest(const BinaryRequest& request) {
if (request.codec != DecoderCodec::AAC) {
LOG_ERROR(Audio_DSP, "AAC Decoder cannot handle such codec: {}",
static_cast<u16>(request.codec));
return {};
}

switch (request.cmd) {
case DecoderCommand::Init: {
return Initalize(request);
}
case DecoderCommand::Decode: {
return Decode(request);
}
case DecoderCommand::Unknown: {
BinaryResponse response;
std::memcpy(&response, &request, sizeof(response));
response.unknown1 = 0x0;
return response;
}
default:
LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", static_cast<u16>(request.cmd));
return {};
}
}

std::optional<BinaryResponse> MediaNDKDecoder::Impl::Decode(const BinaryRequest& request) {
BinaryResponse response;
response.codec = request.codec;
response.cmd = request.cmd;
response.size = request.size;
response.num_samples = 1024;

if (request.src_addr < Memory::FCRAM_PADDR ||
request.src_addr + request.size > Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) {
LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", request.src_addr);
return response;
}

u8* data = mMemory.GetFCRAMPointer(request.src_addr - Memory::FCRAM_PADDR);
ADTSData adts_data = ParseADTS(reinterpret_cast<const char*>(data));
SetMediaType(adts_data);
response.num_channels = adts_data.channels;
if (!mDecoder) {
LOG_ERROR(Audio_DSP, "Missing decoder for profile: {}, channels: {}, samplerate: {}",
adts_data.profile, adts_data.channels, adts_data.samplerate);
return {};
}

// input
constexpr int timeout = 160;
std::size_t buffer_size = 0;
u8* buffer = nullptr;
ssize_t buffer_index = AMediaCodec_dequeueInputBuffer(mDecoder.get(), timeout);
if (buffer_index < 0) {
LOG_ERROR(Audio_DSP, "Failed to enqueue the input samples: {}", buffer_index);
return response;
}
buffer = AMediaCodec_getInputBuffer(mDecoder.get(), buffer_index, &buffer_size);
if (buffer_size < request.size) {
return response;
}
std::memcpy(buffer, data, request.size);
media_status_t status =
AMediaCodec_queueInputBuffer(mDecoder.get(), buffer_index, 0, request.size, 0, 0);
if (status != AMEDIA_OK) {
LOG_WARNING(Audio_DSP, "Try queue input buffer again later!");
return response;
}

// output
AMediaCodecBufferInfo info;
std::array<std::vector<u16>, 2> out_streams;
buffer_index = AMediaCodec_dequeueOutputBuffer(mDecoder.get(), &info, timeout);
switch (buffer_index) {
case AMEDIACODEC_INFO_TRY_AGAIN_LATER:
LOG_WARNING(Audio_DSP, "Failed to dequeue output buffer: timeout!");
break;
case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED:
LOG_WARNING(Audio_DSP, "Failed to dequeue output buffer: buffers changed!");
break;
case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED: {
AMediaFormat* format = AMediaCodec_getOutputFormat(mDecoder.get());
LOG_WARNING(Audio_DSP, "output format: {}", AMediaFormat_toString(format));
AMediaFormat_delete(format);
buffer_index = AMediaCodec_dequeueOutputBuffer(mDecoder.get(), &info, timeout);
}
default: {
int offset = info.offset;
buffer = AMediaCodec_getOutputBuffer(mDecoder.get(), buffer_index, &buffer_size);
while (offset < info.size) {
for (int channel = 0; channel < response.num_channels; channel++) {
u16 pcm_data;
std::memcpy(&pcm_data, buffer + offset, sizeof(pcm_data));
out_streams[channel].push_back(pcm_data);
offset += sizeof(pcm_data);
}
}
AMediaCodec_releaseOutputBuffer(mDecoder.get(), buffer_index, info.size != 0);
}
}

// transfer the decoded buffer from vector to the FCRAM
size_t stream0_size = out_streams[0].size() * sizeof(u16);
if (stream0_size != 0) {
if (request.dst_addr_ch0 < Memory::FCRAM_PADDR ||
request.dst_addr_ch0 + stream0_size >
Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) {
LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch0 {:08x}", request.dst_addr_ch0);
return response;
}
std::memcpy(mMemory.GetFCRAMPointer(request.dst_addr_ch0 - Memory::FCRAM_PADDR),
out_streams[0].data(), stream0_size);
}

size_t stream1_size = out_streams[1].size() * sizeof(u16);
if (stream1_size != 0) {
if (request.dst_addr_ch1 < Memory::FCRAM_PADDR ||
request.dst_addr_ch1 + stream1_size >
Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) {
LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch1 {:08x}", request.dst_addr_ch1);
return response;
}
std::memcpy(mMemory.GetFCRAMPointer(request.dst_addr_ch1 - Memory::FCRAM_PADDR),
out_streams[1].data(), stream1_size);
}
return response;
}

MediaNDKDecoder::MediaNDKDecoder(Memory::MemorySystem& memory)
: impl(std::make_unique<Impl>(memory)) {}

MediaNDKDecoder::~MediaNDKDecoder() = default;

std::optional<BinaryResponse> MediaNDKDecoder::ProcessRequest(const BinaryRequest& request) {
return impl->ProcessRequest(request);
}

bool MediaNDKDecoder::IsValid() const {
return true;
}

} // namespace AudioCore::HLE
22 changes: 22 additions & 0 deletions src/audio_core/hle/mediandk_decoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once

#include "audio_core/hle/decoder.h"

namespace AudioCore::HLE {

class MediaNDKDecoder final : public DecoderBase {
public:
explicit MediaNDKDecoder(Memory::MemorySystem& memory);
~MediaNDKDecoder() override;
std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request) override;
bool IsValid() const override;

private:
class Impl;
std::unique_ptr<Impl> impl;
};

} // namespace AudioCore::HLE