Commit 35d86f34 authored by Sergi Hernandez's avatar Sergi Hernandez
Browse files

Added the repaet header feature to the hardware h264 encoder.

Added audio buffer.
Solved a bug with the resampler when it is not needed.
parent 1817a326
......@@ -20,7 +20,7 @@ extern "C"
#include <chrono>
#define NUM_VIDEO_BUFFERS 4
#define NUM_AUDIO_BUFFERS 4
#define NUM_AUDIO_BUFFERS 8
typedef struct{
void *start;
......@@ -60,6 +60,14 @@ typedef struct{
unsigned int av_profile;
}TVideoConfig;
typedef struct
{
unsigned char **data;
unsigned int num_bytes;
long long frame_time;
int64_t frame_pts;
}TAudioBuffer;
typedef struct{
unsigned long int av_channels_layout;
PaSampleFormat pa_sample_format;
......@@ -71,15 +79,16 @@ typedef struct{
AVCodecContext *encoder_context;
AVFrame *frame;
AVFrame *tmp_frame;
int64_t next_pts;
int nb_samples;
struct SwrContext *resampler;
int samples_count;
int nb_samples;
// device data
PaStream *audio_stream;
CMutex access;
long long frame_time;
std::chrono::time_point<std::chrono::high_resolution_clock> start_time;
int64_t next_pts;
TAudioBuffer *buffers;
unsigned int buffer_write;
unsigned int buffer_read;
}TAudioData;
typedef struct{
......
......@@ -41,11 +41,12 @@ CStreamSrtSource::CStreamSrtSource()
this->audio_data.frame=NULL;
this->audio_data.tmp_frame=NULL;
this->audio_data.next_pts=0;
this->audio_data.samples_count=0;
this->audio_data.start_time=std::chrono::high_resolution_clock::now();
this->audio_data.frame_time=0;
this->audio_data.audio_stream=NULL;
this->audio_data.resampler=NULL;
this->audio_data.buffers=NULL;
this->audio_data.buffer_write=0;
this->audio_data.buffer_read=0;
// initialize events
this->event_server=CEventServer::instance();
this->finish_main_thread_event_id="stream_srt_source_finish_main_thread_event";
......@@ -447,6 +448,21 @@ void CStreamSrtSource::open_video_device(void)
this->close_video_device();
throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 level",errno,strerror(errno));
}
// enable repeting the header sequence
memset(&ecs1, 0, sizeof(ecs1));
memset(&ec1, 0, sizeof(ec1));
ec1.id = V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER;
ec1.value = 1;
ec1.size = 0;
ecs1.controls = &ec1;
ecs1.count = 1;
ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG;
if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1)
{
this->close_video_device();
throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to enable repeating the header sequence",errno,strerror(errno));
}
}
// request DMA buffers
memset(&req,0,sizeof(req));
......@@ -779,7 +795,7 @@ void CStreamSrtSource::open_audio(void)
}
else
this->audio_data.encoder_context->channel_layout=this->audio_data.av_channels_layout;
this->audio_data.encoder_context->flags|=AV_CODEC_FLAG_GLOBAL_HEADER;
// this->audio_data.encoder_context->flags|=AV_CODEC_FLAG_GLOBAL_HEADER;
// open codec
ret=avcodec_open2(this->audio_data.encoder_context,this->audio_data.codec,NULL);
......@@ -833,6 +849,11 @@ void CStreamSrtSource::open_audio(void)
}
this->audio_data.av_sample_format=this->audio_data.encoder_context->sample_fmt;
}
else
{
this->audio_data.resampler=NULL;
this->audio_data.tmp_frame=NULL;
}
// allocate frame
this->audio_data.frame=av_frame_alloc();
if(this->audio_data.frame==NULL)
......@@ -890,7 +911,13 @@ void CStreamSrtSource::open_audio_device(void)
const PaDeviceInfo *device_info;
std::string device_name;
PaStreamParameters audio_params;
// reset any pending events
if(this->event_server->event_is_set(this->new_audio_buffer_event_id))
{
this->event_server->reset_event(this->new_audio_buffer_event_id);
std::cout << "resetting audio event" << std::endl;
}
err=Pa_Initialize();
if(err!=paNoError)
throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to initialize PortAudio",err,Pa_GetErrorText(err));
......@@ -919,6 +946,37 @@ void CStreamSrtSource::open_audio_device(void)
this->close_audio_device();
throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to open audio device",err,Pa_GetErrorText(err));
}
// initialize buffers
if(this->audio_data.buffers!=NULL)
{
for(unsigned int i=0;i<NUM_AUDIO_BUFFERS;i++)
{
if(this->audio_data.buffers[i].data!=NULL)
{
for(int j=0;j<this->config.audio.num_channels;j++)
{
delete[] this->audio_data.buffers[i].data[j];
this->audio_data.buffers[i].data[j]=NULL;
}
delete[] this->audio_data.buffers[i].data;
audio_data.buffers[i].data=NULL;
}
}
delete[] this->audio_data.buffers;
this->audio_data.buffers=NULL;
}
this->audio_data.buffers=new TAudioBuffer[NUM_AUDIO_BUFFERS];
for(unsigned int i=0;i<NUM_AUDIO_BUFFERS;i++)
{
audio_data.buffers[i].data=new unsigned char *[this->config.audio.num_channels];
for(int j=0;j<this->config.audio.num_channels;j++)
audio_data.buffers[i].data[j]=new unsigned char[this->audio_data.nb_samples*this->audio_data.sample_size];
audio_data.buffers[i].frame_time=0;
audio_data.buffers[i].frame_pts=0;
audio_data.buffers[i].num_bytes=0;
}
this->audio_data.buffer_write=0;
this->audio_data.buffer_read=0;
}
return;
}
......@@ -936,9 +994,7 @@ void CStreamSrtSource::start_audio_device(void)
if(this->thread_server->get_thread_state(this->capture_audio_thread_id)==attached)
this->thread_server->start_thread(this->capture_audio_thread_id);
this->audio_data.next_pts=0;
this->audio_data.frame_time=0;
this->audio_data.start_time=std::chrono::high_resolution_clock::now();
this->audio_data.samples_count=0;
}
void CStreamSrtSource::stop_audio_device(void)
......@@ -973,6 +1029,27 @@ void CStreamSrtSource::close_audio_device(void)
this->audio_data.audio_stream=NULL;
}
Pa_Terminate();
// delete buffers
if(this->audio_data.buffers!=NULL)
{
for(unsigned int i=0;i<NUM_AUDIO_BUFFERS;i++)
{
if(this->audio_data.buffers[i].data!=NULL)
{
for(int j=0;j<this->config.audio.num_channels;j++)
{
delete[] this->audio_data.buffers[i].data[j];
this->audio_data.buffers[i].data[j]=NULL;
}
delete[] audio_data.buffers[i].data;
audio_data.buffers[i].data=NULL;
}
}
delete[] this->audio_data.buffers;
this->audio_data.buffers=NULL;
}
this->audio_data.buffer_write=0;
this->audio_data.buffer_read=0;
}
int CStreamSrtSource::audioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
......@@ -982,16 +1059,18 @@ int CStreamSrtSource::audioCallback(const void *inputBuffer, void *outputBuffer,
data->audio_data.access.enter();
for(unsigned int i=0;i<framesPerBuffer; i++)
for(unsigned int j=0;j<data->config.audio.num_channels;j++)
for(int j=0;j<data->config.audio.num_channels;j++)
for(unsigned int k=0;k<data->audio_data.sample_size;k++)
data->audio_data.tmp_frame->data[j][i*data->audio_data.sample_size+k]=((unsigned char *)inputBuffer)[i*data->audio_data.sample_size*data->config.audio.num_channels+j*data->audio_data.sample_size+k];
if(!data->event_server->event_is_set(data->new_audio_buffer_event_id))
data->event_server->set_event(data->new_audio_buffer_event_id);
data->audio_data.buffers[data->audio_data.buffer_write].data[j][i*data->audio_data.sample_size+k]=((unsigned char *)inputBuffer)[i*data->audio_data.sample_size*data->config.audio.num_channels+j*data->audio_data.sample_size+k];
std::chrono::time_point<std::chrono::high_resolution_clock> end=std::chrono::high_resolution_clock::now();
data->audio_data.frame_time=std::chrono::duration_cast<std::chrono::microseconds>(end-data->audio_data.start_time).count();
data->audio_data.tmp_frame->pts=data->audio_data.next_pts;
data->audio_data.buffers[data->audio_data.buffer_write].num_bytes=framesPerBuffer*data->audio_data.sample_size;
data->audio_data.buffers[data->audio_data.buffer_write].frame_time=std::chrono::duration_cast<std::chrono::microseconds>(end-data->audio_data.start_time).count();
data->audio_data.buffers[data->audio_data.buffer_write].frame_pts=data->audio_data.next_pts;
data->audio_data.next_pts+=std::chrono::duration_cast<std::chrono::microseconds>(end-data->audio_data.start_time).count();
data->audio_data.start_time=end;
// if(!data->event_server->event_is_set(data->new_audio_buffer_event_id))
data->event_server->set_event(data->new_audio_buffer_event_id);
data->audio_data.buffer_write=(data->audio_data.buffer_write+1)%NUM_AUDIO_BUFFERS;
data->audio_data.access.exit();
/* set the output to zero */
......@@ -1005,6 +1084,8 @@ void *CStreamSrtSource::capture_audio_thread(void *param)
int ret,got_packet,index,dst_nb_samples;
CStreamSrtSource *stream=(CStreamSrtSource *)param;
std::list<std::string> events;
long long frame_time;
int64_t frame_pts;
bool end=false;
AVPacket pkt;
......@@ -1018,18 +1099,38 @@ void *CStreamSrtSource::capture_audio_thread(void *param)
else// new buffer available
{
av_init_packet(&pkt);
dst_nb_samples=av_rescale_rnd(swr_get_delay(stream->audio_data.resampler,stream->audio_data.encoder_context->sample_rate)+stream->audio_data.tmp_frame->nb_samples,stream->audio_data.encoder_context->sample_rate,stream->audio_data.encoder_context->sample_rate, AV_ROUND_UP);
ret=av_frame_make_writable(stream->audio_data.frame);
if(ret<0)
std::cout << "Impossible to make frame audio writable" << std::endl;
/* convert to destination format */
stream->audio_data.access.enter();
ret=swr_convert(stream->audio_data.resampler,stream->audio_data.frame->data,dst_nb_samples,(const uint8_t **)stream->audio_data.tmp_frame->data,stream->audio_data.tmp_frame->nb_samples);
if(ret<0)
std::cout << "Impossible to make resample audio frame" << std::endl;
stream->audio_data.access.exit();
stream->audio_data.frame->pts=av_rescale_q(stream->audio_data.samples_count,(AVRational){1,stream->audio_data.encoder_context->sample_rate},stream->audio_data.encoder_context->time_base);
stream->audio_data.samples_count+=dst_nb_samples;
if(stream->audio_data.resampler!=NULL)
{
stream->audio_data.access.enter();
for(int i=0;i<stream->config.audio.num_channels;i++)
memcpy(stream->audio_data.tmp_frame->data[i],stream->audio_data.buffers[stream->audio_data.buffer_read].data[i],stream->audio_data.buffers[stream->audio_data.buffer_read].num_bytes);
stream->audio_data.tmp_frame->pts=stream->audio_data.buffers[stream->audio_data.buffer_read].frame_pts;
stream->audio_data.tmp_frame->nb_samples=stream->audio_data.buffers[stream->audio_data.buffer_read].num_bytes/stream->audio_data.sample_size;
dst_nb_samples=av_rescale_rnd(swr_get_delay(stream->audio_data.resampler,stream->audio_data.encoder_context->sample_rate)+stream->audio_data.tmp_frame->nb_samples,stream->audio_data.encoder_context->sample_rate,stream->audio_data.encoder_context->sample_rate, AV_ROUND_UP);
/* convert to destination format */
ret=swr_convert(stream->audio_data.resampler,stream->audio_data.frame->data,dst_nb_samples,(const uint8_t **)stream->audio_data.tmp_frame->data,stream->audio_data.tmp_frame->nb_samples);
if(ret<0)
std::cout << "Impossible to make resample audio frame" << std::endl;
frame_time=stream->audio_data.buffers[stream->audio_data.buffer_read].frame_time;
frame_pts=stream->audio_data.buffers[stream->audio_data.buffer_read].frame_pts;
stream->audio_data.buffer_read=(stream->audio_data.buffer_read+1)%NUM_AUDIO_BUFFERS;
stream->audio_data.access.exit();
}
else
{
stream->audio_data.access.enter();
for(int i=0;i<stream->config.audio.num_channels;i++)
memcpy(stream->audio_data.frame->data[i],stream->audio_data.buffers[stream->audio_data.buffer_read].data[i],stream->audio_data.buffers[stream->audio_data.buffer_read].num_bytes);
stream->audio_data.frame->pts=stream->audio_data.buffers[stream->audio_data.buffer_read].frame_pts;
stream->audio_data.frame->nb_samples=stream->audio_data.buffers[stream->audio_data.buffer_read].num_bytes/stream->audio_data.sample_size;
frame_time=stream->audio_data.buffers[stream->audio_data.buffer_read].frame_time;
frame_pts=stream->audio_data.buffers[stream->audio_data.buffer_read].frame_pts;
stream->audio_data.buffer_read=(stream->audio_data.buffer_read+1)%NUM_AUDIO_BUFFERS;
stream->audio_data.access.exit();
}
// encode audio frame
ret=avcodec_encode_audio2(stream->audio_data.encoder_context,&pkt,stream->audio_data.frame,&got_packet);
if(ret<0)
......@@ -1037,9 +1138,10 @@ void *CStreamSrtSource::capture_audio_thread(void *param)
else if(got_packet)
{
// assign actual time stamps
pkt.duration=(stream->audio_data.frame_time*9)/100;
pkt.pts=(stream->audio_data.next_pts*9)/100;
pkt.dts=(stream->audio_data.next_pts*9)/100;
pkt.duration=(frame_time*9)/100;
pkt.pts=(frame_pts*9)/100;
pkt.dts=AV_NOPTS_VALUE;
//pkt.dts=(stream->audio_data.next_pts*9)/100;
pkt.stream_index = stream->audio_data.stream->index;
/* Write the compressed frame to the media file. */
// std::cout << "pts: " << av_ts2str(pkt.pts) << " pts_time: " << av_ts2timestr(pkt.pts,&stream->output_context->streams[pkt.stream_index]->time_base) << " dts: " << av_ts2str(pkt.dts) << " dts_time: " << av_ts2timestr(pkt.dts, &stream->output_context->streams[pkt.stream_index]->time_base) << " duration: " << av_ts2str(pkt.duration) << " duration_time: " << av_ts2timestr(pkt.duration, &stream->output_context->streams[pkt.stream_index]->time_base) << " stream_index: " << pkt.stream_index << std::endl;
......@@ -1058,7 +1160,9 @@ void *CStreamSrtSource::capture_audio_thread(void *param)
av_free_packet(&pkt);
}
}
av_init_packet(&pkt);
avcodec_encode_audio2(stream->audio_data.encoder_context,&pkt,NULL,&got_packet);
av_free_packet(&pkt);
pthread_exit(NULL);
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment