mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-21 18:22:33 +01:00
cellMusicDecode: initial implementation
Implements the basic functionality of cellMusicDecode. Works with Space Invaders (if you add the list selection from the other PR). Probably fixes SSX custom music.
This commit is contained in:
parent
f61ee85f80
commit
aafd74f9ea
6
3rdparty/CMakeLists.txt
vendored
6
3rdparty/CMakeLists.txt
vendored
@ -242,6 +242,7 @@ else()
|
||||
set(FFMPEG_LIB_AVCODEC "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/windows/x86_64/lib/libavcodec.a")
|
||||
set(FFMPEG_LIB_AVUTIL "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/windows/x86_64/lib/libavutil.a")
|
||||
set(FFMPEG_LIB_SWSCALE "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/windows/x86_64/lib/libswscale.a")
|
||||
set(FFMPEG_LIB_SWRESAMPLE "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/windows/x86_64/lib/libswresample.a")
|
||||
else()
|
||||
message("-- RPCS3: using builtin ffmpeg")
|
||||
|
||||
@ -260,6 +261,7 @@ else()
|
||||
find_library(FFMPEG_LIB_AVCODEC avcodec PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_AVUTIL avutil PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_SWSCALE swscale PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_SWRESAMPLE swresample PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
endif()
|
||||
|
||||
target_include_directories(3rdparty_ffmpeg INTERFACE "ffmpeg/include")
|
||||
@ -269,7 +271,9 @@ else()
|
||||
${FFMPEG_LIB_AVFORMAT}
|
||||
${FFMPEG_LIB_AVCODEC}
|
||||
${FFMPEG_LIB_AVUTIL}
|
||||
${FFMPEG_LIB_SWSCALE})
|
||||
${FFMPEG_LIB_SWSCALE}
|
||||
${FFMPEG_LIB_SWRESAMPLE}
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# - Try to find ffmpeg libraries (libavcodec, libavformat, libavutil and libswscale)
|
||||
# - Try to find ffmpeg libraries (libavcodec, libavformat, libavutil, libswresample and libswscale)
|
||||
# Once done this will define
|
||||
#
|
||||
# FFMPEG_FOUND - system has ffmpeg or libav
|
||||
@ -8,6 +8,7 @@
|
||||
# FFMPEG_LIBAVFORMAT
|
||||
# FFMPEG_LIBAVUTIL
|
||||
# FFMPEG_LIBSWSCALE
|
||||
# FFMPEG_LIBSWRESAMPLE
|
||||
#
|
||||
# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
|
||||
# Modified for other libraries by Lasse Kärkkäinen <tronic>
|
||||
@ -29,6 +30,7 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
|
||||
pkg_check_modules(_FFMPEG_AVFORMAT libavformat)
|
||||
pkg_check_modules(_FFMPEG_AVUTIL libavutil)
|
||||
pkg_check_modules(_FFMPEG_SWSCALE libswscale)
|
||||
pkg_check_modules(_FFMPEG_SWRESAMPLE libswresample)
|
||||
endif (PKG_CONFIG_FOUND)
|
||||
|
||||
find_path(FFMPEG_AVCODEC_INCLUDE_DIR
|
||||
@ -57,7 +59,12 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
|
||||
PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib
|
||||
)
|
||||
|
||||
if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT AND FFMPEG_LIBSWSCALE)
|
||||
find_library(FFMPEG_LIBSWRESAMPLE
|
||||
NAMES swresample
|
||||
PATHS ${_FFMPEG_SWRESAMPLE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib
|
||||
)
|
||||
|
||||
if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT AND FFMPEG_LIBSWSCALE AND FFMPEG_LIBSWRESAMPLE)
|
||||
set(FFMPEG_FOUND TRUE)
|
||||
endif()
|
||||
|
||||
@ -69,6 +76,7 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
|
||||
${FFMPEG_LIBAVFORMAT}
|
||||
${FFMPEG_LIBAVUTIL}
|
||||
${FFMPEG_LIBSWSCALE}
|
||||
${FFMPEG_LIBSWRESAMPLE}
|
||||
)
|
||||
|
||||
endif (FFMPEG_FOUND)
|
||||
@ -79,7 +87,7 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
|
||||
endif (NOT FFMPEG_FIND_QUIETLY)
|
||||
else (FFMPEG_FOUND)
|
||||
if (FFMPEG_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find libavcodec or libavformat or libavutil or libswscale")
|
||||
message(FATAL_ERROR "Could not find libavcodec or libavformat or libavutil or libswscale or libswresample")
|
||||
endif (FFMPEG_FIND_REQUIRED)
|
||||
endif (FFMPEG_FOUND)
|
||||
|
||||
|
@ -3,18 +3,22 @@
|
||||
#include "Emu/Cell/lv2/sys_lwmutex.h"
|
||||
#include "Emu/Cell/lv2/sys_lwcond.h"
|
||||
#include "Emu/Cell/lv2/sys_spu.h"
|
||||
#include "Emu/VFS.h"
|
||||
#include "cellMusic.h"
|
||||
#include "cellSearch.h"
|
||||
#include "cellSpurs.h"
|
||||
#include "cellSysutil.h"
|
||||
#include "util/media_utils.h"
|
||||
|
||||
#include <deque>
|
||||
|
||||
|
||||
LOG_CHANNEL(cellMusicDecode);
|
||||
|
||||
// Return Codes
|
||||
// Return Codes (CELL_MUSIC_DECODE2 codes are omitted if they are identical)
|
||||
enum CellMusicDecodeError : u32
|
||||
{
|
||||
CELL_MUSIC_DECODE_CANCELED = 1,
|
||||
CELL_MUSIC_DECODE_DECODE_FINISHED = 0x8002C101,
|
||||
CELL_MUSIC_DECODE_ERROR_PARAM = 0x8002C102,
|
||||
CELL_MUSIC_DECODE_ERROR_BUSY = 0x8002C103,
|
||||
@ -37,6 +41,7 @@ void fmt_class_string<CellMusicDecodeError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(CELL_MUSIC_DECODE_CANCELED);
|
||||
STR_CASE(CELL_MUSIC_DECODE_DECODE_FINISHED);
|
||||
STR_CASE(CELL_MUSIC_DECODE_ERROR_PARAM);
|
||||
STR_CASE(CELL_MUSIC_DECODE_ERROR_BUSY);
|
||||
@ -56,6 +61,7 @@ void fmt_class_string<CellMusicDecodeError>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
// Constants (CELL_MUSIC_DECODE2 codes are omitted if they are identical)
|
||||
enum
|
||||
{
|
||||
CELL_MUSIC_DECODE_EVENT_STATUS_NOTIFICATION = 0,
|
||||
@ -66,18 +72,37 @@ enum
|
||||
CELL_MUSIC_DECODE_EVENT_SET_SELECTION_CONTEXT_RESULT = 5,
|
||||
CELL_MUSIC_DECODE_EVENT_UI_NOTIFICATION = 6,
|
||||
CELL_MUSIC_DECODE_EVENT_NEXT_CONTENTS_READY_RESULT = 7,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CELL_MUSIC_DECODE2_EVENT_STATUS_NOTIFICATION = 0,
|
||||
CELL_MUSIC_DECODE2_EVENT_INITIALIZE_RESULT = 1,
|
||||
CELL_MUSIC_DECODE2_EVENT_FINALIZE_RESULT = 2,
|
||||
CELL_MUSIC_DECODE2_EVENT_SELECT_CONTENTS_RESULT = 3,
|
||||
CELL_MUSIC_DECODE2_EVENT_SET_DECODE_COMMAND_RESULT = 4,
|
||||
CELL_MUSIC_DECODE2_EVENT_SET_SELECTION_CONTEXT_RESULT = 5,
|
||||
CELL_MUSIC_DECODE2_EVENT_UI_NOTIFICATION = 6,
|
||||
CELL_MUSIC_DECODE2_EVENT_NEXT_CONTENTS_READY_RESULT = 7,
|
||||
CELL_MUSIC_DECODE_MODE_NORMAL = 0,
|
||||
|
||||
CELL_MUSIC_DECODE_CMD_STOP = 0,
|
||||
CELL_MUSIC_DECODE_CMD_START = 1,
|
||||
CELL_MUSIC_DECODE_CMD_NEXT = 2,
|
||||
CELL_MUSIC_DECODE_CMD_PREV = 3,
|
||||
|
||||
CELL_MUSIC_DECODE_STATUS_DORMANT = 0,
|
||||
CELL_MUSIC_DECODE_STATUS_DECODING = 1,
|
||||
|
||||
CELL_MUSIC_DECODE_POSITION_NONE = 0,
|
||||
CELL_MUSIC_DECODE_POSITION_START = 1,
|
||||
CELL_MUSIC_DECODE_POSITION_MID = 2,
|
||||
CELL_MUSIC_DECODE_POSITION_END = 3,
|
||||
CELL_MUSIC_DECODE_POSITION_END_LIST_END = 4,
|
||||
|
||||
CELL_MUSIC_DECODE2_SPEED_MAX = 0,
|
||||
CELL_MUSIC_DECODE2_SPEED_2 = 2,
|
||||
|
||||
CELL_SYSUTIL_MUSIC_DECODE2_INITIALIZING_FINISHED = 1,
|
||||
CELL_SYSUTIL_MUSIC_DECODE2_SHUTDOWN_FINISHED = 4, // 3(SDK103) -> 4(SDK110)
|
||||
CELL_SYSUTIL_MUSIC_DECODE2_LOADING_FINISHED = 5,
|
||||
CELL_SYSUTIL_MUSIC_DECODE2_UNLOADING_FINISHED = 7,
|
||||
CELL_SYSUTIL_MUSIC_DECODE2_RELEASED = 9,
|
||||
CELL_SYSUTIL_MUSIC_DECODE2_GRABBED = 11,
|
||||
|
||||
CELL_MUSIC_DECODE2_MIN_BUFFER_SIZE = 448 * 1024,
|
||||
CELL_MUSIC_DECODE2_MANAGEMENT_SIZE = 64 * 1024,
|
||||
CELL_MUSIC_DECODE2_PAGESIZE_64K = 64 * 1024,
|
||||
CELL_MUSIC_DECODE2_PAGESIZE_1M = 1 * 1024 * 1024,
|
||||
};
|
||||
|
||||
using CellMusicDecodeCallback = void(u32, vm::ptr<void> param, vm::ptr<void> userData);
|
||||
@ -87,29 +112,131 @@ struct music_decode
|
||||
{
|
||||
vm::ptr<CellMusicDecodeCallback> func{};
|
||||
vm::ptr<void> userData{};
|
||||
music_selection_context current_selection_context{};
|
||||
s32 decode_status = CELL_MUSIC_DECODE_STATUS_DORMANT;
|
||||
s32 decode_command = CELL_MUSIC_DECODE_CMD_STOP;
|
||||
u64 readPos = 0;
|
||||
utils::audio_decoder decoder{};
|
||||
|
||||
shared_mutex mutex;
|
||||
|
||||
error_code set_decode_command(s32 command)
|
||||
{
|
||||
decode_command = command;
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case CELL_MUSIC_DECODE_CMD_STOP:
|
||||
{
|
||||
decoder.stop();
|
||||
decode_status = CELL_MUSIC_DECODE_STATUS_DORMANT;
|
||||
break;
|
||||
}
|
||||
case CELL_MUSIC_DECODE_CMD_START:
|
||||
{
|
||||
decode_status = CELL_MUSIC_DECODE_STATUS_DECODING;
|
||||
readPos = 0;
|
||||
|
||||
// Decode data. The format of the decoded data is 48kHz, float 32bit, 2ch LPCM data interleaved in order from left to right.
|
||||
decoder.set_path(vfs::get(current_selection_context.path));
|
||||
decoder.set_swap_endianness(true);
|
||||
decoder.decode();
|
||||
break;
|
||||
}
|
||||
case CELL_MUSIC_DECODE_CMD_NEXT: // TODO: set path of next file if possible
|
||||
case CELL_MUSIC_DECODE_CMD_PREV: // TODO: set path of prev file if possible
|
||||
{
|
||||
return CELL_MUSIC_DECODE_ERROR_NO_MORE_CONTENT;
|
||||
}
|
||||
}
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code finalize()
|
||||
{
|
||||
decoder.stop();
|
||||
decode_status = CELL_MUSIC_DECODE_STATUS_DORMANT;
|
||||
decode_command = CELL_MUSIC_DECODE_CMD_STOP;
|
||||
readPos = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
};
|
||||
|
||||
struct music_decode2
|
||||
struct music_decode2 : music_decode
|
||||
{
|
||||
vm::ptr<CellMusicDecode2Callback> func{};
|
||||
vm::ptr<void> userData{};
|
||||
|
||||
shared_mutex mutex;
|
||||
s32 speed = CELL_MUSIC_DECODE2_SPEED_MAX;
|
||||
};
|
||||
|
||||
template <typename Music_Decode>
|
||||
error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64 reqSize, vm::ptr<u64> readSize, vm::ptr<s32> position)
|
||||
{
|
||||
if (!buf || !startTime || !position || !readSize)
|
||||
return CELL_MUSIC_DECODE_ERROR_PARAM;
|
||||
|
||||
auto& dec = g_fxo->get<Music_Decode>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
std::scoped_lock slock(dec.decoder.m_mtx);
|
||||
|
||||
if (dec.decoder.has_error)
|
||||
return CELL_MUSIC_DECODE_ERROR_DECODE_FAILURE;
|
||||
|
||||
if (dec.decoder.m_size == 0)
|
||||
{
|
||||
*position = CELL_MUSIC_DECODE_POSITION_NONE;
|
||||
*readSize = 0;
|
||||
return CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA;
|
||||
}
|
||||
|
||||
if (dec.readPos == 0)
|
||||
{
|
||||
*position = CELL_MUSIC_DECODE_POSITION_START;
|
||||
}
|
||||
else if ((dec.readPos + reqSize) >= dec.decoder.m_size)
|
||||
{
|
||||
*position = CELL_MUSIC_DECODE_POSITION_END_LIST_END;
|
||||
}
|
||||
else
|
||||
{
|
||||
*position = CELL_MUSIC_DECODE_POSITION_MID;
|
||||
}
|
||||
|
||||
const u64 size_to_read = (dec.readPos + reqSize) <= dec.decoder.m_size ? reqSize : dec.decoder.m_size - dec.readPos;
|
||||
std::memcpy(buf.get_ptr(), &dec.decoder.data[dec.readPos], size_to_read);
|
||||
|
||||
dec.readPos += size_to_read;
|
||||
*readSize = size_to_read;
|
||||
|
||||
s64 start_time_ms = 0;
|
||||
|
||||
if (!dec.decoder.timestamps_ms.empty())
|
||||
{
|
||||
start_time_ms = dec.decoder.timestamps_ms.front().second;
|
||||
|
||||
while (dec.decoder.timestamps_ms.size() > 1 && dec.readPos >= dec.decoder.timestamps_ms.at(1).first)
|
||||
{
|
||||
dec.decoder.timestamps_ms.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
*startTime = static_cast<u32>(start_time_ms); // startTime is milliseconds
|
||||
|
||||
cellMusicDecode.trace("cell_music_decode_read(size_to_read=%d, samples=%d, start_time_ms=%d)", size_to_read, size_to_read / sizeof(u64), start_time_ms);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellMusicDecodeInitialize(s32 mode, u32 container, s32 spuPriority, vm::ptr<CellMusicDecodeCallback> func, vm::ptr<void> userData)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeInitialize(mode=0x%x, container=0x%x, spuPriority=0x%x, func=*0x%x, userData=*0x%x)", mode, container, spuPriority, func, userData);
|
||||
|
||||
auto& dec = g_fxo->get<music_decode>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
dec.func = func;
|
||||
dec.userData = userData;
|
||||
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
sysutil_register_cb([&dec](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
@ -121,12 +248,13 @@ error_code cellMusicDecodeInitializeSystemWorkload(s32 mode, u32 container, vm::
|
||||
cellMusicDecode.todo("cellMusicDecodeInitializeSystemWorkload(mode=0x%x, container=0x%x, func=*0x%x, userData=*0x%x, spuUsageRate=0x%x, spurs=*0x%x, priority=*0x%x, attr=*0x%x)", mode, container, func, userData, spuUsageRate, spurs, priority, attr);
|
||||
|
||||
auto& dec = g_fxo->get<music_decode>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
dec.func = func;
|
||||
dec.userData = userData;
|
||||
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
sysutil_register_cb([&dec](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
@ -138,10 +266,12 @@ error_code cellMusicDecodeFinalize()
|
||||
cellMusicDecode.todo("cellMusicDecodeFinalize()");
|
||||
|
||||
auto& dec = g_fxo->get<music_decode>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
dec.finalize();
|
||||
|
||||
if (dec.func)
|
||||
{
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
sysutil_register_cb([&dec](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_FINALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
return CELL_OK;
|
||||
@ -156,6 +286,7 @@ error_code cellMusicDecodeSelectContents()
|
||||
cellMusicDecode.todo("cellMusicDecodeSelectContents()");
|
||||
|
||||
auto& dec = g_fxo->get<music_decode>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
|
||||
if (!dec.func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
@ -174,13 +305,16 @@ error_code cellMusicDecodeSetDecodeCommand(s32 command)
|
||||
cellMusicDecode.todo("cellMusicDecodeSetDecodeCommand(command=0x%x)", command);
|
||||
|
||||
auto& dec = g_fxo->get<music_decode>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
|
||||
if (!dec.func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
const error_code result = dec.set_decode_command(command);
|
||||
|
||||
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_DECODE_COMMAND_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_DECODE_COMMAND_RESULT, vm::addr_t(s32{result}), dec.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
@ -190,18 +324,36 @@ error_code cellMusicDecodeSetDecodeCommand(s32 command)
|
||||
error_code cellMusicDecodeGetDecodeStatus(vm::ptr<s32> status)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeGetDecodeStatus(status=*0x%x)", status);
|
||||
|
||||
if (!status)
|
||||
return CELL_MUSIC_DECODE_ERROR_PARAM;
|
||||
|
||||
auto& dec = g_fxo->get<music_decode>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
*status = dec.decode_status;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellMusicDecodeRead(vm::ptr<void> buf, vm::ptr<u32> startTime, u64 reqSize, vm::ptr<u64> readSize, vm::ptr<s32> position)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeRead(buf=*0x%x, startTime=*0x%x, reqSize=0x%llx, readSize=*0x%x, position=*0x%x)", buf, startTime, reqSize, readSize, position);
|
||||
return CELL_OK;
|
||||
cellMusicDecode.notice("cellMusicDecodeRead(buf=*0x%x, startTime=*0x%x, reqSize=0x%llx, readSize=*0x%x, position=*0x%x)", buf, startTime, reqSize, readSize, position);
|
||||
|
||||
return cell_music_decode_read<music_decode>(buf, startTime, reqSize, readSize, position);
|
||||
}
|
||||
|
||||
error_code cellMusicDecodeGetSelectionContext(vm::ptr<CellMusicSelectionContext> context)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeGetSelectionContext(context=*0x%x)", context);
|
||||
|
||||
if (!context)
|
||||
return CELL_MUSIC_DECODE_ERROR_PARAM;
|
||||
|
||||
auto& dec = g_fxo->get<music_decode>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
*context = dec.current_selection_context.get();
|
||||
cellMusicDecode.warning("cellMusicDecodeGetSelectionContext: selection_context = %s", dec.current_selection_context.to_string());
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -209,14 +361,23 @@ error_code cellMusicDecodeSetSelectionContext(vm::ptr<CellMusicSelectionContext>
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeSetSelectionContext(context=*0x%x)", context);
|
||||
|
||||
if (!context)
|
||||
return CELL_MUSIC_DECODE_ERROR_PARAM;
|
||||
|
||||
auto& dec = g_fxo->get<music_decode>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
|
||||
if (!dec.func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
const bool result = dec.current_selection_context.set(*context);
|
||||
if (result) cellMusicDecode.warning("cellMusicDecodeSetSelectionContext: new selection_context = %s", dec.current_selection_context.to_string());
|
||||
else cellMusicDecode.error("cellMusicDecodeSetSelectionContext: failed. context = '%s'", context->data);
|
||||
|
||||
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
const u32 status = result ? u32{CELL_OK} : u32{CELL_MUSIC_DECODE_ERROR_INVALID_CONTEXT};
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(status), dec.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
@ -226,37 +387,53 @@ error_code cellMusicDecodeSetSelectionContext(vm::ptr<CellMusicSelectionContext>
|
||||
error_code cellMusicDecodeGetContentsId(vm::ptr<CellSearchContentId> contents_id)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeGetContentsId(contents_id=*0x%x)", contents_id);
|
||||
return CELL_OK;
|
||||
|
||||
if (!contents_id)
|
||||
return CELL_MUSIC_ERROR_PARAM;
|
||||
|
||||
// HACKY
|
||||
auto& dec = g_fxo->get<music_decode>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
return dec.current_selection_context.find_content_id(contents_id);
|
||||
}
|
||||
|
||||
error_code cellMusicDecodeInitialize2(s32 mode, u32 container, s32 spuPriority, vm::ptr<CellMusicDecode2Callback> func, vm::ptr<void> userData, s32 speed, s32 bufsize)
|
||||
error_code cellMusicDecodeInitialize2(s32 mode, u32 container, s32 spuPriority, vm::ptr<CellMusicDecode2Callback> func, vm::ptr<void> userData, s32 speed, s32 bufSize)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeInitialize2(mode=0x%x, container=0x%x, spuPriority=0x%x, func=*0x%x, userData=*0x%x, speed=0x%x, bufsize=0x%x)", mode, container, spuPriority, func, userData, speed, bufsize);
|
||||
cellMusicDecode.todo("cellMusicDecodeInitialize2(mode=0x%x, container=0x%x, spuPriority=0x%x, func=*0x%x, userData=*0x%x, speed=0x%x, bufSize=0x%x)", mode, container, spuPriority, func, userData, speed, bufSize);
|
||||
|
||||
if (bufSize < CELL_MUSIC_DECODE2_MIN_BUFFER_SIZE)
|
||||
return CELL_MUSIC_DECODE_ERROR_PARAM;
|
||||
|
||||
auto& dec = g_fxo->get<music_decode2>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
dec.func = func;
|
||||
dec.userData = userData;
|
||||
dec.speed = speed;
|
||||
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
sysutil_register_cb([userData, &dec](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellMusicDecodeInitialize2SystemWorkload(s32 mode, u32 container, vm::ptr<CellMusicDecode2Callback> func, vm::ptr<void> userData, s32 spuUsageRate, s32 bufsize, vm::ptr<CellSpurs> spurs, vm::cptr<u8> priority, vm::cptr<CellSpursSystemWorkloadAttribute> attr)
|
||||
error_code cellMusicDecodeInitialize2SystemWorkload(s32 mode, u32 container, vm::ptr<CellMusicDecode2Callback> func, vm::ptr<void> userData, s32 spuUsageRate, s32 bufSize, vm::ptr<CellSpurs> spurs, vm::cptr<u8> priority, vm::cptr<CellSpursSystemWorkloadAttribute> attr)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeInitialize2SystemWorkload(mode=0x%x, container=0x%x, func=*0x%x, userData=*0x%x, spuUsageRate=0x%x, bufsize=0x%x, spurs=*0x%x, priority=*0x%x, attr=*0x%x)", mode, container, func, userData, spuUsageRate, bufsize, spurs, priority, attr);
|
||||
cellMusicDecode.todo("cellMusicDecodeInitialize2SystemWorkload(mode=0x%x, container=0x%x, func=*0x%x, userData=*0x%x, spuUsageRate=0x%x, bufSize=0x%x, spurs=*0x%x, priority=*0x%x, attr=*0x%x)", mode, container, func, userData, spuUsageRate, bufSize, spurs, priority, attr);
|
||||
|
||||
if (bufSize < CELL_MUSIC_DECODE2_MIN_BUFFER_SIZE)
|
||||
return CELL_MUSIC_DECODE_ERROR_PARAM;
|
||||
|
||||
auto& dec = g_fxo->get<music_decode2>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
dec.func = func;
|
||||
dec.userData = userData;
|
||||
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
sysutil_register_cb([&dec](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
@ -268,12 +445,14 @@ error_code cellMusicDecodeFinalize2()
|
||||
cellMusicDecode.todo("cellMusicDecodeFinalize2()");
|
||||
|
||||
auto& dec = g_fxo->get<music_decode2>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
dec.finalize();
|
||||
|
||||
if (dec.func)
|
||||
{
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
sysutil_register_cb([&dec](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_FINALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_FINALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
}
|
||||
@ -286,13 +465,14 @@ error_code cellMusicDecodeSelectContents2()
|
||||
cellMusicDecode.todo("cellMusicDecodeSelectContents2()");
|
||||
|
||||
auto& dec = g_fxo->get<music_decode2>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
|
||||
if (!dec.func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_SELECT_CONTENTS_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SELECT_CONTENTS_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
@ -304,13 +484,16 @@ error_code cellMusicDecodeSetDecodeCommand2(s32 command)
|
||||
cellMusicDecode.todo("cellMusicDecodeSetDecodeCommand2(command=0x%x)", command);
|
||||
|
||||
auto& dec = g_fxo->get<music_decode2>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
|
||||
if (!dec.func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
const error_code result = dec.set_decode_command(command);
|
||||
|
||||
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_SET_DECODE_COMMAND_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_DECODE_COMMAND_RESULT, vm::addr_t(s32{result}), dec.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
@ -320,18 +503,36 @@ error_code cellMusicDecodeSetDecodeCommand2(s32 command)
|
||||
error_code cellMusicDecodeGetDecodeStatus2(vm::ptr<s32> status)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeGetDecodeStatus2(status=*0x%x)", status);
|
||||
|
||||
if (!status)
|
||||
return CELL_MUSIC_DECODE_ERROR_PARAM;
|
||||
|
||||
auto& dec = g_fxo->get<music_decode2>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
*status = dec.decode_status;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellMusicDecodeRead2(vm::ptr<void> buf, vm::ptr<u32> startTime, u64 reqSize, vm::ptr<u64> readSize, vm::ptr<s32> position)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeRead2(buf=*0x%x, startTime=*0x%x, reqSize=0x%llx, readSize=*0x%x, position=*0x%x)", buf, startTime, reqSize, readSize, position);
|
||||
return CELL_OK;
|
||||
cellMusicDecode.notice("cellMusicDecodeRead2(buf=*0x%x, startTime=*0x%x, reqSize=0x%llx, readSize=*0x%x, position=*0x%x)", buf, startTime, reqSize, readSize, position);
|
||||
|
||||
return cell_music_decode_read<music_decode2>(buf, startTime, reqSize, readSize, position);
|
||||
}
|
||||
|
||||
error_code cellMusicDecodeGetSelectionContext2(vm::ptr<CellMusicSelectionContext> context)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeGetSelectionContext2(context=*0x%x)", context);
|
||||
|
||||
if (!context)
|
||||
return CELL_MUSIC_DECODE_ERROR_PARAM;
|
||||
|
||||
auto& dec = g_fxo->get<music_decode2>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
*context = dec.current_selection_context.get();
|
||||
cellMusicDecode.warning("cellMusicDecodeGetSelectionContext2: selection context = %s)", dec.current_selection_context.to_string());
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -340,23 +541,36 @@ error_code cellMusicDecodeSetSelectionContext2(vm::ptr<CellMusicSelectionContext
|
||||
cellMusicDecode.todo("cellMusicDecodeSetSelectionContext2(context=*0x%x)", context);
|
||||
|
||||
auto& dec = g_fxo->get<music_decode2>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
|
||||
if (!dec.func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
|
||||
const bool result = dec.current_selection_context.set(*context);
|
||||
if (result) cellMusicDecode.warning("cellMusicDecodeSetSelectionContext2: new selection_context = %s", dec.current_selection_context.to_string());
|
||||
else cellMusicDecode.error("cellMusicDecodeSetSelectionContext2: failed. context = '%s'", context->data);
|
||||
|
||||
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
|
||||
{
|
||||
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(CELL_OK), dec.userData);
|
||||
const u32 status = result ? u32{CELL_OK} : u32{CELL_MUSIC_DECODE_ERROR_INVALID_CONTEXT};
|
||||
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(status), dec.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellMusicDecodeGetContentsId2(vm::ptr<CellSearchContentId> contents_id )
|
||||
error_code cellMusicDecodeGetContentsId2(vm::ptr<CellSearchContentId> contents_id)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeGetContentsId2(contents_id=*0x%x)", contents_id);
|
||||
return CELL_OK;
|
||||
|
||||
if (!contents_id)
|
||||
return CELL_MUSIC2_ERROR_PARAM;
|
||||
|
||||
// HACKY
|
||||
auto& dec = g_fxo->get<music_decode2>();
|
||||
std::lock_guard lock(dec.mutex);
|
||||
return dec.current_selection_context.find_content_id(contents_id);
|
||||
}
|
||||
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace rsx
|
||||
else
|
||||
{
|
||||
// Fallback
|
||||
// TODO: use proper icon
|
||||
static_cast<image_view*>(image.get())->set_image_resource(resource_config::standard_image_resource::square);
|
||||
}
|
||||
|
||||
@ -192,7 +193,6 @@ namespace rsx
|
||||
m_dim_background->back_color.a = 0.5f;
|
||||
}
|
||||
|
||||
std::vector<u8> icon;
|
||||
std::vector<std::unique_ptr<overlay_element>> entries;
|
||||
|
||||
const std::string home_dir = rpcs3::utils::get_hdd0_dir() + "home/";
|
||||
|
@ -7,12 +7,18 @@
|
||||
#pragma warning(push, 0)
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wall"
|
||||
#pragma GCC diagnostic ignored "-Wextra"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
extern "C" {
|
||||
#include "libavcodec/avcodec.h"
|
||||
#include "libavformat/avformat.h"
|
||||
#include "libavutil/dict.h"
|
||||
#include "libavutil/opt.h"
|
||||
#include "libswresample/swresample.h"
|
||||
}
|
||||
constexpr int averror_eof = AVERROR_EOF; // workaround for old-style-cast error
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
@ -156,4 +162,257 @@ namespace utils
|
||||
|
||||
return { true, std::move(info) };
|
||||
}
|
||||
|
||||
struct scoped_av
|
||||
{
|
||||
AVFormatContext* format = nullptr;
|
||||
AVCodec* codec = nullptr;
|
||||
AVCodecContext* context = nullptr;
|
||||
AVFrame* frame = nullptr;
|
||||
SwrContext* swr = nullptr;
|
||||
|
||||
~scoped_av()
|
||||
{
|
||||
// Clean up
|
||||
if (frame)
|
||||
av_frame_free(&frame);
|
||||
if (swr)
|
||||
swr_free(&swr);
|
||||
if (context)
|
||||
avcodec_close(context);
|
||||
if (codec)
|
||||
av_free(codec);
|
||||
if (format)
|
||||
avformat_free_context(format);
|
||||
}
|
||||
};
|
||||
|
||||
audio_decoder::audio_decoder()
|
||||
{
|
||||
}
|
||||
|
||||
audio_decoder::~audio_decoder()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void audio_decoder::set_path(const std::string& path)
|
||||
{
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
void audio_decoder::set_swap_endianness(bool swapped)
|
||||
{
|
||||
m_swap_endianness = swapped;
|
||||
}
|
||||
|
||||
void audio_decoder::stop()
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
auto& thread = *m_thread;
|
||||
thread = thread_state::aborting;
|
||||
thread();
|
||||
}
|
||||
|
||||
has_error = false;
|
||||
m_size = 0;
|
||||
timestamps_ms.clear();
|
||||
data.clear();
|
||||
}
|
||||
|
||||
void audio_decoder::decode()
|
||||
{
|
||||
stop();
|
||||
|
||||
m_thread = std::make_unique<named_thread<std::function<void()>>>("Music Decode Thread", [this, path = m_path]()
|
||||
{
|
||||
scoped_av av;
|
||||
|
||||
// Get format from audio file
|
||||
av.format = avformat_alloc_context();
|
||||
if (int err = avformat_open_input(&av.format, path.c_str(), nullptr, nullptr); err < 0)
|
||||
{
|
||||
media_log.error("audio_decoder: Could not open file '%s'. Error: %d='%s'", path, err, error_to_string(err));
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
if (int err = avformat_find_stream_info(av.format, nullptr); err < 0)
|
||||
{
|
||||
media_log.error("audio_decoder: Could not retrieve stream info from file '%s'. Error: %d='%s'", path, err, error_to_string(err));
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the first audio stream
|
||||
AVStream* stream = nullptr;
|
||||
unsigned int stream_index;
|
||||
for (stream_index = 0; stream_index < av.format->nb_streams; stream_index++)
|
||||
{
|
||||
if (av.format->streams[stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
{
|
||||
stream = av.format->streams[stream_index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!stream)
|
||||
{
|
||||
media_log.error("audio_decoder: Could not retrieve audio stream from file '%s'", path);
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find decoder
|
||||
av.codec = avcodec_find_decoder(stream->codecpar->codec_id);
|
||||
if (!av.codec)
|
||||
{
|
||||
media_log.error("audio_decoder: Failed to find decoder for stream #%u in file '%s'", stream_index, path);
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate context
|
||||
av.context = avcodec_alloc_context3(av.codec);
|
||||
if (!av.context)
|
||||
{
|
||||
media_log.error("audio_decoder: Failed to allocate context for stream #%u in file '%s'", stream_index, path);
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Open decoder
|
||||
if (int err = avcodec_open2(av.context, av.codec, nullptr); err < 0)
|
||||
{
|
||||
media_log.error("audio_decoder: Failed to open decoder for stream #%u in file '%s'. Error: %d='%s'", stream_index, path, err, error_to_string(err));
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare resampler
|
||||
av.swr = swr_alloc();
|
||||
if (!av.swr)
|
||||
{
|
||||
media_log.error("audio_decoder: Failed to allocate resampler for stream #%u in file '%s'", stream_index, path);
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const int dst_channels = 2;
|
||||
const u64 dst_channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
const AVSampleFormat dst_format = AV_SAMPLE_FMT_FLT;
|
||||
|
||||
int set_err = 0;
|
||||
if ((set_err = av_opt_set_int(av.swr, "in_channel_count", stream->codecpar->channels, 0)) ||
|
||||
(set_err = av_opt_set_int(av.swr, "out_channel_count", dst_channels, 0)) ||
|
||||
(set_err = av_opt_set_channel_layout(av.swr, "in_channel_layout", stream->codecpar->channel_layout, 0)) ||
|
||||
(set_err = av_opt_set_channel_layout(av.swr, "out_channel_layout", dst_channel_layout, 0)) ||
|
||||
(set_err = av_opt_set_int(av.swr, "in_sample_rate", stream->codecpar->sample_rate, 0)) ||
|
||||
(set_err = av_opt_set_int(av.swr, "out_sample_rate", sample_rate, 0)) ||
|
||||
(set_err = av_opt_set_sample_fmt(av.swr, "in_sample_fmt", static_cast<AVSampleFormat>(stream->codecpar->format), 0)) ||
|
||||
(set_err = av_opt_set_sample_fmt(av.swr, "out_sample_fmt", dst_format, 0)))
|
||||
{
|
||||
media_log.error("audio_decoder: Failed to set resampler options: Error: %d='%s'", set_err, error_to_string(set_err));
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (int err = swr_init(av.swr); err < 0 || !swr_is_initialized(av.swr))
|
||||
{
|
||||
media_log.error("audio_decoder: Resampler has not been properly initialized: %d='%s'", err, error_to_string(err));
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare to read data
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
av.frame = av_frame_alloc();
|
||||
if (!av.frame)
|
||||
{
|
||||
media_log.error("audio_decoder: Error allocating the frame");
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
duration_ms = stream->duration / 1000;
|
||||
|
||||
// Iterate through frames
|
||||
while (thread_ctrl::state() != thread_state::aborting && av_read_frame(av.format, &packet) >= 0)
|
||||
{
|
||||
if (int err = avcodec_send_packet(av.context, &packet); err < 0)
|
||||
{
|
||||
media_log.error("audio_decoder: Queuing error: %d='%s'", err, error_to_string(err));
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
if (int err = avcodec_receive_frame(av.context, av.frame); err < 0)
|
||||
{
|
||||
if (err == AVERROR(EAGAIN) || err == averror_eof)
|
||||
break;
|
||||
|
||||
media_log.error("audio_decoder: Decoding error: %d='%s'", err, error_to_string(err));
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Resample frames
|
||||
u8* buffer;
|
||||
const int align = 1;
|
||||
const int buffer_size = av_samples_alloc(&buffer, nullptr, dst_channels, av.frame->nb_samples, dst_format, align);
|
||||
if (buffer_size < 0)
|
||||
{
|
||||
media_log.error("audio_decoder: Error allocating buffer: %d='%s'", buffer_size, error_to_string(buffer_size));
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const int frame_count = swr_convert(av.swr, &buffer, av.frame->nb_samples, const_cast<const uint8_t**>(av.frame->data), av.frame->nb_samples);
|
||||
if (frame_count < 0)
|
||||
{
|
||||
media_log.error("audio_decoder: Error converting frame: %d='%s'", frame_count, error_to_string(frame_count));
|
||||
has_error = true;
|
||||
if (buffer)
|
||||
av_free(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
// Append resampled frames to data
|
||||
{
|
||||
std::scoped_lock lock(m_mtx);
|
||||
data.resize(m_size + buffer_size);
|
||||
|
||||
if (m_swap_endianness)
|
||||
{
|
||||
// The format is float 32bit per channel.
|
||||
const auto write_byteswapped = [](const void* src, void* dst) -> void
|
||||
{
|
||||
*static_cast<f32*>(dst) = *static_cast<const be_t<f32>*>(src);
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < (buffer_size - sizeof(f32)); i += sizeof(f32))
|
||||
{
|
||||
write_byteswapped(buffer + i, data.data() + m_size + i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&data[m_size], buffer, buffer_size);
|
||||
}
|
||||
|
||||
const u32 timestamp_ms = stream->time_base.den ? (1000 * av.frame->best_effort_timestamp * stream->time_base.num) / stream->time_base.den : 0;
|
||||
timestamps_ms.push_back({m_size, timestamp_ms});
|
||||
m_size += buffer_size;
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
av_free(buffer);
|
||||
|
||||
media_log.trace("audio_decoder: decoded frame_count=%d buffer_size=%d timestamp_us=%d", frame_count, buffer_size, av.frame->best_effort_timestamp);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "Utilities/Thread.h"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
@ -34,4 +40,29 @@ namespace utils
|
||||
}
|
||||
strcpy_trunc(dst, value);
|
||||
};
|
||||
|
||||
class audio_decoder
|
||||
{
|
||||
public:
|
||||
audio_decoder();
|
||||
~audio_decoder();
|
||||
|
||||
void set_path(const std::string& path);
|
||||
void set_swap_endianness(bool swapped);
|
||||
void stop();
|
||||
void decode();
|
||||
|
||||
std::mutex m_mtx;
|
||||
const s32 sample_rate = 48000;
|
||||
std::vector<u8> data;
|
||||
atomic_t<u64> m_size = 0;
|
||||
atomic_t<u64> duration_ms = 0;
|
||||
atomic_t<bool> has_error{false};
|
||||
std::deque<std::pair<u64, u64>> timestamps_ms;
|
||||
|
||||
private:
|
||||
bool m_swap_endianness = false;
|
||||
std::string m_path;
|
||||
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user