mirror of
https://github.com/Almamu/linux-wallpaperengine.git
synced 2025-07-13 21:02:34 +08:00
Removed SDL_mixer in favour of ffmpeg for audio decoding
Added extra option for general audio volume Signed-off-by: Alexis Maiquez <almamu@almamu.com>
This commit is contained in:
parent
746d0943ff
commit
eb14099c4c
@ -15,7 +15,6 @@ find_package(GLEW REQUIRED)
|
||||
find_package(GLUT REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(SDL REQUIRED)
|
||||
find_package(SDL_mixer REQUIRED)
|
||||
find_package(LZ4 REQUIRED)
|
||||
find_package(FFMPEG REQUIRED)
|
||||
find_package(FreeImage REQUIRED)
|
||||
@ -55,6 +54,9 @@ add_executable(
|
||||
src/WallpaperEngine/Core/Core.h
|
||||
src/WallpaperEngine/Core/Core.cpp
|
||||
|
||||
src/WallpaperEngine/Audio/CAudioStream.cpp
|
||||
src/WallpaperEngine/Audio/CAudioStream.h
|
||||
|
||||
src/WallpaperEngine/Input/CMouseInput.h
|
||||
src/WallpaperEngine/Input/CMouseInput.cpp
|
||||
|
||||
|
@ -81,6 +81,14 @@ else()
|
||||
/opt/local/lib
|
||||
/sw/lib)
|
||||
|
||||
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)
|
||||
set(FFMPEG_FOUND TRUE)
|
||||
endif()
|
||||
@ -91,7 +99,8 @@ else()
|
||||
${FFMPEG_LIBAVCODEC}
|
||||
${FFMPEG_LIBAVFORMAT}
|
||||
${FFMPEG_LIBAVUTIL}
|
||||
${FFMPEG_LIBSWSCALE})
|
||||
${FFMPEG_LIBSWSCALE}
|
||||
${FFMPEG_LIBSWRESAMPLE})
|
||||
endif()
|
||||
|
||||
if(FFMPEG_FOUND)
|
||||
|
@ -14,7 +14,6 @@ Wallpaper Engine is a software designed by [Kristjan Skutta](https://store.steam
|
||||
- LZ4
|
||||
- ZLIB
|
||||
- SDL
|
||||
- SDL_mixer
|
||||
- FFmpeg
|
||||
- X11 (with libxxf86vm)
|
||||
- Xrandr
|
||||
|
31
main.cpp
31
main.cpp
@ -22,6 +22,7 @@
|
||||
|
||||
float g_Time;
|
||||
bool g_KeepRunning = true;
|
||||
int g_AudioVolume = 15;
|
||||
|
||||
using namespace WallpaperEngine::Core::Types;
|
||||
|
||||
@ -37,6 +38,7 @@ void print_help (const char* route)
|
||||
<< "Usage:" << route << " [options] background_path" << std::endl
|
||||
<< "options:" << std::endl
|
||||
<< " --silent\t\tMutes all the sound the wallpaper might produce" << std::endl
|
||||
<< " --volume <amount>\tSets the volume for all the sounds in the background" << std::endl
|
||||
<< " --screen-root <screen name>\tDisplay as screen's background" << std::endl
|
||||
<< " --fps <maximum-fps>\tLimits the FPS to the given number, useful to keep battery consumption low" << std::endl
|
||||
<< " --assets-dir <path>\tFolder where the assets are stored" << std::endl;
|
||||
@ -103,6 +105,7 @@ int main (int argc, char* argv[])
|
||||
{"pkg", required_argument, 0, 'p'},
|
||||
{"dir", required_argument, 0, 'd'},
|
||||
{"silent", no_argument, 0, 's'},
|
||||
{"volume", required_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"fps", required_argument, 0, 'f'},
|
||||
{"assets-dir", required_argument, 0, 'a'},
|
||||
@ -143,6 +146,10 @@ int main (int argc, char* argv[])
|
||||
case 'a':
|
||||
assetsDir = optarg;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
g_AudioVolume = atoi (optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,6 +327,12 @@ int main (int argc, char* argv[])
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (shouldEnableAudio == true && SDL_Init (SDL_INIT_AUDIO) < 0)
|
||||
{
|
||||
std::cerr << "Cannot initialize SDL audio system, SDL_GetError: " << SDL_GetError() << std::endl;
|
||||
std::cerr << "Continuing without audio support" << std::endl;
|
||||
}
|
||||
|
||||
// parse the project.json file
|
||||
auto project = WallpaperEngine::Core::CProject::fromFile ("project.json", containers);
|
||||
// go to the right folder so the videos will play
|
||||
@ -337,24 +350,6 @@ int main (int argc, char* argv[])
|
||||
WallpaperEngine::Render::CWallpaper::fromWallpaper (project->getWallpaper (), containers, context)
|
||||
);
|
||||
|
||||
if (shouldEnableAudio == true)
|
||||
{
|
||||
int mixer_flags = MIX_INIT_MP3 | MIX_INIT_FLAC | MIX_INIT_OGG;
|
||||
|
||||
if (SDL_Init (SDL_INIT_AUDIO) < 0 || mixer_flags != Mix_Init (mixer_flags))
|
||||
{
|
||||
// Mix_GetError is an alias for SDL_GetError, so calling it directly will yield the correct result
|
||||
// it doesn't matter if SDL_Init or Mix_Init failed, both report the errors through the same functions
|
||||
std::cerr << "Cannot initialize SDL audio system, SDL_GetError: " << SDL_GetError() << std::endl;
|
||||
std::cerr << "Continuing without audio support" << std::endl;
|
||||
shouldEnableAudio = false;
|
||||
}
|
||||
|
||||
// initialize audio engine if it should be
|
||||
if (shouldEnableAudio)
|
||||
Mix_OpenAudio (22050, AUDIO_S16SYS, 2, 640);
|
||||
}
|
||||
|
||||
// TODO: FIGURE OUT THE REQUIRED INPUT MODE, AS SOME WALLPAPERS USE THINGS LIKE MOUSE POSITION
|
||||
// glfwSetInputMode (window, GLFW_STICKY_KEYS, GL_TRUE);
|
||||
|
||||
|
734
src/WallpaperEngine/Audio/CAudioStream.cpp
Normal file
734
src/WallpaperEngine/Audio/CAudioStream.cpp
Normal file
@ -0,0 +1,734 @@
|
||||
#include "CAudioStream.h"
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
|
||||
extern int g_AudioVolume;
|
||||
|
||||
using namespace WallpaperEngine::Audio;
|
||||
|
||||
// callback for sdl to play our audio
|
||||
void audio_callback (void* userdata, uint8_t* stream, int length)
|
||||
{
|
||||
auto audioStream = static_cast <CAudioStream*> (userdata);
|
||||
int len1, audio_size;
|
||||
|
||||
static uint8_t audio_buf[(MAX_AUDIO_FRAME_SIZE * 3) / 2];
|
||||
static unsigned int audio_buf_size = 0;
|
||||
static unsigned int audio_buf_index = 0;
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
if (audio_buf_index >= audio_buf_size)
|
||||
{
|
||||
/* We have already sent all our data; get more */
|
||||
audio_size = audioStream->decodeFrame (audio_buf, sizeof (audio_buf));
|
||||
|
||||
if (audio_size < 0)
|
||||
{
|
||||
/* If error, output silence */
|
||||
audio_buf_size = 1024; // arbitrary?
|
||||
memset(audio_buf, 0, audio_buf_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
audio_buf_size = audio_size;
|
||||
}
|
||||
|
||||
audio_buf_index = 0;
|
||||
}
|
||||
|
||||
len1 = audio_buf_size - audio_buf_index;
|
||||
|
||||
if (len1 > length)
|
||||
len1 = length;
|
||||
|
||||
// mix the audio so the volume is right
|
||||
SDL_MixAudio (stream, (uint8_t*) audio_buf + audio_buf_index, len1, g_AudioVolume);
|
||||
|
||||
length -= len1;
|
||||
stream += len1;
|
||||
audio_buf_index += len1;
|
||||
|
||||
if (audioStream->isInitialized () == false)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int audio_read_thread (void* arg)
|
||||
{
|
||||
CAudioStream* stream = static_cast <CAudioStream*> (arg);
|
||||
AVPacket* packet = av_packet_alloc ();
|
||||
int ret = 0;
|
||||
|
||||
while (ret >= 0)
|
||||
{
|
||||
ret = av_read_frame (stream->getFormatContext (), packet);
|
||||
|
||||
if (ret == AVERROR_EOF)
|
||||
{
|
||||
// seek to the beginning of the file again
|
||||
av_seek_frame (stream->getFormatContext (), stream->getAudioStream (), 0, AVSEEK_FLAG_FRAME);
|
||||
avcodec_flush_buffers (stream->getContext ());
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: PROPERLY IMPLEMENT THIS
|
||||
if (packet->stream_index == stream->getAudioStream ())
|
||||
stream->queuePacket (packet);
|
||||
else
|
||||
av_packet_unref (packet);
|
||||
|
||||
if (stream->isInitialized () == false)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_read_data_callback (void* streamarg, uint8_t* buffer, int buffer_size)
|
||||
{
|
||||
auto stream = static_cast <CAudioStream*> (streamarg);
|
||||
int left = stream->getLength () - stream->getPosition ();
|
||||
|
||||
buffer_size = FFMIN (buffer_size, left);
|
||||
|
||||
memcpy (buffer, (uint8_t*) stream->getBuffer () + stream->getPosition (), buffer_size);
|
||||
// update position
|
||||
stream->setPosition (stream->getPosition () + buffer_size);
|
||||
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
int64_t audio_seek_data_callback (void* streamarg, int64_t offset, int whence)
|
||||
{
|
||||
auto stream = static_cast <CAudioStream*> (streamarg);
|
||||
|
||||
if (whence & AVSEEK_SIZE)
|
||||
return stream->getLength ();
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_CUR:
|
||||
stream->setPosition (stream->getPosition () + offset);
|
||||
break;
|
||||
|
||||
case SEEK_SET:
|
||||
stream->setPosition (offset);
|
||||
break;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
CAudioStream::CAudioStream (const std::string& filename)
|
||||
{
|
||||
// do not do anything if sdl audio was not initialized
|
||||
if (SDL_WasInit (SDL_INIT_AUDIO) != SDL_INIT_AUDIO)
|
||||
return;
|
||||
|
||||
this->loadCustomContent (filename.c_str ());
|
||||
}
|
||||
|
||||
CAudioStream::CAudioStream (void* buffer, int length)
|
||||
{
|
||||
// do not do anything if sdl audio was not initialized
|
||||
if (SDL_WasInit (SDL_INIT_AUDIO) != SDL_INIT_AUDIO)
|
||||
return;
|
||||
|
||||
// setup a custom context first
|
||||
this->m_formatContext = avformat_alloc_context ();
|
||||
|
||||
if (this->m_formatContext == nullptr)
|
||||
throw std::runtime_error ("Cannot allocate format context");
|
||||
|
||||
this->m_buffer = buffer;
|
||||
this->m_length = length;
|
||||
this->m_position = 0;
|
||||
|
||||
// setup custom io for it
|
||||
this->m_formatContext->pb = avio_alloc_context (
|
||||
static_cast <uint8_t*> (av_malloc (4096)),
|
||||
4096,
|
||||
0,
|
||||
static_cast <void*> (this),
|
||||
&audio_read_data_callback,
|
||||
nullptr,
|
||||
&audio_seek_data_callback
|
||||
);
|
||||
|
||||
if (this->m_formatContext->pb == nullptr)
|
||||
throw std::runtime_error ("Cannot create avio context");
|
||||
|
||||
// continue the normal load procedure
|
||||
this->loadCustomContent ();
|
||||
}
|
||||
|
||||
CAudioStream::CAudioStream(AVCodecContext* context)
|
||||
: m_context (context),
|
||||
m_queue (new PacketQueue)
|
||||
{
|
||||
// do not do anything if sdl audio was not initialized
|
||||
if (SDL_WasInit (SDL_INIT_AUDIO) != SDL_INIT_AUDIO)
|
||||
return;
|
||||
|
||||
this->initialize ();
|
||||
}
|
||||
|
||||
void CAudioStream::loadCustomContent (const char* filename)
|
||||
{
|
||||
const AVCodec* aCodec = nullptr;
|
||||
AVCodecContext* avCodecContext = nullptr;
|
||||
|
||||
if (avformat_open_input (&this->m_formatContext, filename, nullptr, nullptr) != 0)
|
||||
throw std::runtime_error ("Cannot open audio file");
|
||||
if (avformat_find_stream_info (this->m_formatContext, nullptr) < 0)
|
||||
throw std::runtime_error ("Cannot determine file format");
|
||||
|
||||
// find the audio stream
|
||||
for (int i = 0; i< this->m_formatContext->nb_streams; i ++)
|
||||
{
|
||||
if (this->m_formatContext->streams [i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && this->m_audioStream < 0)
|
||||
this->m_audioStream = i;
|
||||
}
|
||||
|
||||
if (this->m_audioStream == -1)
|
||||
throw std::runtime_error ("Cannot find audio stream in file");
|
||||
|
||||
// get the decoder for it and alloc the required context
|
||||
aCodec = avcodec_find_decoder (this->m_formatContext->streams [this->m_audioStream]->codecpar->codec_id);
|
||||
|
||||
if (aCodec == nullptr)
|
||||
throw std::runtime_error ("Cannot initialize audio decoder");
|
||||
|
||||
// alocate context
|
||||
avCodecContext = avcodec_alloc_context3 (aCodec);
|
||||
|
||||
if (avcodec_parameters_to_context (avCodecContext, this->m_formatContext->streams [this->m_audioStream]->codecpar) != 0)
|
||||
throw std::runtime_error ("Cannot initialize audio decoder parameters");
|
||||
|
||||
// finally open
|
||||
avcodec_open2 (avCodecContext, aCodec, nullptr);
|
||||
|
||||
// initialize default data
|
||||
this->m_context = avCodecContext;
|
||||
this->m_queue = new PacketQueue;
|
||||
|
||||
this->initialize ();
|
||||
|
||||
// initialize an SDL thread to read the file
|
||||
SDL_CreateThread (audio_read_thread, this);
|
||||
}
|
||||
|
||||
void CAudioStream::initialize ()
|
||||
{
|
||||
// allocate the FIFO buffer
|
||||
this->m_queue->packetList = av_fifo_alloc (sizeof (MyAVPacketList));
|
||||
|
||||
// setup the queue information
|
||||
this->m_queue->mutex = SDL_CreateMutex ();
|
||||
this->m_queue->cond = SDL_CreateCond ();
|
||||
|
||||
// take control of the audio device
|
||||
|
||||
SDL_AudioSpec requestedSpec, finalSpec;
|
||||
|
||||
// Set audio settings from codec info
|
||||
requestedSpec.freq = this->m_context->sample_rate;
|
||||
requestedSpec.format = AUDIO_S16SYS;
|
||||
requestedSpec.channels = this->m_context->channels;
|
||||
requestedSpec.silence = 0;
|
||||
requestedSpec.samples = SDL_AUDIO_BUFFER_SIZE;
|
||||
requestedSpec.callback = audio_callback;
|
||||
requestedSpec.userdata = this;
|
||||
|
||||
if (SDL_OpenAudio (&requestedSpec, &finalSpec) < 0) {
|
||||
std::cerr << "SDL_OpenAudio: " << SDL_GetError () << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_PauseAudio (0);
|
||||
|
||||
this->m_initialized = true;
|
||||
}
|
||||
|
||||
void CAudioStream::queuePacket(AVPacket *pkt)
|
||||
{
|
||||
// clone the packet
|
||||
AVPacket* clone = av_packet_alloc ();
|
||||
|
||||
if (clone == nullptr)
|
||||
{
|
||||
av_packet_unref (clone);
|
||||
return;
|
||||
}
|
||||
|
||||
av_packet_move_ref (clone, pkt);
|
||||
|
||||
SDL_LockMutex (this->m_queue->mutex);
|
||||
bool gotQueued = this->doQueue (clone);
|
||||
SDL_UnlockMutex (this->m_queue->mutex);
|
||||
|
||||
if (gotQueued == false)
|
||||
av_packet_free (&pkt);
|
||||
}
|
||||
|
||||
bool CAudioStream::doQueue (AVPacket* pkt)
|
||||
{
|
||||
MyAVPacketList entry;
|
||||
|
||||
// ensure the FIFO has enough space to hold the new entry
|
||||
if (av_fifo_space (this->m_queue->packetList) < sizeof (entry))
|
||||
{
|
||||
if (av_fifo_grow (this->m_queue->packetList, sizeof (entry)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
entry.packet = pkt;
|
||||
|
||||
// write to the FIFO
|
||||
av_fifo_generic_write (this->m_queue->packetList, &entry, sizeof (entry), nullptr);
|
||||
|
||||
this->m_queue->nb_packets ++;
|
||||
this->m_queue->size += entry.packet->size + sizeof (entry);
|
||||
this->m_queue->duration += entry.packet->duration;
|
||||
|
||||
SDL_CondSignal (this->m_queue->cond);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CAudioStream::dequeuePacket (AVPacket* output)
|
||||
{
|
||||
MyAVPacketList entry;
|
||||
|
||||
SDL_LockMutex (this->m_queue->mutex);
|
||||
|
||||
while (true)
|
||||
{
|
||||
// enough data available, read it
|
||||
if (av_fifo_size (this->m_queue->packetList) >= sizeof (entry))
|
||||
{
|
||||
av_fifo_generic_read (this->m_queue->packetList, &entry, sizeof (entry), nullptr);
|
||||
|
||||
this->m_queue->nb_packets --;
|
||||
this->m_queue->size -= entry.packet->duration;
|
||||
|
||||
// move the reference and free the old one
|
||||
av_packet_move_ref (output, entry.packet);
|
||||
av_packet_free (&entry.packet);
|
||||
break;
|
||||
}
|
||||
|
||||
// make the thread wait if nothing was available
|
||||
SDL_CondWait (this->m_queue->cond, this->m_queue->mutex);
|
||||
}
|
||||
|
||||
SDL_UnlockMutex (this->m_queue->mutex);
|
||||
}
|
||||
|
||||
AVCodecContext* CAudioStream::getContext ()
|
||||
{
|
||||
return this->m_context;
|
||||
}
|
||||
|
||||
AVFormatContext* CAudioStream::getFormatContext ()
|
||||
{
|
||||
return this->m_formatContext;
|
||||
}
|
||||
|
||||
int CAudioStream::getAudioStream ()
|
||||
{
|
||||
return this->m_audioStream;
|
||||
}
|
||||
|
||||
bool CAudioStream::isInitialized ()
|
||||
{
|
||||
return this->m_initialized;
|
||||
}
|
||||
|
||||
void CAudioStream::setRepeat (bool newRepeat)
|
||||
{
|
||||
this->m_repeat = newRepeat;
|
||||
}
|
||||
|
||||
bool CAudioStream::isRepeat ()
|
||||
{
|
||||
return this->m_repeat;
|
||||
}
|
||||
|
||||
void* CAudioStream::getBuffer ()
|
||||
{
|
||||
return this->m_buffer;
|
||||
}
|
||||
|
||||
int CAudioStream::getLength ()
|
||||
{
|
||||
return this->m_length;
|
||||
}
|
||||
|
||||
int CAudioStream::getPosition ()
|
||||
{
|
||||
return this->m_position;
|
||||
}
|
||||
|
||||
void CAudioStream::setPosition (int current)
|
||||
{
|
||||
this->m_position = current;
|
||||
}
|
||||
|
||||
void CAudioStream::stop ()
|
||||
{
|
||||
if (this->isInitialized () == false)
|
||||
return;
|
||||
|
||||
// pause audio
|
||||
SDL_PauseAudio (1);
|
||||
|
||||
// stop the threads running
|
||||
this->m_initialized = false;
|
||||
}
|
||||
|
||||
int CAudioStream::resampleAudio (
|
||||
AVFrame * decoded_audio_frame,
|
||||
enum AVSampleFormat out_sample_fmt,
|
||||
int out_channels,
|
||||
int out_sample_rate,
|
||||
uint8_t * out_buf
|
||||
)
|
||||
{
|
||||
SwrContext * swr_ctx = NULL;
|
||||
int ret = 0;
|
||||
int64_t in_channel_layout = this->getContext ()->channel_layout;
|
||||
int64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
int out_nb_channels = 0;
|
||||
int out_linesize = 0;
|
||||
int in_nb_samples = 0;
|
||||
int out_nb_samples = 0;
|
||||
int max_out_nb_samples = 0;
|
||||
uint8_t ** resampled_data = NULL;
|
||||
int resampled_data_size = 0;
|
||||
|
||||
swr_ctx = swr_alloc();
|
||||
|
||||
if (!swr_ctx)
|
||||
{
|
||||
printf("swr_alloc error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get input audio channels
|
||||
in_channel_layout = (this->getContext ()->channels ==
|
||||
av_get_channel_layout_nb_channels(this->getContext ()->channel_layout)) ? // 2
|
||||
this->getContext ()->channel_layout :
|
||||
av_get_default_channel_layout(this->getContext ()->channels);
|
||||
|
||||
// check input audio channels correctly retrieved
|
||||
if (in_channel_layout <= 0)
|
||||
{
|
||||
printf("in_channel_layout error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// set output audio channels based on the input audio channels
|
||||
if (out_channels == 1)
|
||||
{
|
||||
out_channel_layout = AV_CH_LAYOUT_MONO;
|
||||
}
|
||||
else if (out_channels == 2)
|
||||
{
|
||||
out_channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_channel_layout = AV_CH_LAYOUT_SURROUND;
|
||||
}
|
||||
|
||||
// retrieve number of audio samples (per channel)
|
||||
in_nb_samples = decoded_audio_frame->nb_samples;
|
||||
if (in_nb_samples <= 0)
|
||||
{
|
||||
printf("in_nb_samples error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set SwrContext parameters for resampling
|
||||
av_opt_set_int( // 3
|
||||
swr_ctx,
|
||||
"in_channel_layout",
|
||||
in_channel_layout,
|
||||
0
|
||||
);
|
||||
|
||||
// Set SwrContext parameters for resampling
|
||||
av_opt_set_int(
|
||||
swr_ctx,
|
||||
"in_sample_rate",
|
||||
this->getContext ()->sample_rate,
|
||||
0
|
||||
);
|
||||
|
||||
// Set SwrContext parameters for resampling
|
||||
av_opt_set_sample_fmt(
|
||||
swr_ctx,
|
||||
"in_sample_fmt",
|
||||
this->getContext ()->sample_fmt,
|
||||
0
|
||||
);
|
||||
|
||||
// Set SwrContext parameters for resampling
|
||||
av_opt_set_int(
|
||||
swr_ctx,
|
||||
"out_channel_layout",
|
||||
out_channel_layout,
|
||||
0
|
||||
);
|
||||
|
||||
// Set SwrContext parameters for resampling
|
||||
av_opt_set_int(
|
||||
swr_ctx,
|
||||
"out_sample_rate",
|
||||
out_sample_rate,
|
||||
0
|
||||
);
|
||||
|
||||
// Set SwrContext parameters for resampling
|
||||
av_opt_set_sample_fmt(
|
||||
swr_ctx,
|
||||
"out_sample_fmt",
|
||||
out_sample_fmt,
|
||||
0
|
||||
);
|
||||
|
||||
// Once all values have been set for the SwrContext, it must be initialized
|
||||
// with swr_init().
|
||||
ret = swr_init(swr_ctx);;
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("Failed to initialize the resampling context.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
max_out_nb_samples = out_nb_samples = av_rescale_rnd(
|
||||
in_nb_samples,
|
||||
out_sample_rate,
|
||||
this->getContext ()->sample_rate,
|
||||
AV_ROUND_UP
|
||||
);
|
||||
|
||||
// check rescaling was successful
|
||||
if (max_out_nb_samples <= 0)
|
||||
{
|
||||
printf("av_rescale_rnd error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get number of output audio channels
|
||||
out_nb_channels = av_get_channel_layout_nb_channels(out_channel_layout);
|
||||
|
||||
ret = av_samples_alloc_array_and_samples(
|
||||
&resampled_data,
|
||||
&out_linesize,
|
||||
out_nb_channels,
|
||||
out_nb_samples,
|
||||
out_sample_fmt,
|
||||
0
|
||||
);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("av_samples_alloc_array_and_samples() error: Could not allocate destination samples.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// retrieve output samples number taking into account the progressive delay
|
||||
out_nb_samples = av_rescale_rnd(
|
||||
swr_get_delay(swr_ctx, this->getContext ()->sample_rate) + in_nb_samples,
|
||||
out_sample_rate,
|
||||
this->getContext ()->sample_rate,
|
||||
AV_ROUND_UP
|
||||
);
|
||||
|
||||
// check output samples number was correctly retrieved
|
||||
if (out_nb_samples <= 0)
|
||||
{
|
||||
printf("av_rescale_rnd error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (out_nb_samples > max_out_nb_samples)
|
||||
{
|
||||
// free memory block and set pointer to NULL
|
||||
av_free(resampled_data[0]);
|
||||
|
||||
// Allocate a samples buffer for out_nb_samples samples
|
||||
ret = av_samples_alloc(
|
||||
resampled_data,
|
||||
&out_linesize,
|
||||
out_nb_channels,
|
||||
out_nb_samples,
|
||||
out_sample_fmt,
|
||||
1
|
||||
);
|
||||
|
||||
// check samples buffer correctly allocated
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("av_samples_alloc failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
max_out_nb_samples = out_nb_samples;
|
||||
}
|
||||
|
||||
if (swr_ctx)
|
||||
{
|
||||
// do the actual audio data resampling
|
||||
ret = swr_convert(
|
||||
swr_ctx,
|
||||
resampled_data,
|
||||
out_nb_samples,
|
||||
(const uint8_t **) decoded_audio_frame->data,
|
||||
decoded_audio_frame->nb_samples
|
||||
);
|
||||
|
||||
// check audio conversion was successful
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("swr_convert_error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the required buffer size for the given audio parameters
|
||||
resampled_data_size = av_samples_get_buffer_size(
|
||||
&out_linesize,
|
||||
out_nb_channels,
|
||||
ret,
|
||||
out_sample_fmt,
|
||||
1
|
||||
);
|
||||
|
||||
// check audio buffer size
|
||||
if (resampled_data_size < 0)
|
||||
{
|
||||
printf("av_samples_get_buffer_size error.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("swr_ctx null error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// copy the resampled data to the output buffer
|
||||
memcpy(out_buf, resampled_data[0], resampled_data_size);
|
||||
|
||||
/*
|
||||
* Memory Cleanup.
|
||||
*/
|
||||
if (resampled_data)
|
||||
{
|
||||
// free memory block and set pointer to NULL
|
||||
av_freep(&resampled_data[0]);
|
||||
}
|
||||
|
||||
av_freep(&resampled_data);
|
||||
resampled_data = NULL;
|
||||
|
||||
if (swr_ctx)
|
||||
{
|
||||
// Free the given SwrContext and set the pointer to NULL
|
||||
swr_free(&swr_ctx);
|
||||
}
|
||||
|
||||
return resampled_data_size;
|
||||
}
|
||||
|
||||
int CAudioStream::decodeFrame (uint8_t* audioBuffer, int bufferSize)
|
||||
{
|
||||
AVPacket *pkt = av_packet_alloc ();
|
||||
static uint8_t *audio_pkt_data = NULL;
|
||||
static int audio_pkt_size = 0;
|
||||
|
||||
int len1, data_size = 0;
|
||||
|
||||
// allocate a new frame, used to decode audio packets
|
||||
static AVFrame * avFrame = NULL;
|
||||
avFrame = av_frame_alloc();
|
||||
if (!avFrame)
|
||||
{
|
||||
printf("Could not allocate AVFrame.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
while (audio_pkt_size > 0) {
|
||||
int got_frame = 0;
|
||||
int ret = avcodec_receive_frame(this->getContext (), avFrame);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
got_frame = 1;
|
||||
}
|
||||
if (ret == AVERROR(EAGAIN))
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = avcodec_send_packet(this->getContext (), pkt);
|
||||
}
|
||||
if (ret == AVERROR(EAGAIN))
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len1 = pkt->size;
|
||||
}
|
||||
|
||||
if (len1 < 0)
|
||||
{
|
||||
// if error, skip frame
|
||||
audio_pkt_size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
audio_pkt_data += len1;
|
||||
audio_pkt_size -= len1;
|
||||
data_size = 0;
|
||||
|
||||
if (got_frame) {
|
||||
// audio resampling
|
||||
data_size = this->resampleAudio (
|
||||
avFrame,
|
||||
AV_SAMPLE_FMT_S16,
|
||||
this->getContext ()->channels,
|
||||
this->getContext ()->sample_rate,
|
||||
audioBuffer
|
||||
);
|
||||
assert(data_size <= bufferSize);
|
||||
}
|
||||
if (data_size <= 0) {
|
||||
/* No data yet, get more frames */
|
||||
continue;
|
||||
}
|
||||
/* We have data, return it and come back for more later */
|
||||
return data_size;
|
||||
}
|
||||
if (pkt->data)
|
||||
av_packet_unref(pkt);
|
||||
|
||||
this->dequeuePacket (pkt);
|
||||
|
||||
audio_pkt_data = pkt->data;
|
||||
audio_pkt_size = pkt->size;
|
||||
}
|
||||
}
|
88
src/WallpaperEngine/Audio/CAudioStream.h
Normal file
88
src/WallpaperEngine/Audio/CAudioStream.h
Normal file
@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <libavutil/fifo.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavutil/opt.h>
|
||||
};
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
|
||||
#define SDL_AUDIO_BUFFER_SIZE 1024
|
||||
#define MAX_AUDIO_FRAME_SIZE 192000
|
||||
|
||||
namespace WallpaperEngine
|
||||
{
|
||||
namespace Audio
|
||||
{
|
||||
class CAudioStream
|
||||
{
|
||||
public:
|
||||
CAudioStream (const std::string& filename);
|
||||
CAudioStream (void* buffer, int length);
|
||||
CAudioStream (AVCodecContext* context);
|
||||
|
||||
void queuePacket (AVPacket* pkt);
|
||||
|
||||
/**
|
||||
* Gets the next packet in the queue
|
||||
*
|
||||
* WARNING: BLOCKS UNTIL SOME DATA IS READ FROM IT
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
void dequeuePacket (AVPacket* output);
|
||||
|
||||
AVCodecContext* getContext ();
|
||||
AVFormatContext* getFormatContext ();
|
||||
int getAudioStream ();
|
||||
bool isInitialized ();
|
||||
void setRepeat (bool newRepeat = true);
|
||||
bool isRepeat ();
|
||||
void stop ();
|
||||
void* getBuffer ();
|
||||
int getLength ();
|
||||
int getPosition ();
|
||||
void setPosition (int current);
|
||||
|
||||
int decodeFrame (uint8_t* audioBuffer, int bufferSize);
|
||||
|
||||
private:
|
||||
void loadCustomContent (const char* filename = nullptr);
|
||||
int resampleAudio (AVFrame * decoded_audio_frame, enum AVSampleFormat out_sample_fmt, int out_channels, int out_sample_rate, uint8_t* out_buf);
|
||||
bool doQueue (AVPacket* pkt);
|
||||
void initialize ();
|
||||
|
||||
bool m_initialized;
|
||||
bool m_repeat;
|
||||
AVCodecContext* m_context = nullptr;
|
||||
AVFormatContext* m_formatContext = nullptr;
|
||||
int m_audioStream = -1;
|
||||
void* m_buffer;
|
||||
int m_length;
|
||||
int m_position = 0;
|
||||
|
||||
struct MyAVPacketList
|
||||
{
|
||||
AVPacket *packet;
|
||||
};
|
||||
|
||||
struct PacketQueue
|
||||
{
|
||||
AVFifoBuffer* packetList;
|
||||
int nb_packets;
|
||||
int size;
|
||||
int64_t duration;
|
||||
SDL_mutex *mutex;
|
||||
SDL_cond *cond;
|
||||
} *m_queue;
|
||||
};
|
||||
}
|
||||
};
|
@ -1,6 +1,4 @@
|
||||
#include <SDL.h>
|
||||
#include <SDL_rwops.h>
|
||||
#include <SDL_mixer.h>
|
||||
|
||||
#include "CSound.h"
|
||||
|
||||
@ -11,16 +9,10 @@ CSound::CSound (CScene* scene, Core::Objects::CSound* sound) :
|
||||
m_sound (sound)
|
||||
{
|
||||
this->load ();
|
||||
this->play ();
|
||||
}
|
||||
|
||||
void CSound::load ()
|
||||
{
|
||||
if (SDL_WasInit (SDL_INIT_AUDIO) != SDL_INIT_AUDIO)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto cur = this->m_sound->getSounds ().begin ();
|
||||
auto end = this->m_sound->getSounds ().end ();
|
||||
|
||||
@ -29,31 +21,8 @@ void CSound::load ()
|
||||
uint32_t filesize = 0;
|
||||
void* filebuffer = this->getContainer ()->readFile ((*cur), &filesize);
|
||||
|
||||
SDL_RWops* sdlRwops = SDL_RWFromConstMem (filebuffer, filesize);
|
||||
Mix_Music* music = Mix_LoadMUS_RW (sdlRwops);
|
||||
|
||||
if (music == nullptr)
|
||||
throw std::runtime_error ("cannot load audio");
|
||||
|
||||
this->m_bufferReader.push_back (sdlRwops);
|
||||
this->m_audioStreams.push_back (new Audio::CAudioStream (filebuffer, filesize));
|
||||
this->m_soundBuffer.push_back (filebuffer);
|
||||
this->m_sdl.push_back (music);
|
||||
}
|
||||
}
|
||||
void CSound::play ()
|
||||
{
|
||||
if (SDL_WasInit (SDL_INIT_AUDIO) != SDL_INIT_AUDIO)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto mixcur = this->m_sdl.begin ();
|
||||
auto mixend = this->m_sdl.end ();
|
||||
|
||||
for (; mixcur != mixend; mixcur ++)
|
||||
{
|
||||
if (Mix_PlayMusic ((*mixcur), -1) == -1)
|
||||
throw std::runtime_error ("cannot play audio");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL_rwops.h>
|
||||
#include <SDL_mixer.h>
|
||||
|
||||
#include "WallpaperEngine/Core/Objects/CSound.h"
|
||||
|
||||
#include "WallpaperEngine/Render/CObject.h"
|
||||
#include "WallpaperEngine/Audio/CAudioStream.h"
|
||||
|
||||
using namespace WallpaperEngine;
|
||||
|
||||
@ -22,13 +20,10 @@ namespace WallpaperEngine::Render::Objects
|
||||
static const std::string Type;
|
||||
|
||||
void load ();
|
||||
void play ();
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_filenames;
|
||||
std::vector <Mix_Music*> m_sdl;
|
||||
std::vector <SDL_RWops*> m_bufferReader;
|
||||
std::vector <void*> m_soundBuffer;
|
||||
std::vector <Audio::CAudioStream*> m_audioStreams;
|
||||
|
||||
Core::Objects::CSound* m_sound;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user