|
|
|
@ -15,9 +15,136 @@
|
|
|
|
|
#include <etk/tool.hpp>
|
|
|
|
|
#include <egami/egami.hpp>
|
|
|
|
|
|
|
|
|
|
// TODO: Remove this ==> this is bad ...
|
|
|
|
|
ememory::SharedPtr<appl::widget::VideoDisplay> g_diplayElement;
|
|
|
|
|
static void unPlanar(void* _buffer, int32_t _len, audio::format _format, int32_t _nbChannel) {
|
|
|
|
|
if (_nbChannel == 1) {
|
|
|
|
|
// nothing to do only one channel ...
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
std::vector<uint8_t> tmpData;
|
|
|
|
|
tmpData.resize(_len);
|
|
|
|
|
memcpy(&tmpData[0], _buffer, _len);
|
|
|
|
|
// depend on the type of flow:
|
|
|
|
|
switch(_format) {
|
|
|
|
|
case audio::format_int8:
|
|
|
|
|
{
|
|
|
|
|
uint8_t* in = reinterpret_cast<uint8_t*>(&tmpData[0]);
|
|
|
|
|
uint8_t* out = reinterpret_cast<uint8_t*>(_buffer);
|
|
|
|
|
int32_t nbSample = _len/static_cast<int32_t>(sizeof(int8_t)*_nbChannel);
|
|
|
|
|
for (int32_t iii=0; iii<nbSample; ++iii) {
|
|
|
|
|
for (int32_t jjj=0; jjj<_nbChannel; ++jjj) {
|
|
|
|
|
out[iii*_nbChannel + jjj] = in[jjj*nbSample+iii];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
case audio::format_int16:
|
|
|
|
|
{
|
|
|
|
|
int16_t* in = reinterpret_cast<int16_t*>(&tmpData[0]);
|
|
|
|
|
int16_t* out = reinterpret_cast<int16_t*>(_buffer);
|
|
|
|
|
int32_t nbSample = _len/static_cast<int32_t>(sizeof(int16_t)*_nbChannel);
|
|
|
|
|
for (int32_t iii=0; iii<nbSample; ++iii) {
|
|
|
|
|
for (int32_t jjj=0; jjj<_nbChannel; ++jjj) {
|
|
|
|
|
out[iii*_nbChannel + jjj] = in[jjj*nbSample+iii];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
case audio::format_int32:
|
|
|
|
|
{
|
|
|
|
|
int32_t* in = reinterpret_cast<int32_t*>(&tmpData[0]);
|
|
|
|
|
int32_t* out = reinterpret_cast<int32_t*>(_buffer);
|
|
|
|
|
int32_t nbSample = _len/static_cast<int32_t>(sizeof(int32_t)*_nbChannel);
|
|
|
|
|
for (int32_t iii=0; iii<nbSample; ++iii) {
|
|
|
|
|
for (int32_t jjj=0; jjj<_nbChannel; ++jjj) {
|
|
|
|
|
out[iii*_nbChannel + jjj] = in[jjj*nbSample+iii];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
case audio::format_float:
|
|
|
|
|
{
|
|
|
|
|
float* in = reinterpret_cast<float*>(&tmpData[0]);
|
|
|
|
|
float* out = reinterpret_cast<float*>(_buffer);
|
|
|
|
|
int32_t nbSample = _len/static_cast<int32_t>(sizeof(float)*_nbChannel);
|
|
|
|
|
for (int32_t iii=0; iii<nbSample; ++iii) {
|
|
|
|
|
for (int32_t jjj=0; jjj<_nbChannel; ++jjj) {
|
|
|
|
|
out[iii*_nbChannel + jjj] = in[jjj*nbSample+iii];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
case audio::format_double:
|
|
|
|
|
{
|
|
|
|
|
double* in = reinterpret_cast<double*>(&tmpData[0]);
|
|
|
|
|
double* out = reinterpret_cast<double*>(_buffer);
|
|
|
|
|
int32_t nbSample = _len/static_cast<int32_t>(sizeof(double)*_nbChannel);
|
|
|
|
|
for (int32_t iii=0; iii<nbSample; ++iii) {
|
|
|
|
|
for (int32_t jjj=0; jjj<_nbChannel; ++jjj) {
|
|
|
|
|
out[iii*_nbChannel + jjj] = in[jjj*nbSample+iii];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief get the next power 2 if the input
|
|
|
|
|
* @param[in] value Value that we want the next power of 2
|
|
|
|
|
* @return result value
|
|
|
|
|
*/
|
|
|
|
|
static int32_t nextP2(int32_t _value) {
|
|
|
|
|
int32_t val=1;
|
|
|
|
|
for (int32_t iii=1; iii<31; iii++) {
|
|
|
|
|
if (_value <= val) {
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
val *=2;
|
|
|
|
|
}
|
|
|
|
|
EWOL_CRITICAL("impossible CASE....");
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appl::BufferElementVideo::setSize(const ivec2& _size) {
|
|
|
|
|
if (m_imagerealSize != _size) {
|
|
|
|
|
// Resize the buffer:
|
|
|
|
|
m_imagerealSize = _size;
|
|
|
|
|
m_image.resize(ivec2(nextP2(_size.x()), nextP2(_size.y())));
|
|
|
|
|
m_lineSize = m_image.getSize().x() * 3; // 3 is for RGBA
|
|
|
|
|
//m_image.getSize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void appl::BufferElementAudio::configure(audio::format _format, uint32_t _sampleRate, int32_t _nbChannel, int32_t _nbSample) {
|
|
|
|
|
// resize the buffer:
|
|
|
|
|
m_buffer.resize(_nbSample*_nbChannel*audio::getFormatBytes(_format));
|
|
|
|
|
m_format = _format;
|
|
|
|
|
m_sampleRate = _sampleRate;
|
|
|
|
|
m_map.resize(_nbChannel);
|
|
|
|
|
switch(_nbChannel) {
|
|
|
|
|
case 1:
|
|
|
|
|
m_map[0] = audio::channel_frontCenter;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
m_map[0] = audio::channel_frontLeft;
|
|
|
|
|
m_map[1] = audio::channel_frontRight;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
m_map[0] = audio::channel_frontLeft;
|
|
|
|
|
m_map[1] = audio::channel_lfe;
|
|
|
|
|
m_map[2] = audio::channel_frontRight;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
m_map[0] = audio::channel_frontLeft;
|
|
|
|
|
m_map[1] = audio::channel_frontRight;
|
|
|
|
|
m_map[2] = audio::channel_rearLeft;
|
|
|
|
|
m_map[3] = audio::channel_rearRight;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
APPL_ERROR("not supportef nbChannel" << _nbChannel);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appl::Decoder::Decoder() {
|
|
|
|
|
m_formatContext = nullptr;
|
|
|
|
@ -27,8 +154,6 @@ appl::Decoder::Decoder() {
|
|
|
|
|
m_videoStream = nullptr;
|
|
|
|
|
m_audioStream = nullptr;
|
|
|
|
|
|
|
|
|
|
m_videoDestinationData[0] = nullptr;
|
|
|
|
|
|
|
|
|
|
m_videoDestinationRGBData[0] = nullptr;
|
|
|
|
|
|
|
|
|
|
m_videoStream_idx = -1;
|
|
|
|
@ -39,7 +164,8 @@ appl::Decoder::Decoder() {
|
|
|
|
|
|
|
|
|
|
// output format convertion:
|
|
|
|
|
m_convertContext = nullptr;
|
|
|
|
|
|
|
|
|
|
m_audioPresent = false;
|
|
|
|
|
m_audioFormat = audio::format_unknow;
|
|
|
|
|
// Enable or disable frame reference counting.
|
|
|
|
|
// You are not supposed to support both paths in your application but pick the one most appropriate to your needs.
|
|
|
|
|
// Look for the use of refcount in this example to see what are the differences of API usage between them.
|
|
|
|
@ -67,23 +193,29 @@ int appl::Decoder::decode_packet(int *_gotFrame, int _cached) {
|
|
|
|
|
APPL_ERROR("new: size=" << ivec2(m_frame->width,m_frame->height) << " format=" << av_get_pix_fmt_name((enum AVPixelFormat)m_frame->format));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
APPL_INFO("video_frame " << (_cached?"(cached)":"")
|
|
|
|
|
<< " n=" << m_videoFrameCount
|
|
|
|
|
<< " coded_n=" << m_frame->coded_picture_number
|
|
|
|
|
<< " pts=" << av_ts2timestr(m_frame->pts, &m_videoDecoderContext->time_base));
|
|
|
|
|
APPL_VERBOSE("video_frame " << (_cached?"(cached)":"")
|
|
|
|
|
<< " n=" << m_videoFrameCount
|
|
|
|
|
<< " coded_n=" << m_frame->coded_picture_number
|
|
|
|
|
<< " pts=" << av_ts2timestr(m_frame->pts, &m_videoDecoderContext->time_base));
|
|
|
|
|
m_videoFrameCount++;
|
|
|
|
|
// copy decoded frame to destination buffer: this is required since rawvideo expects non aligned data
|
|
|
|
|
if (false) {
|
|
|
|
|
av_image_copy(m_videoDestinationData, m_videoDestinationLineSize,
|
|
|
|
|
(const uint8_t **)(m_frame->data), m_frame->linesize,
|
|
|
|
|
m_pixelFormat, m_size.x(), m_size.y());
|
|
|
|
|
int32_t slotId = videoGetEmptySlot();
|
|
|
|
|
if (slotId == -1) {
|
|
|
|
|
APPL_ERROR("an error occured when getting an empty slot for video");
|
|
|
|
|
} else {
|
|
|
|
|
m_videoPool[slotId].setSize(ivec2(m_frame->width, m_frame->height));
|
|
|
|
|
uint8_t* dataPointer = (uint8_t*)(m_videoPool[slotId].m_image.getTextureDataPointer());
|
|
|
|
|
// Convert Image in RGB:
|
|
|
|
|
sws_scale(m_convertContext, (const uint8_t **)(m_frame->data), m_frame->linesize, 0, m_frame->height, m_videoDestinationRGBData, m_videoDestinationRGBLineSize);
|
|
|
|
|
// Send it to the display engine ...
|
|
|
|
|
if (g_diplayElement != nullptr) {
|
|
|
|
|
g_diplayElement->setRawData(m_size, m_videoDestinationRGBData[0]);
|
|
|
|
|
}
|
|
|
|
|
sws_scale(m_convertContext,
|
|
|
|
|
(const uint8_t **)(m_frame->data),
|
|
|
|
|
m_frame->linesize,
|
|
|
|
|
0, m_frame->height,
|
|
|
|
|
&dataPointer,
|
|
|
|
|
&m_videoPool[slotId].m_lineSize);
|
|
|
|
|
m_videoPool[slotId].m_id = m_videoFrameCount;
|
|
|
|
|
m_videoPool[slotId].m_time = m_currentVideoTime;
|
|
|
|
|
m_videoPool[slotId].m_duration = echrono::Duration(0, 1000000000.0/float(getFps(m_videoDecoderContext)));
|
|
|
|
|
m_currentVideoTime += m_videoPool[slotId].m_duration;
|
|
|
|
|
m_videoPool[slotId].m_isUsed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (m_packet.stream_index == m_audioStream_idx) {
|
|
|
|
@ -96,17 +228,48 @@ int appl::Decoder::decode_packet(int *_gotFrame, int _cached) {
|
|
|
|
|
// Some audio decoders decode only part of the packet, and have to be called again with the remainder of the packet data.
|
|
|
|
|
decoded = FFMIN(ret, m_packet.size);
|
|
|
|
|
if (*_gotFrame) {
|
|
|
|
|
size_t unPaddedLineSize = m_frame->nb_samples * av_get_bytes_per_sample((enum AVSampleFormat)m_frame->format);
|
|
|
|
|
APPL_INFO("audio_frame " << (_cached?"(cached)":"")
|
|
|
|
|
<< " n=" << m_audioFrameCount
|
|
|
|
|
<< " nb_samples=" << m_frame->nb_samples
|
|
|
|
|
<< " pts=" << av_ts2timestr(m_frame->pts, &m_audioDecoderContext->time_base));
|
|
|
|
|
APPL_VERBOSE("audio_frame " << (_cached?"(cached)":"")
|
|
|
|
|
<< " n=" << m_audioFrameCount
|
|
|
|
|
<< " nb_samples=" << m_frame->nb_samples
|
|
|
|
|
<< " pts=" << av_ts2timestr(m_frame->pts, &m_audioDecoderContext->time_base));
|
|
|
|
|
m_audioFrameCount++;
|
|
|
|
|
// Write the raw audio data samples of the first plane. This works fine for packed formats (e.g. AV_SAMPLE_FMT_S16).
|
|
|
|
|
// However, most audio decoders output planar audio, which uses a separate plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P).
|
|
|
|
|
// In other words, this code will write only the first audio channel in these cases.
|
|
|
|
|
// You should use libswresample or libavfilter to convert the frame to packed data.
|
|
|
|
|
// TODO: fwrite(m_frame->extended_data[0], 1, unPaddedLineSize, m_audioDestinationFile);
|
|
|
|
|
int32_t slotId = audioGetEmptySlot();
|
|
|
|
|
if (slotId == -1) {
|
|
|
|
|
APPL_ERROR("an error occured when getting an empty slot for audio");
|
|
|
|
|
} else {
|
|
|
|
|
//m_frame->channel_layout
|
|
|
|
|
bool isPlanar = false;
|
|
|
|
|
audio::format format = audio::format_unknow;
|
|
|
|
|
switch(m_frame->format) {
|
|
|
|
|
case AV_SAMPLE_FMT_U8P: isPlanar = true;
|
|
|
|
|
case AV_SAMPLE_FMT_U8: format = audio::format_int8; break;
|
|
|
|
|
case AV_SAMPLE_FMT_S16P: isPlanar = true;
|
|
|
|
|
case AV_SAMPLE_FMT_S16: format = audio::format_int16; break;
|
|
|
|
|
case AV_SAMPLE_FMT_S32P: isPlanar = true;
|
|
|
|
|
case AV_SAMPLE_FMT_S32: format = audio::format_int32; break;
|
|
|
|
|
case AV_SAMPLE_FMT_FLTP: isPlanar = true;
|
|
|
|
|
case AV_SAMPLE_FMT_FLT: format = audio::format_float; break;
|
|
|
|
|
case AV_SAMPLE_FMT_DBLP: isPlanar = true;
|
|
|
|
|
case AV_SAMPLE_FMT_DBL: format = audio::format_double; break;
|
|
|
|
|
}
|
|
|
|
|
if (format == audio::format_unknow) {
|
|
|
|
|
APPL_ERROR("Unsupported audio format :" << m_frame->format << " ...");
|
|
|
|
|
} else {
|
|
|
|
|
// configure Buffer:
|
|
|
|
|
m_audioPool[slotId].configure(format, m_frame->sample_rate, m_frame->channels, m_frame->nb_samples);
|
|
|
|
|
// TODO : Optimise buffer transfer
|
|
|
|
|
if (isPlanar == true) {
|
|
|
|
|
unPlanar(m_frame->extended_data[0], m_audioPool[slotId].m_buffer.size(), m_audioPool[slotId].m_format, m_frame->channels);
|
|
|
|
|
}
|
|
|
|
|
// inject data in the buffer:
|
|
|
|
|
memcpy(&m_audioPool[slotId].m_buffer[0], m_frame->extended_data[0], m_audioPool[slotId].m_buffer.size());
|
|
|
|
|
m_audioPool[slotId].m_id = m_audioFrameCount;
|
|
|
|
|
m_audioPool[slotId].m_time = m_currentAudioTime;
|
|
|
|
|
m_audioPool[slotId].m_duration = echrono::Duration(0,(1000000000.0*m_frame->nb_samples)/float(m_frame->sample_rate));
|
|
|
|
|
m_currentAudioTime += m_audioPool[slotId].m_duration;
|
|
|
|
|
m_audioPool[slotId].m_isUsed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// If we use frame reference counting, we own the data and need to de-reference it when we don't use it anymore
|
|
|
|
@ -177,12 +340,9 @@ void appl::Decoder::init(const std::string& _filename) {
|
|
|
|
|
// allocate image where the decoded image will be put
|
|
|
|
|
m_size.setValue(m_videoDecoderContext->width, m_videoDecoderContext->height);
|
|
|
|
|
m_pixelFormat = m_videoDecoderContext->pix_fmt;
|
|
|
|
|
ret = av_image_alloc(m_videoDestinationData, m_videoDestinationLineSize, m_size.x(), m_size.y(), m_pixelFormat, 1);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
APPL_ERROR("Could not allocate raw video buffer");
|
|
|
|
|
return; // TODO : An error occured ... !!!!!
|
|
|
|
|
}
|
|
|
|
|
m_videoDestinationBufferSize = ret;
|
|
|
|
|
|
|
|
|
|
m_videoPool.resize(10);
|
|
|
|
|
|
|
|
|
|
// Create the video buffer for RGB mode:
|
|
|
|
|
ret = av_image_alloc(m_videoDestinationRGBData, m_videoDestinationRGBLineSize, m_size.x(), m_size.y(), AV_PIX_FMT_RGB24, 1);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
@ -200,64 +360,61 @@ void appl::Decoder::init(const std::string& _filename) {
|
|
|
|
|
}
|
|
|
|
|
// Open Audio Decoder:
|
|
|
|
|
if (open_codec_context(&m_audioStream_idx, m_formatContext, AVMEDIA_TYPE_AUDIO) >= 0) {
|
|
|
|
|
m_audioPresent = true;
|
|
|
|
|
m_audioStream = m_formatContext->streams[m_audioStream_idx];
|
|
|
|
|
m_audioDecoderContext = m_audioStream->codec;
|
|
|
|
|
|
|
|
|
|
m_audioPool.resize(80);
|
|
|
|
|
|
|
|
|
|
// Number of channels: m_audioDecoderContext->channels
|
|
|
|
|
// Framerate: m_audioDecoderContext->sample_rate
|
|
|
|
|
APPL_INFO("Open audio stream with audio property: " << int32_t(m_audioDecoderContext->channels) << " channel(s) & samplerate=" << m_audioDecoderContext->sample_rate << " Hz");
|
|
|
|
|
|
|
|
|
|
//m_frame->channel_layout
|
|
|
|
|
bool isPlanar = false;
|
|
|
|
|
m_audioSampleRate = m_audioDecoderContext->sample_rate;
|
|
|
|
|
m_audioFormat = audio::format_unknow;
|
|
|
|
|
switch(m_audioDecoderContext->sample_fmt) {
|
|
|
|
|
case AV_SAMPLE_FMT_NONE:
|
|
|
|
|
APPL_ERROR("Unsupported audio format : UNKNOW ...");
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_U8:
|
|
|
|
|
APPL_INFO("ffmpeg mode : AV_SAMPLE_FMT_U8");
|
|
|
|
|
isPlanar = false;
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_S16:
|
|
|
|
|
APPL_INFO("ffmpeg mode : AV_SAMPLE_FMT_S16");
|
|
|
|
|
isPlanar = false;
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_S32:
|
|
|
|
|
APPL_INFO("ffmpeg mode : AV_SAMPLE_FMT_S32 (double)");
|
|
|
|
|
isPlanar = false;
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_FLT:
|
|
|
|
|
APPL_INFO("ffmpeg mode : AV_SAMPLE_FMT_FLT (float)");
|
|
|
|
|
isPlanar = false;
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_DBL:
|
|
|
|
|
APPL_INFO("ffmpeg mode : AV_SAMPLE_FMT_DBL");
|
|
|
|
|
APPL_ERROR(" ==> Unsupported audio format : double");
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_U8P:
|
|
|
|
|
APPL_INFO("ffmpeg mode : AV_SAMPLE_FMT_U8P (planar)");
|
|
|
|
|
isPlanar = true;
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_U8: m_audioFormat = audio::format_int8; break;
|
|
|
|
|
case AV_SAMPLE_FMT_S16P:
|
|
|
|
|
APPL_INFO("ffmpeg mode : AV_SAMPLE_FMT_S16P (planar)");
|
|
|
|
|
isPlanar = true;
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_S16: m_audioFormat = audio::format_int16; break;
|
|
|
|
|
case AV_SAMPLE_FMT_S32P:
|
|
|
|
|
APPL_INFO("ffmpeg mode : AV_SAMPLE_FMT_S32P (planar)");
|
|
|
|
|
isPlanar = true;
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_S32: m_audioFormat = audio::format_int32; break;
|
|
|
|
|
case AV_SAMPLE_FMT_FLTP:
|
|
|
|
|
APPL_INFO("ffmpeg mode : AV_SAMPLE_FMT_FLTP (float/planar)");
|
|
|
|
|
isPlanar = true;
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_FLT: m_audioFormat = audio::format_float; break;
|
|
|
|
|
case AV_SAMPLE_FMT_DBLP:
|
|
|
|
|
APPL_INFO("ffmpeg mode : AV_SAMPLE_FMT_DBLP (double/planar)");
|
|
|
|
|
APPL_ERROR(" ==> Unsupported audio format : double Planar");
|
|
|
|
|
break;
|
|
|
|
|
case AV_SAMPLE_FMT_DBL: m_audioFormat = audio::format_double; break;
|
|
|
|
|
case AV_SAMPLE_FMT_NONE:
|
|
|
|
|
case AV_SAMPLE_FMT_NB:
|
|
|
|
|
APPL_ERROR("Unsupported audio format : Wrong ID ...");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
APPL_CRITICAL("Unsupported audio format :" << m_audioDecoderContext->sample_fmt << " ...");
|
|
|
|
|
m_audioFormat = audio::format_unknow;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// TODO : Do it better:
|
|
|
|
|
m_audioMap.resize(m_audioDecoderContext->channels);
|
|
|
|
|
switch(m_audioDecoderContext->channels) {
|
|
|
|
|
case 1:
|
|
|
|
|
m_audioMap[0] = audio::channel_frontCenter;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
m_audioMap[0] = audio::channel_frontLeft;
|
|
|
|
|
m_audioMap[1] = audio::channel_frontRight;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
m_audioMap[0] = audio::channel_frontLeft;
|
|
|
|
|
m_audioMap[1] = audio::channel_lfe;
|
|
|
|
|
m_audioMap[2] = audio::channel_frontRight;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
m_audioMap[0] = audio::channel_frontLeft;
|
|
|
|
|
m_audioMap[1] = audio::channel_frontRight;
|
|
|
|
|
m_audioMap[2] = audio::channel_rearLeft;
|
|
|
|
|
m_audioMap[3] = audio::channel_rearRight;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
APPL_ERROR("not supportef nbChannel " << m_audioDecoderContext->channels);
|
|
|
|
|
}
|
|
|
|
|
APPL_PRINT("Audio configuration : " << m_audioMap << " " << m_audioFormat << " sampleRate=" <<m_audioSampleRate);
|
|
|
|
|
}
|
|
|
|
|
// dump input information to stderr
|
|
|
|
|
av_dump_format(m_formatContext, 0, m_sourceFilename.c_str(), 0);
|
|
|
|
@ -278,7 +435,17 @@ void appl::Decoder::init(const std::string& _filename) {
|
|
|
|
|
m_packet.size = 0;
|
|
|
|
|
}
|
|
|
|
|
bool appl::Decoder::onThreadCall() {
|
|
|
|
|
APPL_INFO("Work on decoding");
|
|
|
|
|
// check if we have space to decode data
|
|
|
|
|
if ( ( m_videoPool.size() != 0
|
|
|
|
|
&& videoGetEmptySlot() == -1)
|
|
|
|
|
|| ( m_audioPool.size() != 0
|
|
|
|
|
&& audioGetEmptySlot() == -1)
|
|
|
|
|
) {
|
|
|
|
|
// take some time to sleep the decoding ...
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(60/25));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
APPL_VERBOSE("Work on decoding");
|
|
|
|
|
int gotFrame;
|
|
|
|
|
// read frames from the file
|
|
|
|
|
int ret = av_read_frame(m_formatContext, &m_packet);
|
|
|
|
@ -309,9 +476,52 @@ void appl::Decoder::uninit() {
|
|
|
|
|
avcodec_close(m_audioDecoderContext);
|
|
|
|
|
avformat_close_input(&m_formatContext);
|
|
|
|
|
av_frame_free(&m_frame);
|
|
|
|
|
av_free(m_videoDestinationData[0]);
|
|
|
|
|
av_free(m_videoDestinationRGBData[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t appl::Decoder::videoGetEmptySlot() {
|
|
|
|
|
for (int32_t iii=0; iii<m_videoPool.size(); ++iii) {
|
|
|
|
|
if (m_videoPool[iii].m_isUsed == false) {
|
|
|
|
|
return iii;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t appl::Decoder::audioGetEmptySlot() {
|
|
|
|
|
for (int32_t iii=0; iii<m_audioPool.size(); ++iii) {
|
|
|
|
|
if (m_audioPool[iii].m_isUsed == false) {
|
|
|
|
|
return iii;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t appl::Decoder::videoGetOlderSlot() {
|
|
|
|
|
int32_t smallerId = 0x7FFFFFFE;
|
|
|
|
|
int32_t findId = -1;
|
|
|
|
|
for (int32_t iii=0; iii<m_videoPool.size(); ++iii) {
|
|
|
|
|
if ( m_videoPool[iii].m_isUsed == true
|
|
|
|
|
&& smallerId > m_videoPool[iii].m_id) {
|
|
|
|
|
smallerId = m_videoPool[iii].m_id;
|
|
|
|
|
findId = iii;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return findId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t appl::Decoder::audioGetOlderSlot() {
|
|
|
|
|
int32_t smallerId = 0x7FFFFFFF;
|
|
|
|
|
int32_t findId = -1;
|
|
|
|
|
for (int32_t iii=0; iii<m_audioPool.size(); ++iii) {
|
|
|
|
|
if ( m_audioPool[iii].m_isUsed == true
|
|
|
|
|
&& smallerId > m_audioPool[iii].m_id) {
|
|
|
|
|
smallerId = m_audioPool[iii].m_id;
|
|
|
|
|
findId = iii;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return findId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VBO table property:
|
|
|
|
|
const int32_t appl::widget::VideoDisplay::m_vboIdCoord(0);
|
|
|
|
@ -323,6 +533,7 @@ appl::widget::VideoDisplay::VideoDisplay() {
|
|
|
|
|
addObjectType("appl::widget::VideoDisplay");
|
|
|
|
|
m_color = etk::color::white;
|
|
|
|
|
m_nbFramePushed = 0;
|
|
|
|
|
m_isPalying = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appl::widget::VideoDisplay::init() {
|
|
|
|
@ -339,38 +550,19 @@ void appl::widget::VideoDisplay::init() {
|
|
|
|
|
m_VBO->setName("[VBO] of appl::widget::VideoDisplay");
|
|
|
|
|
loadProgram();
|
|
|
|
|
m_matrixApply.identity();
|
|
|
|
|
g_diplayElement = ememory::dynamicPointerCast<appl::widget::VideoDisplay>(sharedFromThis());
|
|
|
|
|
}
|
|
|
|
|
void appl::widget::VideoDisplay::setRawData(const ivec2& _size, void* _dataPointer) {
|
|
|
|
|
APPL_INFO("setRaw Data ... " << _size << " " << int64_t(_dataPointer) << " " << int64_t(m_resource.get()));
|
|
|
|
|
// By default we load a graphic resource ...
|
|
|
|
|
if (m_resource == nullptr) {
|
|
|
|
|
m_resource = ewol::resource::Texture::create();
|
|
|
|
|
if (m_resource == nullptr) {
|
|
|
|
|
EWOL_ERROR("Can not CREATE Image resource");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_videoSize = ivec2(0,0);
|
|
|
|
|
// All time need to configure in RGB, By default it is in RGBA ...
|
|
|
|
|
m_resource->get().configure(ivec2(128,128), egami::colorType::RGB8);
|
|
|
|
|
}
|
|
|
|
|
if (m_videoSize != _size) {
|
|
|
|
|
// Resize the buffer:
|
|
|
|
|
m_videoSize = _size;
|
|
|
|
|
m_resource->setImageSize(m_videoSize);
|
|
|
|
|
m_imageSize = m_resource->get().getSize();
|
|
|
|
|
}
|
|
|
|
|
egami::Image& image = m_resource->get();
|
|
|
|
|
uint8_t* pointer = (uint8_t*)_dataPointer;
|
|
|
|
|
for (int32_t yyy=0; yyy<_size.y(); ++yyy) {
|
|
|
|
|
for (int32_t xxx=0; xxx<_size.x(); ++xxx) {
|
|
|
|
|
uint8_t r = *pointer++;
|
|
|
|
|
uint8_t g = *pointer++;
|
|
|
|
|
uint8_t b = *pointer++;
|
|
|
|
|
etk::Color<>(r,g,b);
|
|
|
|
|
image.set(ivec2(xxx,yyy), etk::Color<>(r,g,b));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//egami::store(image, "test.bmp");
|
|
|
|
|
m_resource->flush();
|
|
|
|
|
m_nbFramePushed++;
|
|
|
|
|
// Create the River manager for tha application or part of the application.
|
|
|
|
|
m_audioManager = audio::river::Manager::create("river_sample_read");
|
|
|
|
|
//! [audio_river_sample_create_write_interface]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appl::widget::VideoDisplay::~VideoDisplay() {
|
|
|
|
@ -392,19 +584,43 @@ void appl::widget::VideoDisplay::loadProgram() {
|
|
|
|
|
}
|
|
|
|
|
void appl::widget::VideoDisplay::setFile(const std::string& _filename) {
|
|
|
|
|
m_decoder.init(_filename);
|
|
|
|
|
if (m_decoder.haveAudio() == true) {
|
|
|
|
|
//Get the generic input:
|
|
|
|
|
m_audioInterface = m_audioManager->createOutput(m_decoder.audioGetSampleRate(),
|
|
|
|
|
m_decoder.audioGetChannelMap(),
|
|
|
|
|
m_decoder.audioGetFormat(),
|
|
|
|
|
"speaker");
|
|
|
|
|
if(m_audioInterface == nullptr) {
|
|
|
|
|
APPL_ERROR("Can not creata Audio interface");
|
|
|
|
|
}
|
|
|
|
|
m_audioInterface->setReadwrite();
|
|
|
|
|
m_audioInterface->start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_decoder.start();
|
|
|
|
|
m_audioManager->generateDotAll("out/local_player_flow.dot");
|
|
|
|
|
markToRedraw();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool appl::widget::VideoDisplay::isPlaying() {
|
|
|
|
|
return m_isPalying;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appl::widget::VideoDisplay::play() {
|
|
|
|
|
m_isPalying = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appl::widget::VideoDisplay::pause() {
|
|
|
|
|
m_isPalying = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appl::widget::VideoDisplay::onDraw() {
|
|
|
|
|
APPL_INFO("Draw ..................................");
|
|
|
|
|
if (m_VBO->bufferSize(m_vboIdCoord) <= 0) {
|
|
|
|
|
APPL_WARNING("Nothink to draw...");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (m_resource == nullptr) {
|
|
|
|
|
// this is a normale case ... the user can choice to have no image ...
|
|
|
|
|
APPL_ERROR("No Resource ...");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (m_GLprogram == nullptr) {
|
|
|
|
@ -489,6 +705,34 @@ void appl::widget::VideoDisplay::onRegenerateDisplay() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appl::widget::VideoDisplay::periodicEvent(const ewol::event::Time& _event) {
|
|
|
|
|
if (m_isPalying == true) {
|
|
|
|
|
m_currentTime += _event.getDeltaCallDuration();
|
|
|
|
|
}
|
|
|
|
|
// SET AUDIO:
|
|
|
|
|
int32_t idSlot = m_decoder.audioGetOlderSlot();
|
|
|
|
|
if ( idSlot != -1
|
|
|
|
|
&& m_currentTime > m_decoder.m_audioPool[idSlot].m_time) {
|
|
|
|
|
int32_t nbSample = m_decoder.m_audioPool[idSlot].m_buffer.size()
|
|
|
|
|
/ audio::getFormatBytes(m_decoder.m_audioPool[idSlot].m_format)
|
|
|
|
|
/ m_decoder.m_audioPool[idSlot].m_map.size();
|
|
|
|
|
m_audioInterface->write(&m_decoder.m_audioPool[idSlot].m_buffer[0], nbSample);
|
|
|
|
|
m_decoder.m_audioPool[idSlot].m_isUsed = false;
|
|
|
|
|
}
|
|
|
|
|
// SET VIDEO:
|
|
|
|
|
idSlot = m_decoder.videoGetOlderSlot();
|
|
|
|
|
// check the slot is valid and check display time of the element:
|
|
|
|
|
if ( idSlot != -1
|
|
|
|
|
&& m_currentTime > m_decoder.m_videoPool[idSlot].m_time) {
|
|
|
|
|
m_resource->get().swap(m_decoder.m_videoPool[idSlot].m_image);
|
|
|
|
|
m_imageSize = m_resource->get().getSize();
|
|
|
|
|
ivec2 tmpSize = m_decoder.m_videoPool[idSlot].m_imagerealSize;
|
|
|
|
|
m_decoder.m_videoPool[idSlot].m_imagerealSize = m_videoSize;
|
|
|
|
|
m_videoSize = tmpSize;
|
|
|
|
|
m_decoder.m_videoPool[idSlot].m_isUsed = false;
|
|
|
|
|
m_resource->flush();
|
|
|
|
|
m_nbFramePushed++;
|
|
|
|
|
}
|
|
|
|
|
// Display FPS ...
|
|
|
|
|
m_LastResetCounter += _event.getDeltaCallDuration();
|
|
|
|
|
if (m_LastResetCounter > echrono::seconds(1)) {
|
|
|
|
|
m_LastResetCounter.reset();
|
|
|
|
@ -497,3 +741,4 @@ void appl::widget::VideoDisplay::periodicEvent(const ewol::event::Time& _event)
|
|
|
|
|
}
|
|
|
|
|
markToRedraw();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|