Skip to content

Instantly share code, notes, and snippets.

@rtmpnewbie
Last active August 29, 2015 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rtmpnewbie/a7e45fa145942a4ee2e8 to your computer and use it in GitHub Desktop.
Save rtmpnewbie/a7e45fa145942a4ee2e8 to your computer and use it in GitHub Desktop.
configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-avynth --enable-bzlib --enable-fontconfig
--enable-frei0r --enable-gnutls --enab-iconv --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca
--able-libfreetype --enable-libgme --enable-libgsm --enable-libilbc --enable-libdplug --enable-libmp3lame
--enable-libopencore-amrnb --enable-libopencore-amrw--enable-libopenjpeg --enable-libopus --enable-librtmp
--enable-libschroedinge--enable-libsoxr --enable-libspeex --enable-libtheora --enable-libtwolame --enle-libvidstab
--enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis -enable-libvpx --enable-libwavpack --enable-libwebp
--enable-libx264 --enable- bx265 --enable-libxavs --enable-libxvid --enable-lzma --enable-decklink --enab -zlib
MP4RecorderSink::MP4RecorderSink( void )
{
Init();
}
MP4RecorderSink::~MP4RecorderSink( void )
{
if(IsOpened())
Close();
}
int MP4RecorderSink::Open( const EncoderConfig& encoder_config, const std::string& file_path )
{
encoder_config_ = encoder_config;
file_path_ = file_path;
if(CreateContext() != PA_OK)
{
PA_WarnLog("CreateMp4Context failed!");
return PA_ERROR;
}
return PA_OK;
}
int MP4RecorderSink::CreateContext()
{
avformat_alloc_output_context2(&output_ctx_, NULL, NULL, file_path_.c_str());
if (!output_ctx_)
{
PA_WarnLog("Could not deduce output format from file extension: using MPEG.\n");
avformat_alloc_output_context2(&output_ctx_, NULL, "mpeg", file_path_.c_str());
}
if (!output_ctx_)
{
return PA_ERROR;
}
output_ctx_->oformat->video_codec = AV_CODEC_ID_H264;
output_ctx_->oformat->audio_codec = CODEC_ID_AAC;
output_format_ = output_ctx_->oformat;
if (avio_open(&output_ctx_->pb, file_path_.c_str(), AVIO_FLAG_READ_WRITE) < 0)
{
PA_WarnLog("avio_open failed \n");
goto label_error;
}
if (output_format_->video_codec != CODEC_ID_NONE)
{
video_steam_ = AddVideoStream(output_ctx_,
output_format_->video_codec,
encoder_config_.video_bitrate_,
encoder_config_.width_,
encoder_config_.height_,
encoder_config_.frame_rate_);
}
if (output_format_->audio_codec != CODEC_ID_NONE)
{
audio_stream_ = AddAudioStream(output_ctx_,
output_format_->audio_codec,
encoder_config_.audio_bitrate_,
encoder_config_.sample_rate_,
encoder_config_.channels_);
}
if (NULL == video_steam_ || NULL == audio_stream_)
{
PA_WarnLog("NULL == video_steam_ || NULL == audio_stream_");
goto label_error;
}
av_dump_format(output_ctx_, 0, file_path_.c_str(), 1);
//if (OpenVideo(output_ctx_, video_steam_) == -1)
//{
// PA_WarnLog("NULL == video_steam_ || NULL == audio_stream_");
// goto label_error;
//}
//if (OpenAudio(output_ctx_, audio_stream_))
//{
// PA_WarnLog("NULL == video_steam_ || NULL == audio_stream_");
// goto label_error;
//}
if (avformat_write_header(output_ctx_, NULL))
{
PA_WarnLog("Call avformat_write_header function failed! \n");
goto label_error;
}
return PA_OK;
label_error:
if (NULL != video_steam_)
{
if(video_steam_->codec != NULL)
{
avcodec_close(video_steam_->codec);
}
video_steam_ = NULL;
}
if (NULL != audio_stream_)
{
if(audio_stream_->codec != NULL)
{
avcodec_close(audio_stream_->codec);
}
audio_stream_ = NULL;
}
for(unsigned i = 0; i < output_ctx_->nb_streams; i++)
{
av_freep(&output_ctx_->streams[i]->codec);
av_freep(&output_ctx_->streams[i]);
}
if(output_ctx_ != NULL)
{
if (output_ctx_->pb != NULL)
{
avio_close(output_ctx_->pb);
}
av_free(output_ctx_);
output_ctx_ = NULL;
}
output_format_ = NULL;
return PA_ERROR;
}
AVStream* MP4RecorderSink::AddVideoStream(AVFormatContext *oc, enum AVCodecID codec_id , int bit_rate , int width , int height , int frame_rate)
{
AVCodecContext *c;
AVStream *new_stream;
AVCodec *codec;
new_stream = avformat_new_stream(oc, NULL);
if (!new_stream)
{
PA_WarnLog("Could not alloc stream\n");
return NULL;
}
new_stream->id = 0;
c = new_stream->codec;
/* find the video encoder */
codec = avcodec_find_encoder(codec_id);
if (!codec)
{
PA_WarnLog("add_video_stream_v codec not found\n");
av_freep(new_stream);
return NULL;
}
avcodec_get_context_defaults3(c, codec);
c->codec_id = codec_id;
c->codec_type = AVMEDIA_TYPE_VIDEO;
/* put sample parameters */
c->bit_rate = /*400000*/bit_rate;
/* resolution must be a multiple of two */
c->width = width;
c->height = height;
/* time base: this is the fundamental unit of time (in seconds) in terms
of which frame timestamps are represented. for fixed-fps content,
timebase should be 1/framerate and timestamp increments should be
identically 1. */
c->time_base.den = frame_rate;
c->time_base.num = 1;
c->gop_size = 12; /* emit one intra frame every twelve frames at most */
c->pix_fmt = PIX_FMT_YUV420P;
if (c->codec_id == CODEC_ID_MPEG2VIDEO)
{
/* just for testing, we also add B frames */
c->max_b_frames = 2;
}
if (c->codec_id == CODEC_ID_MPEG1VIDEO){
/* Needed to avoid using macroblocks in which some coeffs overflow.
This does not happen with normal video, it just happens here as
the motion of the chroma plane does not match the luma plane. */
c->mb_decision=2;
}
// some formats want stream headers to be separate
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
return new_stream;
}
AVStream* MP4RecorderSink::AddAudioStream(AVFormatContext *oc, enum AVCodecID codec_id,
int bit_rate, int sample_rate, int channels)
{
AVCodecContext *c;
AVStream *new_stream;
AVCodec *codec;
new_stream = avformat_new_stream(oc, NULL);
if (!new_stream)
{
PA_WarnLog("Could not alloc stream\n");
return NULL;
}
new_stream->id = 1;
c = new_stream->codec;
/* find the video encoder */
/* codec = avcodec_find_encoder(codec_id);
if (!codec)
{
fprintf(stderr, "add_video_stream_v codec not found\n");
exit(1);
}
avcodec_get_context_defaults3(c, codec);*/
c->codec_id = codec_id;
c->codec_type = AVMEDIA_TYPE_AUDIO;
/* put sample parameters */
c->sample_fmt = AV_SAMPLE_FMT_S16;
c->bit_rate = bit_rate;
c->sample_rate = sample_rate;
c->channels = channels;
c->profile = FF_PROFILE_AAC_LOW;
c->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
// some formats want stream headers to be separate
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
return new_stream;
}
int MP4RecorderSink::OpenAudio(AVFormatContext *oc, AVStream *st)
{
AVCodecContext *audio_codec_context;
AVCodec *codec;
audio_codec_context = st->codec;
/* find the audio encoder */
codec = avcodec_find_encoder(audio_codec_context->codec_id);
if (!codec)
{
PA_WarnLog("codec not found\n");
return -1;
}
/* open it */
if (avcodec_open2(audio_codec_context, codec, NULL) < 0)
{
PA_WarnLog("could not open codec\n");
return -1;
}
return 0;
}
int MP4RecorderSink::OpenVideo(AVFormatContext *oc, AVStream *st)
{
AVCodec *codec;
AVCodecContext *c;
c = st->codec;
/* find the video encoder */
codec = avcodec_find_encoder(c->codec_id);
if (!codec)
{
PA_WarnLog("codec not found\n");
return -1;
}
/* open the codec */
if (avcodec_open2(c, codec, NULL) < 0)
{
PA_WarnLog("could not open codec\n");
return -1;
}
return 0;
}
int MP4RecorderSink::WriteAudioFrame(char* data , int len , unsigned int timestemp)
{
if(output_ctx_ == NULL)
return PA_ERROR;
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index= 1;
pkt.data= (uint8_t *)data+7;
pkt.size = len - 7;
if (last_audio_timestamp_ == 0)
{
last_audio_timestamp_ = timestemp;
pkt.pts = 0;
}
else
{
pkt.pts = (timestemp - last_audio_timestamp_) / (av_q2d(audio_stream_->time_base)*1000);
}
/* write the compressed frame in the media file */
if (av_write_frame(output_ctx_, &pkt) != 0)
{
PA_WarnLog("Error while writing audio frame\n");
return PA_ERROR;
}
return PA_OK;
}
int MP4RecorderSink::WriteVideoFrame(char* data , int len , bool keyframe , unsigned int timestemp)
{
if(output_ctx_ == NULL)
return PA_ERROR;
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index = 0;
if (keyframe)
{
pkt.flags |= AV_PKT_FLAG_KEY;
}
pkt.data= (uint8_t *)data;
pkt.size = len;
if (last_video_timestamp_ == 0)
{
last_video_timestamp_ = timestemp;
pkt.pts = 0;
video_timestamp_ = 0;
}
else
{
pkt.pts = (timestemp - last_video_timestamp_) / (av_q2d(video_steam_->time_base)*1000);
if (video_timestamp_ == pkt.pts)
{
pkt.pts += 1;
}
video_timestamp_ = pkt.pts;
}
/* write the compressed frame in the media file */
if (av_write_frame(output_ctx_, &pkt) != 0)
{
PA_WarnLog("Error while writing audio frame\n");
return PA_ERROR;
}
return PA_OK;
}
void MP4RecorderSink::Init()
{
output_format_ = NULL;
output_ctx_ = NULL;
last_audio_timestamp_ = 0;
last_video_timestamp_ = 0;
video_timestamp_ = 0;;
audio_stream_ = NULL;
video_steam_ = NULL;
}
void MP4RecorderSink::Finalize()
{
if(output_ctx_ != NULL)
{
av_write_trailer(output_ctx_);
}
}
void MP4RecorderSink::Close()
{
if (NULL != video_steam_)
{
if(video_steam_->codec != NULL)
{
avcodec_close(video_steam_->codec);
}
video_steam_ = NULL;
}
if (NULL != audio_stream_)
{
if(audio_stream_->codec != NULL)
{
avcodec_close(audio_stream_->codec);
}
audio_stream_ = NULL;
}
if(output_ctx_ != NULL)
{
for(unsigned i = 0; i < output_ctx_->nb_streams; i++)
{
av_freep(&output_ctx_->streams[i]->codec);
av_freep(&output_ctx_->streams[i]);
}
if (output_ctx_->pb != NULL)
{
avio_close(output_ctx_->pb);
}
av_free(output_ctx_);
output_ctx_ = NULL;
}
output_format_ = NULL;
}
bool MP4RecorderSink::IsOpened()
{
return output_format_ != NULL;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment