-
Notifications
You must be signed in to change notification settings - Fork 0
Source: Audio Player
eXpl0it3r edited this page Oct 18, 2012
·
1 revision
By MickaGL
Works exclusively with the latest revision of SFML 2.
Here is a class using the library ffmpeg which allows playback of audio files.
Works on the same principle that sf:: Music
Inspired by the source code of the tutorial found on this page alFFmpeg : http://kcat.strangesoft.net/openal.html
Media.h [Top]
#ifndef MEDIA_H_INCLUDED
#define MEDIA_H_INCLUDED
#include <SFML/Audio.hpp>
extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
namespace sfe
{
class Media : public sf::SoundStream
{
public :
Media();
~Media();
bool OpenFromFile(const std::string& filename);
private :
int GetAVAudioData(void* data, int length);
void GetNextPacket();
bool OnGetData(Chunk& data);
void OnSeek(sf::Time timeOffset);
AVFormatContext* myFormatCtx;
int myAudioStream;
char* mySamples;
char* myData;
size_t myDataSize;
size_t myDataSizeMax;
};
} // namespace sfe
#endif // MEDIA_H_INCLUDED
Media.cpp [Top]
#include "Media.h"
#include <iostream>
namespace sfe
{
Media::Media() :
myFormatCtx (NULL),
myAudioStream (-1),
mySamples (NULL),
myData (NULL),
myDataSize (0),
myDataSizeMax (0)
{
av_register_all();
}
Media::~Media()
{
Stop();
if (myFormatCtx)
{
for (unsigned int i=0; i<myFormatCtx->nb_streams; i++)
{
if (myFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
if (myData)
av_free(myData);
if (mySamples)
{
delete [] mySamples;
mySamples = NULL;
}
avcodec_close(myFormatCtx->streams[i]->codec);
}
}
avformat_free_context(myFormatCtx);
}
}
bool Media::OpenFromFile(const std::string& filename)
{
if (avformat_open_input(&myFormatCtx, filename.c_str(), NULL, NULL) != 0)
{
std::cerr << "Unexisting file!" << std::endl;
return false;
}
if (avformat_find_stream_info(myFormatCtx, NULL) < 0)
{
std::cerr << "Couldn't find stream information!" << std::endl;
return false;
}
for (unsigned int i=0; i<myFormatCtx->nb_streams; i++)
{
if (myFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && myAudioStream < 0)
{
AVCodec* codec = avcodec_find_decoder(myFormatCtx->streams[i]->codec->codec_id);
if (!codec)
std::cerr << "Unsupported codec!" << std::endl;
else if (avcodec_open2(myFormatCtx->streams[i]->codec, codec, NULL) < 0)
std::cerr << "Couldn't open audio codec context!" << std::endl;
else
{
mySamples = new char[19200];
if (!mySamples)
std::cerr << "Out of memory allocating temp buffer!" << std::endl;
else
myAudioStream = i;
Initialize(myFormatCtx->streams[i]->codec->channels, myFormatCtx->streams[i]->codec->sample_rate);
}
}
}
if (myAudioStream == -1)
return false;
return true;
}
int Media::GetAVAudioData(void* data, int length)
{
static size_t decodedDataSize = 0;
static char decodedData[AVCODEC_MAX_AUDIO_FRAME_SIZE];
int dec = 0;
while(dec < length)
{
if (decodedDataSize > 0)
{
size_t rem = length - dec;
if (rem > decodedDataSize)
rem = decodedDataSize;
memcpy(data, decodedData, rem);
data = (char*)data + rem;
dec += rem;
if (rem < decodedDataSize)
memmove(decodedData, &decodedData[rem], decodedDataSize - rem);
decodedDataSize -= rem;
}
if (decodedDataSize == 0)
{
size_t insize = myDataSize;
if (insize == 0)
{
GetNextPacket();
if (insize == myDataSize)
break;
insize = myDataSize;
memset(&myData[insize], 0, FF_INPUT_BUFFER_PADDING_SIZE);
}
int size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
int len;
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = (unsigned char*)myData;
pkt.size = insize;
while((len = avcodec_decode_audio3(myFormatCtx->streams[myAudioStream]->codec, (int16_t*)decodedData,
&size, &pkt)) == 0)
{
if (size > 0)
break;
GetNextPacket();
if (insize == myDataSize)
break;
insize = myDataSize;
memset(&myData[insize], 0, FF_INPUT_BUFFER_PADDING_SIZE);
}
if (len < 0)
break;
if (len > 0)
{
size_t rem = insize - len;
if (rem)
memmove(myData, &myData[len], rem);
myDataSize = rem;
}
decodedDataSize = size;
}
}
return dec;
}
void Media::GetNextPacket()
{
AVPacket packet;
while(av_read_frame(myFormatCtx, &packet) >= 0)
{
for (unsigned int i=0; i<myFormatCtx->nb_streams; i++)
{
if (myAudioStream == packet.stream_index)
{
size_t idx = myDataSize;
if (idx + packet.size > myDataSizeMax)
{
void *temp = av_realloc(myData, idx+packet.size + FF_INPUT_BUFFER_PADDING_SIZE);
if (!temp)
return;
myData = (char*)temp;
myDataSizeMax = idx+packet.size;
}
memcpy(&myData[idx], packet.data, packet.size);
myDataSize += packet.size;
return;
}
else
av_free_packet(&packet);
}
}
}
bool Media::OnGetData(Chunk &data)
{
if (myAudioStream >= 0)
{
int done = GetAVAudioData(mySamples, 19200);
data.Samples = (sf::Int16*)mySamples;
data.SampleCount = done/sizeof(sf::Int16);
return (data.SampleCount > 0);
}
else
return false;
}
void Media::OnSeek(float timeOffset)
{
}
} // namespace sfe
main.cpp [Top]
#include <SFML/Graphics.hpp>
#include "Media.h"
int main()
{
sf::RenderWindow application(sf::VideoMode(640, 480), "ffmpeg avec OpenAL", sf::Style::Close);
sfe::Media media;
if (!media.OpenFromFile("data/audio.mp3"))
exit(EXIT_FAILURE);
media.Play();
while (application.IsOpen())
{
sf::Event evenement;
while (application.PollEvent(evenement))
{
if (evenement.Type == sf::Event::Closed)
application.Close();
if ((evenement.Type == sf::Event::KeyPressed) && (evenement.Key.Code == sf::Keyboard::Escape))
application.Close();
if ((evenement.Type == sf::Event::KeyPressed) && (evenement.Key.Code == sf::Keyboard::P))
{
if (media.GetStatus() != sf::SoundStream::Paused)
media.Pause();
else
media.Play();
}
}
if (media.GetStatus() != sf::SoundStream::Playing && media.GetStatus() != sf::SoundStream::Paused)
application.Close();
application.Clear();
application.Display();
}
return EXIT_SUCCESS;
}