From 65f3a9b9167029dff4f26e7abab89afa1e0df17d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 22 Feb 2018 09:34:50 -0500 Subject: [PATCH 1/8] try crf instead of preset ultrafast --- src/zm_videostore.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 7eb434141..7943046fc 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -277,7 +277,8 @@ Debug(2,"Using mjpeg"); if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { video_out_ctx->max_b_frames = 1; if ( video_out_ctx->priv_data ) { - av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); + av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); + //av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); } else { Debug(2, "Not setting priv_data"); } From 84b42ef4d7ecfb262c620dc13dc66b8dd82d0ad5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 22 Feb 2018 11:43:04 -0500 Subject: [PATCH 2/8] workarouns for omx --- src/zm_event.cpp | 1 + src/zm_local_camera.cpp | 1 + src/zm_videostore.cpp | 25 ++++++++++++++++--------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index e275fa963..da23288ad 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -465,6 +465,7 @@ void Event::AddPacket( ZMPacket *packet, int score, Image *alarm_image ) { have_video_keyframe = have_video_keyframe || ( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) && packet->keyframe ); if ( videoStore ) { if ( have_video_keyframe ) { + Debug(2,"Have video keyframe, writing packet to videostore"); videoStore->writePacket( packet ); } else { Debug(2, "No video keyframe yet, not writing"); diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index c8a653b14..d75c9b854 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -2135,6 +2135,7 @@ AVStream *LocalCamera::get_VideoStream() { AVFormatContext *oc = avformat_alloc_context(); video_stream = avformat_new_stream( oc, NULL ); if ( video_stream ) { + video_stream->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) video_stream->codecpar->width = width; video_stream->codecpar->height = height; diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 7943046fc..253eb500a 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -200,6 +200,7 @@ Debug(2,"Using mjpeg"); } video_out_ctx->pix_fmt = pf; + video_out_ctx->level = 32; } else { Error("Unsupported output codec selected"); @@ -268,16 +269,20 @@ Debug(2,"Using mjpeg"); } /* video time_base can be set to whatever is handy and supported by encoder */ - video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate - video_out_ctx->gop_size = 12; - video_out_ctx->qmin = 10; - video_out_ctx->qmax = 51; - video_out_ctx->qcompress = 0.6; - //video_out_ctx->bit_rate = 4000000; + //video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate + video_out_ctx->time_base = (AVRational){1, 30}; // microseconds as base frame rate + video_out_ctx->framerate = (AVRational){30,1}; + //video_out_ctx->gop_size = 12; + //video_out_ctx->qmin = 10; + //video_out_ctx->qmax = 51; + //video_out_ctx->qcompress = 0.6; + video_out_ctx->bit_rate = 400*1024; + video_out_ctx->thread_count = 0; + if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { video_out_ctx->max_b_frames = 1; if ( video_out_ctx->priv_data ) { - av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); + av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); //av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); } else { Debug(2, "Not setting priv_data"); @@ -1027,8 +1032,9 @@ int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) { zm_packet->out_frame->pts = 0; zm_packet->out_frame->coded_picture_number = 0; } else { - uint64_t seconds = zm_packet->timestamp->tv_sec*(uint64_t)1000000; - zm_packet->out_frame->pts = ( seconds + zm_packet->timestamp->tv_usec ) - video_start_pts; + uint64_t seconds = ( zm_packet->timestamp->tv_sec*(uint64_t)1000000 + zm_packet->timestamp->tv_usec ) - video_start_pts; + zm_packet->out_frame->pts = av_rescale_q( seconds, video_in_stream->time_base, video_out_ctx->time_base); + //zm_packet->out_frame->pkt_duration = zm_packet->out_frame->pts - video_start_pts; Debug(2, " Setting pts for frame(%d), set to (%" PRId64 ") from (start %" PRIu64 " - %" PRIu64 " - secs(%d) usecs(%d)", frame_count, zm_packet->out_frame->pts, video_start_pts, seconds, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec ); @@ -1044,6 +1050,7 @@ int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) // Do this to allow the encoder to choose whether to use I/P/B frame zm_packet->out_frame->pict_type = AV_PICTURE_TYPE_NONE; + Debug(4, "Sending frame"); if ( (ret = avcodec_send_frame(video_out_ctx, zm_packet->out_frame)) < 0 ) { Error("Could not send frame (error '%s')", av_make_error_string(ret).c_str()); return -1; From f00983b450a8b7f8667f75f65bcf6316a3f39e72 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 27 Feb 2018 20:19:39 -0500 Subject: [PATCH 3/8] Don't setup snap and timestamp if it's an audio packet --- src/zm_monitor.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index e6519acd4..5ce02bbd7 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1273,17 +1273,19 @@ bool Monitor::Analyse() { Debug(2, "Analysis index (%d), last_Write(%d)", index, shared_data->last_write_index); packets_processed += 1; - struct timeval *timestamp = snap->timestamp; - Image *snap_image = snap->image; if ( snap->image_index == -1 ) { snap->unlock(); Debug(2, "skipping because audio"); + // We want to skip, but if we return, we may sleep. + // if ( ! packetqueue->increment_analysis_it() ) { Debug(2, "No more packets to analyse"); return false; } continue; } + struct timeval *timestamp = snap->timestamp; + Image *snap_image = snap->image; // signal is set by capture bool signal = shared_data->signal; @@ -2831,6 +2833,7 @@ int Monitor::Capture() { return 0; } } else { + Debug(4, "Capturing"); captureResult = camera->Capture(*packet); gettimeofday( packet->timestamp, NULL ); if ( captureResult < 0 ) { From c443168389467f2cec636a6b3fe190c025e6483b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 28 Feb 2018 07:17:16 -0800 Subject: [PATCH 4/8] split out codec and encoder, allowing one to specify which encoder to use --- db/zm_create.sql.in | 3 +- db/zm_update-1.31.38.sql | 7 +- src/zm_event.cpp | 2 +- src/zm_monitor.cpp | 32 +- src/zm_monitor.h | 9 +- src/zm_videostore.cpp | 2006 +++++++++++++-------------- src/zm_videostore.h | 9 + version | 2 +- web/includes/Monitor.php | 3 +- web/skins/classic/views/monitor.php | 14 +- 10 files changed, 1036 insertions(+), 1051 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index b50c5934b..0431456ea 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -710,7 +710,8 @@ CREATE TABLE `Monitors` ( `Deinterlacing` int(10) unsigned NOT NULL default '0', `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' , `VideoWriter` TINYINT NOT NULL DEFAULT '0', - `OutputCodec` enum('h264','mjpeg','mpeg1','mpeg2'), + `OutputCodec` int(10) unsigned NOT NULL default 0, + `Encoder` enum('auto','h264','h264_omx','mjpeg','mpeg1','mpeg2'), `OutputContainer` enum('auto','mp4','mkv'), `EncoderParameters` TEXT, `RecordAudio` TINYINT NOT NULL DEFAULT '0', diff --git a/db/zm_update-1.31.38.sql b/db/zm_update-1.31.38.sql index 0ca0be6ea..12fd3e96d 100644 --- a/db/zm_update-1.31.38.sql +++ b/db/zm_update-1.31.38.sql @@ -176,8 +176,10 @@ BEGIN WHERE Id=OLD.MonitorId; END IF; END IF; - ELSEIF ( NEW.Archived AND diff ) THEN - UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + ELSE + IF ( NEW.Archived AND diff ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + END IF; END IF; IF ( diff ) THEN @@ -185,7 +187,6 @@ BEGIN END IF; END; - // delimiter ; diff --git a/src/zm_event.cpp b/src/zm_event.cpp index da23288ad..cd7360ace 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -190,7 +190,7 @@ Event::Event( if ( monitor->GetOptVideoWriter() != 0 ) { std::string container = monitor->OutputContainer(); if ( container == "auto" || container == "" ) { - if ( monitor->OutputCodec() == "h264" ) { + if ( monitor->OutputCodec() == AV_CODEC_ID_H264 ) { container = "mp4"; } else { container = "mkv"; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index e6519acd4..1ae4425c0 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -249,7 +249,8 @@ Monitor::Monitor( int p_colours, VideoWriter p_videowriter, std::string &p_encoderparams, - std::string &p_output_codec, + int p_output_codec, + std::string &p_encoder, std::string &p_output_container, bool p_record_audio, const char *p_event_prefix, @@ -292,6 +293,7 @@ Monitor::Monitor( videowriter( p_videowriter ), encoderparams( p_encoderparams ), output_codec( p_output_codec ), + encoder( p_encoder ), output_container( p_output_container ), record_audio( p_record_audio ), label_coord( p_label_coord ), @@ -1761,7 +1763,7 @@ void Monitor::ReloadLinkedMonitors( const char *p_linked_monitors ) { #if ZM_HAS_V4L int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ) { - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local'"; + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, Encoder, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local'"; ; if ( device[0] ) { sql += " AND Device='"; @@ -1826,7 +1828,8 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose int savejpegs = atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; - std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++; + int output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string encoder = dbrow[col] ? dbrow[col] : ""; col++; std::string output_container = dbrow[col] ? dbrow[col] : ""; col++; bool record_audio = (*dbrow[col] != '0'); col++; @@ -1907,6 +1910,7 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose videowriter, encoderparams, output_codec, + encoder, output_container, record_audio, event_prefix, @@ -1955,7 +1959,7 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose #endif // ZM_HAS_V4L int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose ) { - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'"; + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, Encoder, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'"; if ( staticConfig.SERVER_ID ) { sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); } @@ -2001,7 +2005,8 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c int savejpegs = atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; - std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++; + int output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string encoder = dbrow[col] ? dbrow[col] : ""; col++; std::string output_container = dbrow[col] ? dbrow[col] : ""; col++; bool record_audio = (*dbrow[col] != '0'); col++; @@ -2096,6 +2101,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c videowriter, encoderparams, output_codec, + encoder, output_container, record_audio, event_prefix, @@ -2143,7 +2149,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c } int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'"; + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, Encoder, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'"; if ( file[0] ) { sql += " AND Path='"; sql += file; @@ -2185,7 +2191,8 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu int savejpegs = atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; std::string encoderparams = dbrow[col]; col++; - std::string output_codec = dbrow[col]; col++; + int output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string encoder = dbrow[col]; col++; std::string output_container = dbrow[col]; col++; bool record_audio = (*dbrow[col] != '0'); col++; @@ -2250,6 +2257,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu videowriter, encoderparams, output_codec, + encoder, output_container, record_audio, event_prefix, @@ -2298,7 +2306,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu #if HAVE_LIBAVFORMAT int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'"; + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, Encoder, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'"; if ( file[0] ) { sql += " AND Path = '"; sql += file; @@ -2343,7 +2351,8 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose int savejpegs = atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; - std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++; + int output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string encoder = dbrow[col] ? dbrow[col] : ""; col++; std::string output_container = dbrow[col] ? dbrow[col] : ""; col++; bool record_audio = (*dbrow[col] != '0'); col++; @@ -2414,6 +2423,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose videowriter, encoderparams, output_codec, + encoder, output_container, record_audio, event_prefix, @@ -2524,7 +2534,8 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) { int savejpegs = atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; - std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++; + int output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string encoder = dbrow[col] ? dbrow[col] : ""; col++; std::string output_container = dbrow[col] ? dbrow[col] : ""; col++; bool record_audio = (*dbrow[col] != '0'); col++; @@ -2746,6 +2757,7 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) { videowriter, encoderparams, output_codec, + encoder, output_container, record_audio, event_prefix, diff --git a/src/zm_monitor.h b/src/zm_monitor.h index d35d8e30d..acb76fda9 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -239,7 +239,8 @@ protected: int colours; VideoWriter videowriter; std::string encoderparams; - std::string output_codec; + int output_codec; + std::string encoder; std::string output_container; std::vector encoderparamsvec; _AVPIXELFORMAT imagePixFormat; @@ -359,7 +360,8 @@ public: int p_colours, VideoWriter p_videowriter, std::string &p_encoderparams, - std::string &p_output_codec, + int p_output_codec, + std::string &p_encoder, std::string &p_output_container, bool p_record_audio, const char *p_event_prefix, @@ -458,7 +460,8 @@ public: VideoWriter GetOptVideoWriter() const { return( videowriter ); } const std::vector* GetOptEncoderParams() const { return( &encoderparamsvec ); } const std::string &GetEncoderOptions() const { return( encoderparams ); } - const std::string &OutputCodec() const { return output_codec; } + const std::string &Encoder() const { return encoder; } + const int OutputCodec() const { return output_codec; } const std::string &OutputContainer() const { return output_container; } uint32_t GetVideoWriterEventId() const { return video_store_data->current_event; } diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 253eb500a..8f099ae0c 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -31,6 +31,13 @@ extern "C" { #include "libavutil/time.h" } +VideoStore::CodecData VideoStore::codec_data[] = { + { AV_CODEC_ID_H264, "h264", "h264_omx", AV_PIX_FMT_YUV420P }, + { AV_CODEC_ID_H264, "h264", "h264", AV_PIX_FMT_YUV420P }, + { AV_CODEC_ID_H264, "h264", "libx264", AV_PIX_FMT_YUV420P }, + { AV_CODEC_ID_MJPEG, "mjpeg", "mjpeg", AV_PIX_FMT_YUVJ422P }, +}; + VideoStore::VideoStore( const char *filename_in, const char *format_in, @@ -140,95 +147,34 @@ bool VideoStore::open() { video_in_stream_index = 0; } - if ( monitor->OutputCodec() == "mjpeg" ) { -Debug(2,"Using mjpeg"); - video_out_codec = avcodec_find_encoder_by_name("mjpeg"); - if ( ! video_out_codec ) { - Debug(1, "Didn't find mjpeg encoder"); - video_out_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG); - } - video_out_ctx = avcodec_alloc_context3( video_out_codec ); - video_out_ctx->codec_id = video_out_codec->id; - video_out_ctx->pix_fmt = AV_PIX_FMT_YUVJ422P; - - } else if ( monitor->OutputCodec() == "h264" || monitor->OutputCodec() == "" ) { - AVPixelFormat pf = AV_PIX_FMT_YUV420P; - - // First try hardware accell - video_out_codec = avcodec_find_encoder_by_name("h264_omx"); - if ( ! video_out_codec ) { - Debug(1, "Didn't find omx"); - video_out_codec = avcodec_find_encoder(AV_CODEC_ID_H264); - } - if ( ! video_out_codec ) { - if ( AV_CODEC_ID_NONE == -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - video_in_stream->codecpar->codec_id -#else - video_in_stream->codec->codec_id + video_out_ctx = avcodec_alloc_context3(NULL); + if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { +#if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) + video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; +#else + video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; #endif - ) { - Debug(1, "trying xh264rgb"); - // We will be encoding rgb images, so prefer - video_out_codec = avcodec_find_encoder_by_name("libx264rgb"); - if ( ! video_out_codec ) { - video_out_codec = avcodec_find_encoder_by_name("libx264"); - } else { - pf = -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - (AVPixelFormat)video_in_stream->codecpar->format; -#else - video_in_stream->codec->pix_fmt; -#endif - } - } else { - video_out_codec = avcodec_find_encoder_by_name("libx264"); - pf = AV_PIX_FMT_YUV420P; - } - } - // Need to do lookup by codec_id - if ( ! video_out_codec ) { - Error("Didn't find h264 encoder"); - video_out_codec = NULL; - return false; - } - Debug(1, "Using %s for codec", video_out_codec->name); - video_out_ctx = avcodec_alloc_context3(video_out_codec); - if ( AV_CODEC_ID_H264 != video_out_ctx->codec_id ) { - Warning("Have to set codec_id?"); - video_out_ctx->codec_id = AV_CODEC_ID_H264; - } - - video_out_ctx->pix_fmt = pf; - video_out_ctx->level = 32; - - } else { - Error("Unsupported output codec selected"); - return false; + } + int wanted_codec = monitor->OutputCodec(); + if ( ! wanted_codec ) { + // default to h264 + wanted_codec = AV_CODEC_ID_H264; } - // Copy params from instream to ctx // // FIXME SHould check that we are set to passthrough - if ( video_in_stream && ( video_in_ctx->codec_id == AV_CODEC_ID_H264 ) ) { + if ( video_in_stream && ( video_in_ctx->codec_id == wanted_codec ) ) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) ret = avcodec_parameters_to_context(video_out_ctx, video_in_stream->codecpar); #else ret = avcodec_copy_context( video_out_ctx, video_in_ctx ); #endif + // Copy params from instream to ctx if ( ret < 0 ) { Error("Could not initialize ctx parameteres"); return false; } //video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate video_out_ctx->time_base = video_in_ctx->time_base; - - if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { -#if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) - video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; -#else - video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; -#endif - } // Fix deprecated formats switch ( video_out_ctx->pix_fmt ) { case AV_PIX_FMT_YUVJ422P : @@ -243,128 +189,9 @@ Debug(2,"Using mjpeg"); case AV_PIX_FMT_NONE : case AV_PIX_FMT_YUVJ420P : default: - video_out_ctx->pix_fmt = AV_PIX_FMT_YUV420P; + video_out_ctx->pix_fmt = AV_PIX_FMT_YUV420P; break; } - zm_dump_codec(video_out_ctx); - - } else { - - /** Create a new frame to store the */ - if ( !(video_in_frame = zm_av_frame_alloc()) ) { - Error("Could not allocate video_in frame"); - return false; - } - // Don't have an input stream, so need to tell it what we are sending it, or are transcoding - video_out_ctx->width = monitor->Width(); - video_out_ctx->height = monitor->Height(); - video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO; - - if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { -#if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) - video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; -#else - video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; -#endif - } - - /* video time_base can be set to whatever is handy and supported by encoder */ - //video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate - video_out_ctx->time_base = (AVRational){1, 30}; // microseconds as base frame rate - video_out_ctx->framerate = (AVRational){30,1}; - //video_out_ctx->gop_size = 12; - //video_out_ctx->qmin = 10; - //video_out_ctx->qmax = 51; - //video_out_ctx->qcompress = 0.6; - video_out_ctx->bit_rate = 400*1024; - video_out_ctx->thread_count = 0; - - if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { - video_out_ctx->max_b_frames = 1; - if ( video_out_ctx->priv_data ) { - av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); - //av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); - } else { - Debug(2, "Not setting priv_data"); - } - } else if (video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) { - /* just for testing, we also add B frames */ - video_out_ctx->max_b_frames = 2; - } else if (video_out_ctx->codec_id == AV_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. */ - video_out_ctx->mb_decision = 2; - } - - AVDictionary *opts = 0; - std::string Options = monitor->GetEncoderOptions(); - ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0); - if ( ret < 0 ) { - Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str()); - } else { - AVDictionaryEntry *e = NULL; - while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { - Debug( 3, "Encoder Option %s=%s", e->key, e->value ); - } - } - - if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { - Warning("Can't open video codec (%s)! %s, trying h264", - video_out_codec->name, - av_make_error_string(ret).c_str() - ); - video_out_codec = avcodec_find_encoder_by_name("h264"); - if ( ! video_out_codec ) { - Error("Can't find h264 encoder"); - video_out_codec = avcodec_find_encoder_by_name("libx264"); - if ( ! video_out_codec ) { - Error("Can't find libx264 encoder"); - return false; - } - } - if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { - Error("Can't open video codec (%s)! %s", video_out_codec->name, - av_make_error_string(ret).c_str() ); -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // We allocate and copy in newer ffmpeg, so need to free it - avcodec_free_context(&video_out_ctx); -#endif - video_out_ctx=NULL; - - return false; - } - } // end if can't open codec - Debug(2,"Sucess opening codec"); - AVDictionaryEntry *e = NULL; - while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { - Warning( "Encoder Option %s not recognized by ffmpeg codec", e->key); - } - av_dict_free(&opts); - - if ( !video_out_ctx->codec_tag ) { - video_out_ctx->codec_tag = - av_codec_get_tag(oc->oformat->codec_tag, video_out_ctx->codec_id ); - Debug(2, "No codec_tag, setting to h264 ? "); - } - } // end if copying or trasncoding - - video_out_stream = avformat_new_stream(oc, video_out_codec); - if ( ! video_out_stream ) { - Error("Unable to create video out stream"); - return false; - } -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - ret = avcodec_parameters_from_context(video_out_stream->codecpar, video_out_ctx); - if ( ret < 0 ) { - Error("Could not initialize stream parameteres"); - return false; - } -#else - avcodec_copy_context(video_out_stream->codec, video_out_ctx); -#endif - - if ( video_in_stream && ( video_in_ctx->codec_id == AV_CODEC_ID_H264 ) ) { // Only set orientation if doing passthrough, otherwise the frame image will be rotated Monitor::Orientation orientation = monitor->getOrientation(); if ( orientation ) { @@ -382,15 +209,138 @@ Debug(2,"Using mjpeg"); } else { Warning("Unsupported Orientation(%d)", orientation); } + } // end if orientation + } else { + /** Create a new frame to store the */ + if ( !(video_in_frame = zm_av_frame_alloc()) ) { + Error("Could not allocate video_in frame"); + return false; } + for (int i = 0; i < sizeof(codec_data) / sizeof(*codec_data); i++ ) { + if ( codec_data[i].codec_id != monitor->OutputCodec() ) + continue; + + video_out_codec = avcodec_find_encoder_by_name(codec_data[i].codec_name); + if ( ! video_out_codec ) { + Debug(1, "Didn't find encoder for %s", codec_data[i].codec_name); + continue; + } + + Debug(1, "Using %s for codec", video_out_codec->name); + //video_out_ctx = avcodec_alloc_context3(video_out_codec); + if ( video_out_codec->id != video_out_ctx->codec_id ) { + Warning("Have to set codec_id?"); + video_out_ctx->codec_id = AV_CODEC_ID_H264; + } + + video_out_ctx->pix_fmt = codec_data[i].pix_fmt; + video_out_ctx->level = 32; + + // Don't have an input stream, so need to tell it what we are sending it, or are transcoding + video_out_ctx->width = monitor->Width(); + video_out_ctx->height = monitor->Height(); + video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO; + + /* video time_base can be set to whatever is handy and supported by encoder */ + //video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate + video_out_ctx->time_base = (AVRational){1, 30}; // microseconds as base frame rate + video_out_ctx->framerate = (AVRational){30,1}; + //video_out_ctx->gop_size = 12; + //video_out_ctx->qmin = 10; + //video_out_ctx->qmax = 51; + //video_out_ctx->qcompress = 0.6; + video_out_ctx->bit_rate = 400*1024; + video_out_ctx->thread_count = 0; + + if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { + video_out_ctx->max_b_frames = 1; + if ( video_out_ctx->priv_data ) { + av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); + //av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); + } else { + Debug(2, "Not setting priv_data"); + } + } else if (video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + /* just for testing, we also add B frames */ + video_out_ctx->max_b_frames = 2; + } else if (video_out_ctx->codec_id == AV_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. */ + video_out_ctx->mb_decision = 2; + } + + AVDictionary *opts = 0; + std::string Options = monitor->GetEncoderOptions(); + ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0); + if ( ret < 0 ) { + Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str()); + } else { + AVDictionaryEntry *e = NULL; + while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { + Debug( 3, "Encoder Option %s=%s", e->key, e->value ); + } + } + + if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { + Warning("Can't open video codec (%s) %s", + video_out_codec->name, + av_make_error_string(ret).c_str() + ); + video_out_codec = NULL; + } + + AVDictionaryEntry *e = NULL; + while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { + Warning( "Encoder Option %s not recognized by ffmpeg codec", e->key); + } + av_dict_free(&opts); + if ( video_out_codec ) break; + + } // end foreach codec + + if ( ! video_out_codec ) { + Error("Can't open video codec!"); +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + // We allocate and copy in newer ffmpeg, so need to free it + avcodec_free_context(&video_out_ctx); +#endif + video_out_ctx = NULL; + + return false; + } // end if can't open codec + + Debug(2,"Sucess opening codec"); + + } // end if copying or trasncoding + + if ( !video_out_ctx->codec_tag ) { + video_out_ctx->codec_tag = + av_codec_get_tag(oc->oformat->codec_tag, video_out_ctx->codec_id ); + Debug(2, "No codec_tag, setting to h264 ? "); } + video_out_stream = avformat_new_stream(oc, video_out_codec); + if ( ! video_out_stream ) { + Error("Unable to create video out stream"); + return false; + } +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + ret = avcodec_parameters_from_context(video_out_stream->codecpar, video_out_ctx); + if ( ret < 0 ) { + Error("Could not initialize stream parameteres"); + return false; + } +#else + avcodec_copy_context(video_out_stream->codec, video_out_ctx); +#endif + if ( audio_in_stream ) { audio_in_stream_index = audio_in_stream->index; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) audio_in_ctx = avcodec_alloc_context3(NULL); ret = avcodec_parameters_to_context(audio_in_ctx, - audio_in_stream->codecpar); + audio_in_stream->codecpar); #else audio_in_ctx = audio_in_stream->codec; #endif @@ -398,7 +348,7 @@ Debug(2,"Using mjpeg"); if ( audio_in_ctx->codec_id != AV_CODEC_ID_AAC ) { static char error_buffer[256]; avcodec_string(error_buffer, sizeof(error_buffer), audio_in_ctx, - 0); + 0); Debug(2, "Got something other than AAC (%s)", error_buffer); if ( !setup_resampler() ) { @@ -409,9 +359,9 @@ Debug(2,"Using mjpeg"); audio_out_stream = #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - avformat_new_stream(oc, (const AVCodec *)(audio_in_ctx->codec)); + avformat_new_stream(oc, (const AVCodec *)(audio_in_ctx->codec)); #else - avformat_new_stream(oc, (AVCodec *)audio_in_ctx->codec); + avformat_new_stream(oc, (AVCodec *)audio_in_ctx->codec); #endif if ( !audio_out_stream ) { Error("Unable to create audio out stream\n"); @@ -424,13 +374,13 @@ Debug(2,"Using mjpeg"); // Copy params from instream to ctx if ( (ret = avcodec_parameters_to_context(audio_out_ctx, audio_in_stream->codecpar) ) < 0 ) { Error("Unable to copy audio params to ctx %s\n", - av_make_error_string(ret).c_str()); + av_make_error_string(ret).c_str()); } // Then from ctx to out_stream ret = avcodec_parameters_from_context(audio_out_stream->codecpar, audio_out_ctx); if ( ret < 0 ) { Error("Unable to copy audio params to stream %s\n", - av_make_error_string(ret).c_str()); + av_make_error_string(ret).c_str()); } if ( !audio_out_ctx->codec_tag ) { @@ -445,7 +395,7 @@ Debug(2,"Using mjpeg"); #endif if ( ret < 0 ) { Error("Unable to copy audio ctx %s\n", - av_make_error_string(ret).c_str()); + av_make_error_string(ret).c_str()); audio_out_stream = NULL; } else { if ( audio_out_ctx->channels > 1 ) { @@ -456,26 +406,24 @@ Debug(2,"Using mjpeg"); } } } // end if audio_out_stream - } // end if is AAC + } // end if is AAC if ( audio_out_stream ) { if (oc->oformat->flags & AVFMT_GLOBALHEADER) { #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) - audio_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + audio_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else - audio_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; + audio_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; #endif } } } // end if audio_in_stream /* open the out file, if needed */ - if (!(out_format->flags & AVFMT_NOFILE)) { - ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL); - if (ret < 0) { + if ( !(out_format->flags & AVFMT_NOFILE) ) { + if ( (ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL) ) < 0 ) { Error("Could not open out file '%s': %s\n", filename, - av_make_error_string(ret).c_str()); - + av_make_error_string(ret).c_str()); return false; } } @@ -495,7 +443,7 @@ Debug(2,"Using mjpeg"); } if ( ret < 0 ) { Error("Error occurred when writing out file header to %s: %s\n", - filename, av_make_error_string(ret).c_str()); + filename, av_make_error_string(ret).c_str()); return false; } if ( opts ) av_dict_free(&opts); @@ -503,83 +451,32 @@ Debug(2,"Using mjpeg"); zm_dump_stream_format(oc, 0, 0, 1); if (audio_out_stream) zm_dump_stream_format(oc, 1, 0, 1); return true; -} // end bool VideoStore::open() + } // end bool VideoStore::open() -void VideoStore::write_audio_packet( AVPacket &pkt ) { -//Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, - //pkt.dts, pkt.duration); - pkt.pts = audio_next_pts; - pkt.dts = audio_next_dts; + void VideoStore::write_audio_packet( AVPacket &pkt ) { + //Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, + //pkt.dts, pkt.duration); + pkt.pts = audio_next_pts; + pkt.dts = audio_next_dts; - Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration); - if ( pkt.duration > 0 ) { - pkt.duration = - av_rescale_q(pkt.duration, audio_out_ctx->time_base, - audio_out_stream->time_base); - } - audio_next_pts += pkt.duration; - audio_next_dts += pkt.duration; + Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration); + if ( pkt.duration > 0 ) { + pkt.duration = + av_rescale_q(pkt.duration, audio_out_ctx->time_base, + audio_out_stream->time_base); + } + audio_next_pts += pkt.duration; + audio_next_dts += pkt.duration; - Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration); - pkt.stream_index = audio_out_stream->index; - av_interleaved_write_frame(oc, &pkt); -} // end void VideoStore::Write_audio_packet( AVPacket &pkt ) + Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration); + pkt.stream_index = audio_out_stream->index; + av_interleaved_write_frame(oc, &pkt); + } // end void VideoStore::Write_audio_packet( AVPacket &pkt ) -VideoStore::~VideoStore() { - if ( oc->pb ) { - if ( ( video_out_ctx->codec_id != video_in_ctx->codec_id ) || audio_out_codec ) { - Debug(2,"Different codecs between in and out"); - // The codec queues data. We need to send a flush command and out - // whatever we get. Failures are not fatal. - AVPacket pkt; - // WIthout these we seg fault I don't know why. - pkt.data = NULL; - pkt.size = 0; - av_init_packet(&pkt); - - // I got crashes if the codec didn't do DELAY, so let's test for it. - if ( video_out_ctx->codec && ( video_out_ctx->codec->capabilities & -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - AV_CODEC_CAP_DELAY -#else - CODEC_CAP_DELAY -#endif - ) ) { - -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // Put encoder into flushing mode - avcodec_send_frame(video_out_ctx, NULL); - while (1) { - ret = avcodec_receive_packet(video_out_ctx, &pkt); - if (ret < 0) { - if (AVERROR_EOF != ret) { - Error("ERror encoding audio while flushing (%d) (%s)", ret, - av_err2str(ret)); - } - break; - } -#else - while (1) { - // WIthout these we seg fault I don't know why. - pkt.data = NULL; - pkt.size = 0; - av_init_packet(&pkt); - int got_packet = 0; - ret = avcodec_encode_video2(video_out_ctx, &pkt, NULL, &got_packet); - if ( ret < 0 ) { - Error("ERror encoding video while flushing (%d) (%s)", ret, av_err2str(ret)); - break; - } - if (!got_packet) { - break; - } -#endif - write_video_packet(pkt); - zm_av_packet_unref(&pkt); - } // while have buffered frames - } // end if have delay capability - - if ( audio_out_codec ) { + VideoStore::~VideoStore() { + if ( oc->pb ) { + if ( ( video_out_ctx->codec_id != video_in_ctx->codec_id ) || audio_out_codec ) { + Debug(2,"Different codecs between in and out"); // The codec queues data. We need to send a flush command and out // whatever we get. Failures are not fatal. AVPacket pkt; @@ -588,845 +485,896 @@ VideoStore::~VideoStore() { pkt.size = 0; av_init_packet(&pkt); + // I got crashes if the codec didn't do DELAY, so let's test for it. + if ( video_out_ctx->codec && ( video_out_ctx->codec->capabilities & #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // Put encoder into flushing mode - avcodec_send_frame(audio_out_ctx, NULL); - while (1) { - if ( (ret = avcodec_receive_packet(audio_out_ctx, &pkt) ) < 0 ) { - if ( AVERROR_EOF != ret ) { - Error("ERror encoding audio while flushing (%d) (%s)", ret, av_err2str(ret)); + AV_CODEC_CAP_DELAY +#else + CODEC_CAP_DELAY +#endif + ) ) { + +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + // Put encoder into flushing mode + avcodec_send_frame(video_out_ctx, NULL); + while (1) { + ret = avcodec_receive_packet(video_out_ctx, &pkt); + if (ret < 0) { + if (AVERROR_EOF != ret) { + Error("ERror encoding audio while flushing (%d) (%s)", ret, + av_err2str(ret)); + } + break; } - break; - } #else - while (1) { - pkt.data = NULL; - pkt.size = 0; - av_init_packet(&pkt); - int got_packet = 0; - if ( (ret = avcodec_encode_audio2(audio_out_ctx, &pkt, NULL, &got_packet)) < 0 ) { - Error("ERror encoding audio while flushing (%d) (%s)", ret, av_err2str(ret)); - break; + while (1) { + // WIthout these we seg fault I don't know why. + pkt.data = NULL; + pkt.size = 0; + av_init_packet(&pkt); + int got_packet = 0; + ret = avcodec_encode_video2(video_out_ctx, &pkt, NULL, &got_packet); + if ( ret < 0 ) { + Error("ERror encoding video while flushing (%d) (%s)", ret, av_err2str(ret)); + break; + } + if (!got_packet) { + break; + } +#endif + write_video_packet(pkt); + zm_av_packet_unref(&pkt); + } // while have buffered frames + } // end if have delay capability + + if ( audio_out_codec ) { + // The codec queues data. We need to send a flush command and out + // whatever we get. Failures are not fatal. + AVPacket pkt; + // WIthout these we seg fault I don't know why. + pkt.data = NULL; + pkt.size = 0; + av_init_packet(&pkt); + +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + // Put encoder into flushing mode + avcodec_send_frame(audio_out_ctx, NULL); + while (1) { + if ( (ret = avcodec_receive_packet(audio_out_ctx, &pkt) ) < 0 ) { + if ( AVERROR_EOF != ret ) { + Error("ERror encoding audio while flushing (%d) (%s)", ret, av_err2str(ret)); + } + break; + } +#else + while (1) { + pkt.data = NULL; + pkt.size = 0; + av_init_packet(&pkt); + int got_packet = 0; + if ( (ret = avcodec_encode_audio2(audio_out_ctx, &pkt, NULL, &got_packet)) < 0 ) { + Error("ERror encoding audio while flushing (%d) (%s)", ret, av_err2str(ret)); + break; + } + Debug(1, "Have audio encoder, need to flush it's out"); + if (!got_packet) { + break; + } +#endif + write_audio_packet(pkt); + zm_av_packet_unref(&pkt); + } // while have buffered frames + } // end if audio_out_codec + } // end if buffers + + // Flush Queues + av_interleaved_write_frame(oc, NULL); + + /* Write the trailer before close */ + if ( int rc = av_write_trailer(oc) ) { + Error("Error writing trailer %s", av_err2str(rc)); + } else { + Debug(3, "Sucess Writing trailer"); } - Debug(1, "Have audio encoder, need to flush it's out"); - if (!got_packet) { - break; + + // WHen will be not using a file ? + if ( !(out_format->flags & AVFMT_NOFILE) ) { + /* Close the out file. */ + Debug(2, "Closing"); + if (int rc = avio_close(oc->pb)) { + oc->pb = NULL; + Error("Error closing avio %s", av_err2str(rc)); + } + } else { + Debug(3, "Not closing avio because we are not writing to a file."); } -#endif - write_audio_packet(pkt); - zm_av_packet_unref(&pkt); - } // while have buffered frames - } // end if audio_out_codec - } // end if buffers + } // end if oc->pb - // Flush Queues - av_interleaved_write_frame(oc, NULL); + // I wonder if we should be closing the file first. + // I also wonder if we really need to be doing all the ctx + // allocation/de-allocation constantly, or whether we can just re-use it. + // Just do a file open/close/writeheader/etc. + // What if we were only doing audio recording? - /* Write the trailer before close */ - if ( int rc = av_write_trailer(oc) ) { - Error("Error writing trailer %s", av_err2str(rc)); - } else { - Debug(3, "Sucess Writing trailer"); - } - - // WHen will be not using a file ? - if ( !(out_format->flags & AVFMT_NOFILE) ) { - /* Close the out file. */ - Debug(2, "Closing"); - if (int rc = avio_close(oc->pb)) { - oc->pb = NULL; - Error("Error closing avio %s", av_err2str(rc)); - } - } else { - Debug(3, "Not closing avio because we are not writing to a file."); - } - } // end if oc->pb - - // I wonder if we should be closing the file first. - // I also wonder if we really need to be doing all the ctx - // allocation/de-allocation constantly, or whether we can just re-use it. - // Just do a file open/close/writeheader/etc. - // What if we were only doing audio recording? - - if ( video_out_stream ) { + if ( video_out_stream ) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // We allocate and copy in newer ffmpeg, so need to free it - avcodec_free_context(&video_in_ctx); + // We allocate and copy in newer ffmpeg, so need to free it + avcodec_free_context(&video_in_ctx); #endif - video_in_ctx=NULL; + video_in_ctx=NULL; - avcodec_close(video_out_ctx); + avcodec_close(video_out_ctx); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - avcodec_free_context(&video_out_ctx); + avcodec_free_context(&video_out_ctx); #endif - video_out_ctx = NULL; - Debug(4, "Success freeing video_out_ctx"); - } - if ( audio_out_stream ) { - if ( audio_in_codec ) { - avcodec_close(audio_in_ctx); + video_out_ctx = NULL; + Debug(4, "Success freeing video_out_ctx"); + } + if ( audio_out_stream ) { + if ( audio_in_codec ) { + avcodec_close(audio_in_ctx); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // We allocate and copy in newer ffmpeg, so need to free it - avcodec_free_context(&audio_in_ctx); + // We allocate and copy in newer ffmpeg, so need to free it + avcodec_free_context(&audio_in_ctx); #endif - audio_in_ctx = NULL; - audio_in_codec = NULL; - } // end if audio_in_codec + audio_in_ctx = NULL; + audio_in_codec = NULL; + } // end if audio_in_codec - avcodec_close(audio_out_ctx); + avcodec_close(audio_out_ctx); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - avcodec_free_context(&audio_out_ctx); + avcodec_free_context(&audio_out_ctx); #endif - audio_out_ctx = NULL; + audio_out_ctx = NULL; #ifdef HAVE_LIBAVRESAMPLE - if ( resample_ctx ) { - avresample_close(resample_ctx); - avresample_free(&resample_ctx); - } - if ( out_frame ) { - av_frame_free(&out_frame); - out_frame = NULL; - } - if ( converted_in_samples ) { - av_free(converted_in_samples); - converted_in_samples = NULL; - } + if ( resample_ctx ) { + avresample_close(resample_ctx); + avresample_free(&resample_ctx); + } + if ( out_frame ) { + av_frame_free(&out_frame); + out_frame = NULL; + } + if ( converted_in_samples ) { + av_free(converted_in_samples); + converted_in_samples = NULL; + } #endif - } + } #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ( video_in_ctx ) { - avcodec_free_context(&video_in_ctx); - video_in_ctx = NULL; - } - if ( video_out_ctx ) { - avcodec_close(video_out_ctx); - avcodec_free_context(&video_out_ctx); - video_out_ctx = NULL; - } + if ( video_in_ctx ) { + avcodec_free_context(&video_in_ctx); + video_in_ctx = NULL; + } + if ( video_out_ctx ) { + avcodec_close(video_out_ctx); + avcodec_free_context(&video_out_ctx); + video_out_ctx = NULL; + } #endif - /* free the stream */ - avformat_free_context(oc); -} - -bool VideoStore::setup_resampler() { - //I think this is unneccessary, we should be able to just pass in the decoder from the input. -#ifdef HAVE_LIBAVRESAMPLE - // Newer ffmpeg wants to keep everything separate... so have to lookup our own - // decoder, can't reuse the one from the camera. - AVCodec *audio_in_codec = avcodec_find_decoder( -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - audio_in_stream->codecpar->codec_id -#else - audio_in_ctx->codec_id -#endif - ); - if ( (ret = avcodec_open2(audio_in_ctx, audio_in_codec, NULL)) < 0 ) { - Error("Can't open in codec!"); - return false; - } - - audio_out_codec = avcodec_find_encoder(AV_CODEC_ID_AAC); - if ( !audio_out_codec ) { - Error("Could not find codec for AAC"); - return false; - } - - // Now copy them to the out stream - audio_out_stream = avformat_new_stream(oc, audio_out_codec); - -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - audio_out_ctx = avcodec_alloc_context3(audio_out_codec); - if ( !audio_out_ctx ) { - Error("could not allocate codec ctx for AAC\n"); - audio_out_stream = NULL; - return false; - } -#else - audio_out_ctx = audio_out_stream->codec; -#endif - - /* put sample parameters */ - audio_out_ctx->bit_rate = audio_in_ctx->bit_rate; - audio_out_ctx->sample_rate = audio_in_ctx->sample_rate; - audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt; - audio_out_ctx->channels = audio_in_ctx->channels; - audio_out_ctx->channel_layout = audio_in_ctx->channel_layout; - - if ( audio_out_codec->supported_samplerates ) { - int found = 0; - for ( int i=0; audio_out_codec->supported_samplerates[i]; i++ ) { - if ( audio_out_ctx->sample_rate == - audio_out_codec->supported_samplerates[i]) { - found = 1; - break; + /* free the stream */ + avformat_free_context(oc); } - } - if ( found ) { - Debug(4, "Sample rate is good"); - } else { - audio_out_ctx->sample_rate = - audio_out_codec->supported_samplerates[0]; - Debug(1, "Sampel rate is no good, setting to (%d)", - audio_out_codec->supported_samplerates[0]); - } - } - /* check that the encoder supports s16 pcm in */ - if ( !check_sample_fmt(audio_out_codec, audio_out_ctx->sample_fmt) ) { - Debug(3, "Encoder does not support sample format %s, setting to FLTP", - av_get_sample_fmt_name(audio_out_ctx->sample_fmt)); - audio_out_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; - } - - // Example code doesn't set the codec tb. I think it just uses whatever defaults - //audio_out_ctx->time_base = (AVRational){1, audio_out_ctx->sample_rate}; - - - AVDictionary *opts = NULL; - // Needed to allow AAC - if ( (ret = av_dict_set(&opts, "strict", "experimental", 0)) < 0 ) { - Error("Couldn't set experimental"); - } - ret = avcodec_open2(audio_out_ctx, audio_out_codec, &opts); - av_dict_free(&opts); - if ( ret < 0 ) { - Fatal("could not open codec (%d) (%s)\n", ret, av_make_error_string(ret).c_str()); - audio_out_codec = NULL; - audio_out_ctx = NULL; - audio_out_stream = NULL; - return false; - } - - audio_out_stream->time_base = (AVRational){1, audio_out_ctx->sample_rate}; + bool VideoStore::setup_resampler() { + //I think this is unneccessary, we should be able to just pass in the decoder from the input. +#ifdef HAVE_LIBAVRESAMPLE + // Newer ffmpeg wants to keep everything separate... so have to lookup our own + // decoder, can't reuse the one from the camera. + AVCodec *audio_in_codec = avcodec_find_decoder( #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ( (ret = avcodec_parameters_from_context( - audio_out_stream->codecpar, - audio_out_ctx)) < 0 ) { - Error("Could not initialize stream parameteres"); - return false; - } - //audio_out_stream->codecpar->frame_size = audio_out_ctx->frame_size; - //audio_out_stream->codecpar->bit_rate = audio_out_ctx->bit_rate; + audio_in_stream->codecpar->codec_id #else - avcodec_copy_context( audio_out_stream->codec, audio_out_ctx ); + audio_in_ctx->codec_id #endif + ); + if ( (ret = avcodec_open2(audio_in_ctx, audio_in_codec, NULL)) < 0 ) { + Error("Can't open in codec!"); + return false; + } - Debug(1, - "Audio out context bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " - "layout(%d) frame_size(%d)", - audio_out_ctx->bit_rate, audio_out_ctx->sample_rate, - audio_out_ctx->channels, audio_out_ctx->sample_fmt, - audio_out_ctx->channel_layout, audio_out_ctx->frame_size); + audio_out_codec = avcodec_find_encoder(AV_CODEC_ID_AAC); + if ( !audio_out_codec ) { + Error("Could not find codec for AAC"); + return false; + } + + // Now copy them to the out stream + audio_out_stream = avformat_new_stream(oc, audio_out_codec); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - Debug(1, - "Audio out stream bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " - "layout(%d) frame_size(%d)", - audio_out_stream->codecpar->bit_rate, audio_out_stream->codecpar->sample_rate, - audio_out_stream->codecpar->channels, audio_out_stream->codecpar->format, - audio_out_stream->codecpar->channel_layout, audio_out_stream->codecpar->frame_size); + audio_out_ctx = avcodec_alloc_context3(audio_out_codec); + if ( !audio_out_ctx ) { + Error("could not allocate codec ctx for AAC\n"); + audio_out_stream = NULL; + return false; + } #else - Debug(1, - "Audio out bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " - "layout(%d) frame_size(%d)", - audio_out_stream->codec->bit_rate, audio_out_stream->codec->sample_rate, - audio_out_stream->codec->channels, audio_out_stream->codec->sample_fmt, - audio_out_stream->codec->channel_layout, audio_out_stream->codec->frame_size); + audio_out_ctx = audio_out_stream->codec; #endif - /** Create a new frame to store the audio samples. */ - if ( ! in_frame ) { - if (!(in_frame = zm_av_frame_alloc())) { - Error("Could not allocate in frame"); - return false; - } - } + /* put sample parameters */ + audio_out_ctx->bit_rate = audio_in_ctx->bit_rate; + audio_out_ctx->sample_rate = audio_in_ctx->sample_rate; + audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt; + audio_out_ctx->channels = audio_in_ctx->channels; + audio_out_ctx->channel_layout = audio_in_ctx->channel_layout; - /** Create a new frame to store the audio samples. */ - if ( !(out_frame = zm_av_frame_alloc()) ) { - Error("Could not allocate out frame"); - av_frame_free(&in_frame); - return false; - } - out_frame->sample_rate = audio_out_ctx->sample_rate; + if ( audio_out_codec->supported_samplerates ) { + int found = 0; + for ( int i=0; audio_out_codec->supported_samplerates[i]; i++ ) { + if ( audio_out_ctx->sample_rate == + audio_out_codec->supported_samplerates[i]) { + found = 1; + break; + } + } + if ( found ) { + Debug(4, "Sample rate is good"); + } else { + audio_out_ctx->sample_rate = + audio_out_codec->supported_samplerates[0]; + Debug(1, "Sampel rate is no good, setting to (%d)", + audio_out_codec->supported_samplerates[0]); + } + } - // Setup the audio resampler - resample_ctx = avresample_alloc_context(); - if ( !resample_ctx ) { - Error("Could not allocate resample ctx\n"); - return false; - } + /* check that the encoder supports s16 pcm in */ + if ( !check_sample_fmt(audio_out_codec, audio_out_ctx->sample_fmt) ) { + Debug(3, "Encoder does not support sample format %s, setting to FLTP", + av_get_sample_fmt_name(audio_out_ctx->sample_fmt)); + audio_out_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; + } - uint64_t mono_layout = av_get_channel_layout("mono"); - // Some formats (i.e. WAV) do not produce the proper channel layout - if ( audio_in_ctx->channel_layout == 0 ) { - av_opt_set_int(resample_ctx, "in_channel_layout", mono_layout, 0); - Debug(1, "Bad channel layout. Need to set it to mono (%d).", mono_layout); - } else { - Debug(1, "channel layout. set it to mono (%d).", audio_in_ctx->channel_layout); - av_opt_set_int(resample_ctx, "in_channel_layout", - audio_in_ctx->channel_layout, 0); - } + // Example code doesn't set the codec tb. I think it just uses whatever defaults + //audio_out_ctx->time_base = (AVRational){1, audio_out_ctx->sample_rate}; - av_opt_set_int(resample_ctx, "in_sample_fmt", audio_in_ctx->sample_fmt, 0); - av_opt_set_int(resample_ctx, "in_sample_rate", audio_in_ctx->sample_rate, 0); - av_opt_set_int(resample_ctx, "in_channels", audio_in_ctx->channels, 0); - // av_opt_set_int( resample_ctx, "out_channel_layout", - // audio_out_ctx->channel_layout, 0); - av_opt_set_int(resample_ctx, "out_channel_layout", mono_layout, 0); - av_opt_set_int(resample_ctx, "out_sample_fmt", - audio_out_ctx->sample_fmt, 0); - av_opt_set_int(resample_ctx, "out_sample_rate", - audio_out_ctx->sample_rate, 0); - av_opt_set_int(resample_ctx, "out_channels", - audio_out_ctx->channels, 0); - if ( (ret = avresample_open(resample_ctx)) < 0 ) { - Error("Could not open resample ctx\n"); - return false; - } + AVDictionary *opts = NULL; + // Needed to allow AAC + if ( (ret = av_dict_set(&opts, "strict", "experimental", 0)) < 0 ) { + Error("Couldn't set experimental"); + } + ret = avcodec_open2(audio_out_ctx, audio_out_codec, &opts); + av_dict_free(&opts); + if ( ret < 0 ) { + Fatal("could not open codec (%d) (%s)\n", ret, av_make_error_string(ret).c_str()); + audio_out_codec = NULL; + audio_out_ctx = NULL; + audio_out_stream = NULL; + return false; + } - out_frame->nb_samples = audio_out_ctx->frame_size; - out_frame->format = audio_out_ctx->sample_fmt; - out_frame->channel_layout = audio_out_ctx->channel_layout; - - // The codec gives us the frame size, in samples, we calculate the size of the - // samples buffer in bytes - unsigned int audioSampleBuffer_size = av_samples_get_buffer_size( - NULL, audio_out_ctx->channels, audio_out_ctx->frame_size, - audio_out_ctx->sample_fmt, 0); - converted_in_samples = (uint8_t *)av_malloc(audioSampleBuffer_size); - - if ( !converted_in_samples ) { - Error("Could not allocate converted in sample pointers\n"); - return false; - } - - // Setup the data pointers in the AVFrame - if ( avcodec_fill_audio_frame(out_frame, audio_out_ctx->channels, - audio_out_ctx->sample_fmt, - (const uint8_t *)converted_in_samples, - audioSampleBuffer_size, 0) < 0 ) { - Error("Could not allocate converted in sample pointers\n"); - return false; - } - - return true; + audio_out_stream->time_base = (AVRational){1, audio_out_ctx->sample_rate}; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + if ( (ret = avcodec_parameters_from_context( + audio_out_stream->codecpar, + audio_out_ctx)) < 0 ) { + Error("Could not initialize stream parameteres"); + return false; + } + //audio_out_stream->codecpar->frame_size = audio_out_ctx->frame_size; + //audio_out_stream->codecpar->bit_rate = audio_out_ctx->bit_rate; #else - Error( - "Not built with libavresample library. Cannot do audio conversion to " - "AAC"); - return false; + avcodec_copy_context( audio_out_stream->codec, audio_out_ctx ); #endif -} // end bool VideoStore::setup_resampler() + + Debug(1, + "Audio out context bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " + "layout(%d) frame_size(%d)", + audio_out_ctx->bit_rate, audio_out_ctx->sample_rate, + audio_out_ctx->channels, audio_out_ctx->sample_fmt, + audio_out_ctx->channel_layout, audio_out_ctx->frame_size); + +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + Debug(1, + "Audio out stream bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " + "layout(%d) frame_size(%d)", + audio_out_stream->codecpar->bit_rate, audio_out_stream->codecpar->sample_rate, + audio_out_stream->codecpar->channels, audio_out_stream->codecpar->format, + audio_out_stream->codecpar->channel_layout, audio_out_stream->codecpar->frame_size); +#else + Debug(1, + "Audio out bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " + "layout(%d) frame_size(%d)", + audio_out_stream->codec->bit_rate, audio_out_stream->codec->sample_rate, + audio_out_stream->codec->channels, audio_out_stream->codec->sample_fmt, + audio_out_stream->codec->channel_layout, audio_out_stream->codec->frame_size); +#endif + + /** Create a new frame to store the audio samples. */ + if ( ! in_frame ) { + if (!(in_frame = zm_av_frame_alloc())) { + Error("Could not allocate in frame"); + return false; + } + } + + /** Create a new frame to store the audio samples. */ + if ( !(out_frame = zm_av_frame_alloc()) ) { + Error("Could not allocate out frame"); + av_frame_free(&in_frame); + return false; + } + out_frame->sample_rate = audio_out_ctx->sample_rate; + + // Setup the audio resampler + resample_ctx = avresample_alloc_context(); + if ( !resample_ctx ) { + Error("Could not allocate resample ctx\n"); + return false; + } + + uint64_t mono_layout = av_get_channel_layout("mono"); + // Some formats (i.e. WAV) do not produce the proper channel layout + if ( audio_in_ctx->channel_layout == 0 ) { + av_opt_set_int(resample_ctx, "in_channel_layout", mono_layout, 0); + Debug(1, "Bad channel layout. Need to set it to mono (%d).", mono_layout); + } else { + Debug(1, "channel layout. set it to mono (%d).", audio_in_ctx->channel_layout); + av_opt_set_int(resample_ctx, "in_channel_layout", + audio_in_ctx->channel_layout, 0); + } + + av_opt_set_int(resample_ctx, "in_sample_fmt", audio_in_ctx->sample_fmt, 0); + av_opt_set_int(resample_ctx, "in_sample_rate", audio_in_ctx->sample_rate, 0); + av_opt_set_int(resample_ctx, "in_channels", audio_in_ctx->channels, 0); + // av_opt_set_int( resample_ctx, "out_channel_layout", + // audio_out_ctx->channel_layout, 0); + av_opt_set_int(resample_ctx, "out_channel_layout", mono_layout, 0); + av_opt_set_int(resample_ctx, "out_sample_fmt", + audio_out_ctx->sample_fmt, 0); + av_opt_set_int(resample_ctx, "out_sample_rate", + audio_out_ctx->sample_rate, 0); + av_opt_set_int(resample_ctx, "out_channels", + audio_out_ctx->channels, 0); + + if ( (ret = avresample_open(resample_ctx)) < 0 ) { + Error("Could not open resample ctx\n"); + return false; + } + + out_frame->nb_samples = audio_out_ctx->frame_size; + out_frame->format = audio_out_ctx->sample_fmt; + out_frame->channel_layout = audio_out_ctx->channel_layout; + + // The codec gives us the frame size, in samples, we calculate the size of the + // samples buffer in bytes + unsigned int audioSampleBuffer_size = av_samples_get_buffer_size( + NULL, audio_out_ctx->channels, audio_out_ctx->frame_size, + audio_out_ctx->sample_fmt, 0); + converted_in_samples = (uint8_t *)av_malloc(audioSampleBuffer_size); + + if ( !converted_in_samples ) { + Error("Could not allocate converted in sample pointers\n"); + return false; + } + + // Setup the data pointers in the AVFrame + if ( avcodec_fill_audio_frame(out_frame, audio_out_ctx->channels, + audio_out_ctx->sample_fmt, + (const uint8_t *)converted_in_samples, + audioSampleBuffer_size, 0) < 0 ) { + Error("Could not allocate converted in sample pointers\n"); + return false; + } + + return true; +#else + Error( + "Not built with libavresample library. Cannot do audio conversion to " + "AAC"); + return false; +#endif + } // end bool VideoStore::setup_resampler() -int VideoStore::writePacket( ZMPacket *ipkt ) { - if ( ipkt->packet.stream_index == video_in_stream_index ) { - return writeVideoFramePacket( ipkt ); - } else if ( ipkt->packet.stream_index == audio_in_stream_index ) { - return writeAudioFramePacket( ipkt ); - } - Error("Unknown stream type in packet (%d) out input video stream is (%d) and audio is (%d)", - ipkt->packet.stream_index, video_in_stream_index, ( audio_in_stream ? audio_in_stream_index : -1 ) - ); - return 0; -} - -int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) { - frame_count += 1; - - // if we have to transcode - if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) { - //Debug(3, "Have encoding video frame count (%d)", frame_count); - - if ( ! zm_packet->out_frame ) { - //Debug(3, "Have no out frame"); - AVFrame *out_frame = zm_packet->out_frame = zm_av_frame_alloc(); - if ( ! out_frame ) { - Error("Unable to allocate a frame"); + int VideoStore::writePacket( ZMPacket *ipkt ) { + if ( ipkt->packet.stream_index == video_in_stream_index ) { + return writeVideoFramePacket( ipkt ); + } else if ( ipkt->packet.stream_index == audio_in_stream_index ) { + return writeAudioFramePacket( ipkt ); + } + Error("Unknown stream type in packet (%d) out input video stream is (%d) and audio is (%d)", + ipkt->packet.stream_index, video_in_stream_index, ( audio_in_stream ? audio_in_stream_index : -1 ) + ); return 0; } + + int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) { + frame_count += 1; + + // if we have to transcode + if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) { + //Debug(3, "Have encoding video frame count (%d)", frame_count); + + if ( ! zm_packet->out_frame ) { + //Debug(3, "Have no out frame"); + AVFrame *out_frame = zm_packet->out_frame = zm_av_frame_alloc(); + if ( ! out_frame ) { + Error("Unable to allocate a frame"); + return 0; + } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - int codec_imgsize = av_image_get_buffer_size( - video_out_ctx->pix_fmt, - video_out_ctx->width, - video_out_ctx->height, 1); - zm_packet->buffer = (uint8_t *)av_malloc(codec_imgsize); - av_image_fill_arrays( - out_frame->data, - out_frame->linesize, - zm_packet->buffer, - video_out_ctx->pix_fmt, - video_out_ctx->width, - video_out_ctx->height, - 1); + int codec_imgsize = av_image_get_buffer_size( + video_out_ctx->pix_fmt, + video_out_ctx->width, + video_out_ctx->height, 1); + zm_packet->buffer = (uint8_t *)av_malloc(codec_imgsize); + av_image_fill_arrays( + out_frame->data, + out_frame->linesize, + zm_packet->buffer, + video_out_ctx->pix_fmt, + video_out_ctx->width, + video_out_ctx->height, + 1); #else - int codec_imgsize = avpicture_get_size( - video_out_ctx->pix_fmt, - video_out_ctx->width, - video_out_ctx->height); - zm_packet->buffer = (uint8_t *)av_malloc(codec_imgsize); - avpicture_fill( - (AVPicture *)out_frame, - zm_packet->buffer, - video_out_ctx->pix_fmt, - video_out_ctx->width, - video_out_ctx->height - ); + int codec_imgsize = avpicture_get_size( + video_out_ctx->pix_fmt, + video_out_ctx->width, + video_out_ctx->height); + zm_packet->buffer = (uint8_t *)av_malloc(codec_imgsize); + avpicture_fill( + (AVPicture *)out_frame, + zm_packet->buffer, + video_out_ctx->pix_fmt, + video_out_ctx->width, + video_out_ctx->height + ); #endif - out_frame->width = video_out_ctx->width; - out_frame->height = video_out_ctx->height; - out_frame->format = video_out_ctx->pix_fmt; - //out_frame->pkt_duration = 0; + out_frame->width = video_out_ctx->width; + out_frame->height = video_out_ctx->height; + out_frame->format = video_out_ctx->pix_fmt; + //out_frame->pkt_duration = 0; - if ( ! zm_packet->in_frame ) { - //Debug(2,"Have no in_frame"); - if ( zm_packet->packet.size ) { - //Debug(2,"Decoding"); - if ( ! zm_packet->decode( video_in_ctx ) ) { - Debug(2, "unable to decode yet."); + if ( ! zm_packet->in_frame ) { + //Debug(2,"Have no in_frame"); + if ( zm_packet->packet.size ) { + //Debug(2,"Decoding"); + if ( ! zm_packet->decode( video_in_ctx ) ) { + Debug(2, "unable to decode yet."); + return 0; + } + //Go straight to out frame + swscale.Convert( zm_packet->in_frame, out_frame ); + } else if ( zm_packet->image ) { + //Debug(2,"Have an image, convert it"); + //Go straight to out frame + swscale.Convert( + zm_packet->image, + zm_packet->buffer, + codec_imgsize, + (AVPixelFormat)zm_packet->image->AVPixFormat(), + video_out_ctx->pix_fmt, + video_out_ctx->width, + video_out_ctx->height + ); + + } else { + Error("Have neither in_frame or image!"); + return 0; + } // end if has packet or image + } else { + // Have in_frame.... may need to convert it to out_frame + swscale.Convert(zm_packet->in_frame, zm_packet->out_frame); + } // end if no in_frame + } // end if no out_frame + + zm_packet->out_frame->coded_picture_number = frame_count; + zm_packet->out_frame->display_picture_number = frame_count; + zm_packet->out_frame->sample_aspect_ratio = (AVRational){ 0, 1 }; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + zm_packet->out_frame->pkt_duration = 0; +#endif + + if ( ! video_start_pts ) { + uint64_t temp = zm_packet->timestamp->tv_sec*(uint64_t)1000000; + video_start_pts = temp + zm_packet->timestamp->tv_usec; + Debug(2, "No video_lsat_pts, set to (%" PRId64 ") secs(%d=>%" PRId64 ") usecs(%d)", + video_start_pts, zm_packet->timestamp->tv_sec, temp, zm_packet->timestamp->tv_usec ); + Debug(2, "No video_lsat_pts, set to (%" PRId64 ") secs(%d) usecs(%d)", + video_start_pts, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec ); + zm_packet->out_frame->pts = 0; + zm_packet->out_frame->coded_picture_number = 0; + } else { + uint64_t seconds = ( zm_packet->timestamp->tv_sec*(uint64_t)1000000 + zm_packet->timestamp->tv_usec ) - video_start_pts; + zm_packet->out_frame->pts = av_rescale_q( seconds, video_in_stream->time_base, video_out_ctx->time_base); + + //zm_packet->out_frame->pkt_duration = zm_packet->out_frame->pts - video_start_pts; + Debug(2, " Setting pts for frame(%d), set to (%" PRId64 ") from (start %" PRIu64 " - %" PRIu64 " - secs(%d) usecs(%d)", + frame_count, zm_packet->out_frame->pts, video_start_pts, seconds, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec ); + } + if ( zm_packet->keyframe ) { + //Debug(2, "Setting keyframe was (%d)", zm_packet->out_frame->key_frame ); + zm_packet->out_frame->key_frame = 1; + //Debug(2, "Setting keyframe (%d)", zm_packet->out_frame->key_frame ); + } else { + Debug(2, "Not Setting keyframe"); + } + +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + // Do this to allow the encoder to choose whether to use I/P/B frame + zm_packet->out_frame->pict_type = AV_PICTURE_TYPE_NONE; + Debug(4, "Sending frame"); + if ( (ret = avcodec_send_frame(video_out_ctx, zm_packet->out_frame)) < 0 ) { + Error("Could not send frame (error '%s')", av_make_error_string(ret).c_str()); + return -1; + } + + av_init_packet(&opkt); + opkt.data = NULL; + opkt.size = 0; + if ( (ret = avcodec_receive_packet(video_out_ctx, &opkt)) < 0 ) { + zm_av_packet_unref(&opkt); + if ( AVERROR(EAGAIN) == ret ) { + // THe codec may need more samples than it has, perfectly valid + Debug(3, "Could not recieve packet (error '%s')", + av_make_error_string(ret).c_str()); + return 0; + } else { + Error("Could not recieve packet (error %d = '%s')", ret, + av_make_error_string(ret).c_str()); + } + return -1; + } + //Debug(2, "Got packet using receive_packet, dts:%" PRId64 ", pts:%" PRId64 ", keyframe:%d", opkt.dts, opkt.pts, opkt.flags & AV_PKT_FLAG_KEY ); +#else + av_init_packet(&opkt); + opkt.data = NULL; + opkt.size = 0; + int data_present; + if ( (ret = avcodec_encode_video2( + video_out_ctx, &opkt, zm_packet->out_frame, &data_present)) < 0) { + Error("Could not encode frame (error '%s')", + av_make_error_string(ret).c_str()); + zm_av_packet_unref(&opkt); return 0; } - //Go straight to out frame - swscale.Convert( zm_packet->in_frame, out_frame ); - } else if ( zm_packet->image ) { - //Debug(2,"Have an image, convert it"); - //Go straight to out frame - swscale.Convert( - zm_packet->image, - zm_packet->buffer, - codec_imgsize, - (AVPixelFormat)zm_packet->image->AVPixFormat(), - video_out_ctx->pix_fmt, - video_out_ctx->width, - video_out_ctx->height - ); + if ( !data_present ) { + Debug(2, "Not ready to out a frame yet."); + zm_av_packet_unref(&opkt); + return 0; + } +#endif + // Need to adjust pts/dts values from codec time to stream time + if ( opkt.pts != AV_NOPTS_VALUE) + opkt.pts = av_rescale_q(opkt.pts, video_out_ctx->time_base, video_out_stream->time_base); + if ( opkt.dts != AV_NOPTS_VALUE) + opkt.dts = av_rescale_q(opkt.dts, video_out_ctx->time_base, video_out_stream->time_base); } else { - Error("Have neither in_frame or image!"); - return 0; - } // end if has packet or image - } else { - // Have in_frame.... may need to convert it to out_frame - swscale.Convert(zm_packet->in_frame, zm_packet->out_frame); - } // end if no in_frame - } // end if no out_frame + AVPacket *ipkt = &zm_packet->packet; + Debug(3, "Doing passthrough, just copy packet"); + // Just copy it because the codec is the same + av_init_packet(&opkt); + opkt.data = ipkt->data; + opkt.size = ipkt->size; + opkt.flags = ipkt->flags; + if ( ! video_start_pts ) { + // If video has b-frames, pts can be AV_NOPTS_VALUE so use dts intead + video_start_pts = ipkt->dts; + Debug(2, "No video_lsat_pts, set to (%" PRId64 ")", video_start_pts ); + opkt.dts = opkt.pts = 0; + } else { + dumpPacket(ipkt); + if ( ipkt->pts != AV_NOPTS_VALUE ) + opkt.pts = av_rescale_q( ipkt->pts - video_start_pts, video_in_stream->time_base, video_out_stream->time_base ); + else + opkt.pts = 0; - zm_packet->out_frame->coded_picture_number = frame_count; - zm_packet->out_frame->display_picture_number = frame_count; - zm_packet->out_frame->sample_aspect_ratio = (AVRational){ 0, 1 }; + if ( ipkt->dts != AV_NOPTS_VALUE ) + opkt.dts = av_rescale_q( ipkt->dts - video_start_pts, video_in_stream->time_base, video_out_stream->time_base ); + else + opkt.dts = 0; + Debug(2, "out_stream_time_base(%d/%d) in_stream_time_base(%d/%d) video_start_pts(%" PRId64 ")", + video_out_stream->time_base.num, video_out_stream->time_base.den, + video_in_stream->time_base.num, video_in_stream->time_base.den, + video_start_pts + ); + + dumpPacket(&opkt); + + opkt.duration = av_rescale_q( opkt.duration, video_in_stream->time_base, video_out_stream->time_base); + } + } + + //opkt.duration = 0; + + write_video_packet( opkt ); + zm_av_packet_unref(&opkt); + + return 1; + } // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) + + void VideoStore::write_video_packet( AVPacket &opkt ) { + + if ( opkt.dts > opkt.pts ) { + Debug(1, + "opkt.dts(%" PRId64 ") must be <= opkt.pts(%" PRId64 "). Decompression must happen " + "before presentation.", + opkt.dts, opkt.pts); + opkt.dts = opkt.pts; + } + + opkt.stream_index = video_out_stream->index; + //av_packet_rescale_ts( &opkt, video_out_ctx->time_base, video_out_stream->time_base ); + + dumpPacket(&opkt, "writing video packet"); + + if ( (opkt.data == NULL) || (opkt.size < 1) ) { + Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__); + //dumpPacket(&opkt); + + //} else if ((video_next_dts > 0) && (video_next_dts > opkt.dts)) { + //Warning("%s:%d: DTS out of order: next:%lld \u226E opkt.dts %lld; discarding frame", + //__FILE__, __LINE__, video_next_dts, opkt.dts); + //video_next_dts = opkt.dts; + //dumpPacket(&opkt); + + } else { + if ( (ret = av_interleaved_write_frame(oc, &opkt)) < 0 ) { + // There's nothing we can really do if the frame is rejected, just drop it + // and get on with the next + Warning( + "%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) " + " ", + __FILE__, __LINE__, av_make_error_string(ret).c_str(), ret); + + //dumpPacket(&safepkt); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - zm_packet->out_frame->pkt_duration = 0; + if ( video_in_stream ) + zm_dump_codecpar(video_in_stream->codecpar); + zm_dump_codecpar(video_out_stream->codecpar); #endif - - if ( ! video_start_pts ) { - uint64_t temp = zm_packet->timestamp->tv_sec*(uint64_t)1000000; - video_start_pts = temp + zm_packet->timestamp->tv_usec; - Debug(2, "No video_lsat_pts, set to (%" PRId64 ") secs(%d=>%" PRId64 ") usecs(%d)", - video_start_pts, zm_packet->timestamp->tv_sec, temp, zm_packet->timestamp->tv_usec ); - Debug(2, "No video_lsat_pts, set to (%" PRId64 ") secs(%d) usecs(%d)", - video_start_pts, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec ); - zm_packet->out_frame->pts = 0; - zm_packet->out_frame->coded_picture_number = 0; - } else { - uint64_t seconds = ( zm_packet->timestamp->tv_sec*(uint64_t)1000000 + zm_packet->timestamp->tv_usec ) - video_start_pts; - zm_packet->out_frame->pts = av_rescale_q( seconds, video_in_stream->time_base, video_out_ctx->time_base); - - //zm_packet->out_frame->pkt_duration = zm_packet->out_frame->pts - video_start_pts; - Debug(2, " Setting pts for frame(%d), set to (%" PRId64 ") from (start %" PRIu64 " - %" PRIu64 " - secs(%d) usecs(%d)", - frame_count, zm_packet->out_frame->pts, video_start_pts, seconds, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec ); - } - if ( zm_packet->keyframe ) { - //Debug(2, "Setting keyframe was (%d)", zm_packet->out_frame->key_frame ); - zm_packet->out_frame->key_frame = 1; - //Debug(2, "Setting keyframe (%d)", zm_packet->out_frame->key_frame ); - } else { - Debug(2, "Not Setting keyframe"); - } - -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // Do this to allow the encoder to choose whether to use I/P/B frame - zm_packet->out_frame->pict_type = AV_PICTURE_TYPE_NONE; - Debug(4, "Sending frame"); - if ( (ret = avcodec_send_frame(video_out_ctx, zm_packet->out_frame)) < 0 ) { - Error("Could not send frame (error '%s')", av_make_error_string(ret).c_str()); - return -1; - } - - av_init_packet(&opkt); - opkt.data = NULL; - opkt.size = 0; - if ( (ret = avcodec_receive_packet(video_out_ctx, &opkt)) < 0 ) { - zm_av_packet_unref(&opkt); - if ( AVERROR(EAGAIN) == ret ) { - // THe codec may need more samples than it has, perfectly valid - Debug(3, "Could not recieve packet (error '%s')", - av_make_error_string(ret).c_str()); - return 0; - } else { - Error("Could not recieve packet (error %d = '%s')", ret, - av_make_error_string(ret).c_str()); + } else { + packets_written += 1; + } } - return -1; - } -//Debug(2, "Got packet using receive_packet, dts:%" PRId64 ", pts:%" PRId64 ", keyframe:%d", opkt.dts, opkt.pts, opkt.flags & AV_PKT_FLAG_KEY ); -#else - av_init_packet(&opkt); - opkt.data = NULL; - opkt.size = 0; - int data_present; - if ( (ret = avcodec_encode_video2( - video_out_ctx, &opkt, zm_packet->out_frame, &data_present)) < 0) { - Error("Could not encode frame (error '%s')", - av_make_error_string(ret).c_str()); - zm_av_packet_unref(&opkt); - return 0; - } - if ( !data_present ) { - Debug(2, "Not ready to out a frame yet."); - zm_av_packet_unref(&opkt); - return 0; - } -#endif - // Need to adjust pts/dts values from codec time to stream time - if ( opkt.pts != AV_NOPTS_VALUE) - opkt.pts = av_rescale_q(opkt.pts, video_out_ctx->time_base, video_out_stream->time_base); - if ( opkt.dts != AV_NOPTS_VALUE) - opkt.dts = av_rescale_q(opkt.dts, video_out_ctx->time_base, video_out_stream->time_base); - } else { - AVPacket *ipkt = &zm_packet->packet; - Debug(3, "Doing passthrough, just copy packet"); - // Just copy it because the codec is the same - av_init_packet(&opkt); - opkt.data = ipkt->data; - opkt.size = ipkt->size; - opkt.flags = ipkt->flags; - if ( ! video_start_pts ) { - // If video has b-frames, pts can be AV_NOPTS_VALUE so use dts intead - video_start_pts = ipkt->dts; - Debug(2, "No video_lsat_pts, set to (%" PRId64 ")", video_start_pts ); - opkt.dts = opkt.pts = 0; - } else { - dumpPacket(ipkt); - if ( ipkt->pts != AV_NOPTS_VALUE ) - opkt.pts = av_rescale_q( ipkt->pts - video_start_pts, video_in_stream->time_base, video_out_stream->time_base ); - else - opkt.pts = 0; + } // end void VideoStore::write_video_packet - if ( ipkt->dts != AV_NOPTS_VALUE ) - opkt.dts = av_rescale_q( ipkt->dts - video_start_pts, video_in_stream->time_base, video_out_stream->time_base ); - else - opkt.dts = 0; - Debug(2, "out_stream_time_base(%d/%d) in_stream_time_base(%d/%d) video_start_pts(%" PRId64 ")", - video_out_stream->time_base.num, video_out_stream->time_base.den, - video_in_stream->time_base.num, video_in_stream->time_base.den, - video_start_pts - ); + int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) { + Debug(4, "writeAudioFrame"); - dumpPacket(&opkt); + AVPacket *ipkt = &zm_packet->packet; - opkt.duration = av_rescale_q( opkt.duration, video_in_stream->time_base, video_out_stream->time_base); - } - } + if ( !audio_out_stream ) { + Debug(1, "Called writeAudioFramePacket when no audio_out_stream"); + return 0; // FIXME -ve return codes do not free packet in ffmpeg_camera at + // the moment + } - //opkt.duration = 0; - - write_video_packet( opkt ); - zm_av_packet_unref(&opkt); - - return 1; -} // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) - -void VideoStore::write_video_packet( AVPacket &opkt ) { - - if ( opkt.dts > opkt.pts ) { - Debug(1, - "opkt.dts(%" PRId64 ") must be <= opkt.pts(%" PRId64 "). Decompression must happen " - "before presentation.", - opkt.dts, opkt.pts); - opkt.dts = opkt.pts; - } - - opkt.stream_index = video_out_stream->index; - //av_packet_rescale_ts( &opkt, video_out_ctx->time_base, video_out_stream->time_base ); - - dumpPacket(&opkt, "writing video packet"); - - if ( (opkt.data == NULL) || (opkt.size < 1) ) { - Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__); - //dumpPacket(&opkt); - - //} else if ((video_next_dts > 0) && (video_next_dts > opkt.dts)) { - //Warning("%s:%d: DTS out of order: next:%lld \u226E opkt.dts %lld; discarding frame", - //__FILE__, __LINE__, video_next_dts, opkt.dts); - //video_next_dts = opkt.dts; - //dumpPacket(&opkt); - - } else { - if ( (ret = av_interleaved_write_frame(oc, &opkt)) < 0 ) { - // There's nothing we can really do if the frame is rejected, just drop it - // and get on with the next - Warning( - "%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) " - " ", - __FILE__, __LINE__, av_make_error_string(ret).c_str(), ret); - - //dumpPacket(&safepkt); -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ( video_in_stream ) - zm_dump_codecpar(video_in_stream->codecpar); - zm_dump_codecpar(video_out_stream->codecpar); -#endif - } else { - packets_written += 1; - } - } - -} // end void VideoStore::write_video_packet - -int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) { - Debug(4, "writeAudioFrame"); - - AVPacket *ipkt = &zm_packet->packet; - - if ( !audio_out_stream ) { - Debug(1, "Called writeAudioFramePacket when no audio_out_stream"); - return 0; // FIXME -ve return codes do not free packet in ffmpeg_camera at - // the moment - } - - if ( audio_out_codec ) { - Debug(3, "Have audio codec"); + if ( audio_out_codec ) { + Debug(3, "Have audio codec"); #ifdef HAVE_LIBAVRESAMPLE #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ( (ret = avcodec_send_packet(audio_in_ctx, ipkt)) < 0 ) { - Error("avcodec_send_packet fail %s", av_make_error_string(ret).c_str()); - return 0; - } - if ( (ret = avcodec_receive_frame(audio_in_ctx, in_frame)) < 0 ) { - Error("avcodec_receive_frame fail %s", av_make_error_string(ret).c_str()); - return 0; - } - Debug(2, - "Input Frame: samples(%d), format(%d), sample_rate(%d), channel " - "layout(%d)", - in_frame->nb_samples, in_frame->format, - in_frame->sample_rate, in_frame->channel_layout); + if ( (ret = avcodec_send_packet(audio_in_ctx, ipkt)) < 0 ) { + Error("avcodec_send_packet fail %s", av_make_error_string(ret).c_str()); + return 0; + } + if ( (ret = avcodec_receive_frame(audio_in_ctx, in_frame)) < 0 ) { + Error("avcodec_receive_frame fail %s", av_make_error_string(ret).c_str()); + return 0; + } + Debug(2, + "Input Frame: samples(%d), format(%d), sample_rate(%d), channel " + "layout(%d)", + in_frame->nb_samples, in_frame->format, + in_frame->sample_rate, in_frame->channel_layout); #else - /** - * Decode the audio frame stored in the packet. - * The in audio stream decoder is used to do this. - * If we are at the end of the file, pass an empty packet to the decoder - * to flush it. - */ - int data_present; - if ( (ret = avcodec_decode_audio4(audio_in_ctx, in_frame, - &data_present, ipkt)) < 0 ) { - Error("Could not decode frame (error '%s')\n", - av_make_error_string(ret).c_str()); - dumpPacket(ipkt); - av_frame_free(&in_frame); - return 0; - } - if ( !data_present ) { - Debug(2, "Not ready to transcode a frame yet."); - return 0; - } + /** + * Decode the audio frame stored in the packet. + * The in audio stream decoder is used to do this. + * If we are at the end of the file, pass an empty packet to the decoder + * to flush it. + */ + int data_present; + if ( (ret = avcodec_decode_audio4(audio_in_ctx, in_frame, + &data_present, ipkt)) < 0 ) { + Error("Could not decode frame (error '%s')\n", + av_make_error_string(ret).c_str()); + dumpPacket(ipkt); + av_frame_free(&in_frame); + return 0; + } + if ( !data_present ) { + Debug(2, "Not ready to transcode a frame yet."); + return 0; + } #endif - int frame_size = out_frame->nb_samples; - in_frame->pts = audio_next_pts; + int frame_size = out_frame->nb_samples; + in_frame->pts = audio_next_pts; - // Resample the in into the audioSampleBuffer until we proceed the whole - // decoded data - if ((ret = avresample_convert(resample_ctx, NULL, 0, 0, in_frame->data, - 0, in_frame->nb_samples)) < 0) { - Error("Could not resample frame (error '%s')\n", - av_make_error_string(ret).c_str()); - av_frame_unref(in_frame); - return 0; - } - av_frame_unref(in_frame); + // Resample the in into the audioSampleBuffer until we proceed the whole + // decoded data + if ((ret = avresample_convert(resample_ctx, NULL, 0, 0, in_frame->data, + 0, in_frame->nb_samples)) < 0) { + Error("Could not resample frame (error '%s')\n", + av_make_error_string(ret).c_str()); + av_frame_unref(in_frame); + return 0; + } + av_frame_unref(in_frame); - int samples_available = avresample_available(resample_ctx); - if ( samples_available < frame_size ) { - Debug(1, "Not enough samples yet (%d)", samples_available); - return 0; - } + int samples_available = avresample_available(resample_ctx); + if ( samples_available < frame_size ) { + Debug(1, "Not enough samples yet (%d)", samples_available); + return 0; + } - Debug(3, "Output_frame samples (%d)", out_frame->nb_samples); - // Read a frame audio data from the resample fifo - if ( avresample_read(resample_ctx, out_frame->data, frame_size) != frame_size) { - Warning("Error reading resampled audio: "); - return 0; - } - Debug(2, - "Frame: samples(%d), format(%d), sample_rate(%d), channel layout(%d)", - out_frame->nb_samples, out_frame->format, - out_frame->sample_rate, out_frame->channel_layout); + Debug(3, "Output_frame samples (%d)", out_frame->nb_samples); + // Read a frame audio data from the resample fifo + if ( avresample_read(resample_ctx, out_frame->data, frame_size) != frame_size) { + Warning("Error reading resampled audio: "); + return 0; + } + Debug(2, + "Frame: samples(%d), format(%d), sample_rate(%d), channel layout(%d)", + out_frame->nb_samples, out_frame->format, + out_frame->sample_rate, out_frame->channel_layout); - av_init_packet(&opkt); - Debug(5, "after init packet"); + av_init_packet(&opkt); + Debug(5, "after init packet"); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ((ret = avcodec_send_frame(audio_out_ctx, out_frame)) < 0) { - Error("Could not send frame (error '%s')", - av_make_error_string(ret).c_str()); - zm_av_packet_unref(&opkt); - return 0; - } + if ((ret = avcodec_send_frame(audio_out_ctx, out_frame)) < 0) { + Error("Could not send frame (error '%s')", + av_make_error_string(ret).c_str()); + zm_av_packet_unref(&opkt); + return 0; + } - // av_frame_unref( out_frame ); + // av_frame_unref( out_frame ); - if ( (ret = avcodec_receive_packet(audio_out_ctx, &opkt)) < 0 ) { - if ( AVERROR(EAGAIN) == ret ) { - // THe codec may need more samples than it has, perfectly valid - Debug(3, "Could not recieve packet (error '%s')", - av_make_error_string(ret).c_str()); - } else { - Error("Could not recieve packet (error %d = '%s')", ret, - av_make_error_string(ret).c_str()); - } - //zm_av_packet_unref(&opkt); - av_frame_unref(in_frame); - // av_frame_unref( out_frame ); - return 0; - } + if ( (ret = avcodec_receive_packet(audio_out_ctx, &opkt)) < 0 ) { + if ( AVERROR(EAGAIN) == ret ) { + // THe codec may need more samples than it has, perfectly valid + Debug(3, "Could not recieve packet (error '%s')", + av_make_error_string(ret).c_str()); + } else { + Error("Could not recieve packet (error %d = '%s')", ret, + av_make_error_string(ret).c_str()); + } + //zm_av_packet_unref(&opkt); + av_frame_unref(in_frame); + // av_frame_unref( out_frame ); + return 0; + } #else - if ( (ret = avcodec_encode_audio2(audio_out_ctx, &opkt, out_frame, - &data_present)) < 0) { - Error("Could not encode frame (error '%s')", - av_make_error_string(ret).c_str()); - zm_av_packet_unref(&opkt); - return 0; - } - if ( !data_present ) { - Debug(2, "Not ready to out a frame yet."); - zm_av_packet_unref(&opkt); - return 0; - } + if ( (ret = avcodec_encode_audio2(audio_out_ctx, &opkt, out_frame, + &data_present)) < 0) { + Error("Could not encode frame (error '%s')", + av_make_error_string(ret).c_str()); + zm_av_packet_unref(&opkt); + return 0; + } + if ( !data_present ) { + Debug(2, "Not ready to out a frame yet."); + zm_av_packet_unref(&opkt); + return 0; + } #endif #endif - opkt.duration = out_frame->nb_samples; - } else { - av_init_packet(&opkt); - Debug(5, "after init packet"); - opkt.data = ipkt->data; - opkt.size = ipkt->size; - opkt.duration = ipkt->duration; - } + opkt.duration = out_frame->nb_samples; + } else { + av_init_packet(&opkt); + Debug(5, "after init packet"); + opkt.data = ipkt->data; + opkt.size = ipkt->size; + opkt.duration = ipkt->duration; + } -// PTS is difficult, because of the buffering of the audio packets in the -// resampler. So we have to do it once we actually have a packet... -// audio_last_pts is the pts of ipkt, audio_next_pts is the last pts of the -// out + // PTS is difficult, because of the buffering of the audio packets in the + // resampler. So we have to do it once we actually have a packet... + // audio_last_pts is the pts of ipkt, audio_next_pts is the last pts of the + // out -// Scale the PTS of the outgoing packet to be the correct time base + // Scale the PTS of the outgoing packet to be the correct time base #if 0 - if ( ipkt->pts != AV_NOPTS_VALUE ) { - if ( !audio_last_pts ) { - opkt.pts = 0; - Debug(1, "No audio_last_pts"); - } else { - if ( audio_last_pts > ipkt->pts ) { - Debug(1, "Resetting audio_start_pts from (%d) to (%d)", audio_last_pts, ipkt->pts); - opkt.pts = audio_next_pts + av_rescale_q(ipkt->pts, audio_in_stream->time_base, audio_out_stream->time_base); - } else { - opkt.pts = audio_next_pts + av_rescale_q(ipkt->pts - audio_last_pts, audio_in_stream->time_base, audio_out_stream->time_base); - } - Debug(2, "audio opkt.pts = %d from ipkt->pts(%d) - last_pts(%d)", opkt.pts, ipkt->pts, audio_last_pts); - } - audio_last_pts = ipkt->pts; - } else { - Debug(2, "opkt.pts = undef"); - opkt.pts = AV_NOPTS_VALUE; - } + if ( ipkt->pts != AV_NOPTS_VALUE ) { + if ( !audio_last_pts ) { + opkt.pts = 0; + Debug(1, "No audio_last_pts"); + } else { + if ( audio_last_pts > ipkt->pts ) { + Debug(1, "Resetting audio_start_pts from (%d) to (%d)", audio_last_pts, ipkt->pts); + opkt.pts = audio_next_pts + av_rescale_q(ipkt->pts, audio_in_stream->time_base, audio_out_stream->time_base); + } else { + opkt.pts = audio_next_pts + av_rescale_q(ipkt->pts - audio_last_pts, audio_in_stream->time_base, audio_out_stream->time_base); + } + Debug(2, "audio opkt.pts = %d from ipkt->pts(%d) - last_pts(%d)", opkt.pts, ipkt->pts, audio_last_pts); + } + audio_last_pts = ipkt->pts; + } else { + Debug(2, "opkt.pts = undef"); + opkt.pts = AV_NOPTS_VALUE; + } #else - opkt.pts = audio_next_pts; - opkt.dts = audio_next_dts; + opkt.pts = audio_next_pts; + opkt.dts = audio_next_dts; #endif #if 0 - if ( ipkt->dts == AV_NOPTS_VALUE ) { - // So if the in has no dts assigned... still need an out dts... so we use cur_dts? + if ( ipkt->dts == AV_NOPTS_VALUE ) { + // So if the in has no dts assigned... still need an out dts... so we use cur_dts? - if ( audio_last_dts >= audio_in_stream->cur_dts ) { - Debug(1, "Resetting audio_last_dts from (%d) to cur_dts (%d)", audio_last_dts, audio_in_stream->cur_dts); - opkt.dts = audio_next_dts + av_rescale_q( audio_in_stream->cur_dts, AV_TIME_BASE_Q, audio_out_stream->time_base); - } else { - opkt.dts = audio_next_dts + av_rescale_q( audio_in_stream->cur_dts - audio_last_dts, AV_TIME_BASE_Q, audio_out_stream->time_base); + if ( audio_last_dts >= audio_in_stream->cur_dts ) { + Debug(1, "Resetting audio_last_dts from (%d) to cur_dts (%d)", audio_last_dts, audio_in_stream->cur_dts); + opkt.dts = audio_next_dts + av_rescale_q( audio_in_stream->cur_dts, AV_TIME_BASE_Q, audio_out_stream->time_base); + } else { + opkt.dts = audio_next_dts + av_rescale_q( audio_in_stream->cur_dts - audio_last_dts, AV_TIME_BASE_Q, audio_out_stream->time_base); + } + audio_last_dts = audio_in_stream->cur_dts; + Debug(2, "opkt.dts = %d from video_in_stream->cur_dts(%d) - last_dts(%d)", opkt.dts, audio_in_stream->cur_dts, audio_last_dts); + } else { + if ( audio_last_dts >= ipkt->dts ) { + Debug(1, "Resetting audio_last_dts from (%d) to (%d)", audio_last_dts, ipkt->dts ); + opkt.dts = audio_next_dts + av_rescale_q(ipkt->dts, audio_in_stream->time_base, audio_out_stream->time_base); + } else { + opkt.dts = audio_next_dts + av_rescale_q(ipkt->dts - audio_last_dts, audio_in_stream->time_base, audio_out_stream->time_base); + Debug(2, "opkt.dts = %d from previous(%d) + ( ipkt->dts(%d) - last_dts(%d) )", opkt.dts, audio_next_dts, ipkt->dts, audio_last_dts ); + } + } } - audio_last_dts = audio_in_stream->cur_dts; - Debug(2, "opkt.dts = %d from video_in_stream->cur_dts(%d) - last_dts(%d)", opkt.dts, audio_in_stream->cur_dts, audio_last_dts); - } else { - if ( audio_last_dts >= ipkt->dts ) { - Debug(1, "Resetting audio_last_dts from (%d) to (%d)", audio_last_dts, ipkt->dts ); - opkt.dts = audio_next_dts + av_rescale_q(ipkt->dts, audio_in_stream->time_base, audio_out_stream->time_base); - } else { - opkt.dts = audio_next_dts + av_rescale_q(ipkt->dts - audio_last_dts, audio_in_stream->time_base, audio_out_stream->time_base); - Debug(2, "opkt.dts = %d from previous(%d) + ( ipkt->dts(%d) - last_dts(%d) )", opkt.dts, audio_next_dts, ipkt->dts, audio_last_dts ); - } - } - } #endif - // audio_last_dts = ipkt->dts; - if ( opkt.dts > opkt.pts ) { - Debug(1, - "opkt.dts(%" PRId64 ") must be <= opkt.pts(%" PRId64 "). Decompression must happen " - "before presentation.", - opkt.dts, opkt.pts); - opkt.dts = opkt.pts; - } + // audio_last_dts = ipkt->dts; + if ( opkt.dts > opkt.pts ) { + Debug(1, + "opkt.dts(%" PRId64 ") must be <= opkt.pts(%" PRId64 "). Decompression must happen " + "before presentation.", + opkt.dts, opkt.pts); + opkt.dts = opkt.pts; + } - //opkt.duration = out_frame ? out_frame->nb_samples : ipkt->duration; - // opkt.duration = av_rescale_q(ipkt->duration, audio_in_stream->time_base, - // audio_out_stream->time_base); - dumpPacket(&opkt); + //opkt.duration = out_frame ? out_frame->nb_samples : ipkt->duration; + // opkt.duration = av_rescale_q(ipkt->duration, audio_in_stream->time_base, + // audio_out_stream->time_base); + dumpPacket(&opkt); - // pkt.pos: byte position in stream, -1 if unknown - opkt.pos = -1; - opkt.stream_index = audio_out_stream->index; - audio_next_dts = opkt.dts + opkt.duration; - audio_next_pts = opkt.pts + opkt.duration; + // pkt.pos: byte position in stream, -1 if unknown + opkt.pos = -1; + opkt.stream_index = audio_out_stream->index; + audio_next_dts = opkt.dts + opkt.duration; + audio_next_pts = opkt.pts + opkt.duration; - AVPacket safepkt; - memcpy(&safepkt, &opkt, sizeof(AVPacket)); - ret = av_interleaved_write_frame(oc, &opkt); - if ( ret != 0 ) { - Error("Error writing audio frame packet: %s\n", - av_make_error_string(ret).c_str()); - dumpPacket(&safepkt); - } else { - Debug(2, "Success writing audio frame"); - } - zm_av_packet_unref(&opkt); - return 1; -} // end int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) + AVPacket safepkt; + memcpy(&safepkt, &opkt, sizeof(AVPacket)); + ret = av_interleaved_write_frame(oc, &opkt); + if ( ret != 0 ) { + Error("Error writing audio frame packet: %s\n", + av_make_error_string(ret).c_str()); + dumpPacket(&safepkt); + } else { + Debug(2, "Success writing audio frame"); + } + zm_av_packet_unref(&opkt); + return 1; + } // end int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) -int VideoStore::write_packets( zm_packetqueue &queue ) { - // Need to write out all the frames from the last keyframe? - // No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe. - unsigned int packet_count = 0; - ZMPacket *queued_packet; + int VideoStore::write_packets( zm_packetqueue &queue ) { + // Need to write out all the frames from the last keyframe? + // No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe. + unsigned int packet_count = 0; + ZMPacket *queued_packet; - while ( ( queued_packet = queue.popPacket() ) ) { - AVPacket *avp = queued_packet->av_packet(); + while ( ( queued_packet = queue.popPacket() ) ) { + AVPacket *avp = queued_packet->av_packet(); - packet_count += 1; - //Write the packet to our video store - Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", - avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, queue.size() ); - int ret = this->writePacket( queued_packet ); - if ( ret < 0 ) { - //Less than zero and we skipped a frame - } - delete queued_packet; - } // end while packets in the packetqueue - Debug(2, "Wrote %d queued packets", packet_count ); - return packet_count; -} // end int VideoStore::write_packets( PacketQueue &queue ) { + packet_count += 1; + //Write the packet to our video store + Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", + avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, queue.size() ); + int ret = this->writePacket( queued_packet ); + if ( ret < 0 ) { + //Less than zero and we skipped a frame + } + delete queued_packet; + } // end while packets in the packetqueue + Debug(2, "Wrote %d queued packets", packet_count ); + return packet_count; + } // end int VideoStore::write_packets( PacketQueue &queue ) { diff --git a/src/zm_videostore.h b/src/zm_videostore.h index d2b14ae2e..cf8168c06 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -19,6 +19,15 @@ class VideoStore; class VideoStore { private: +struct CodecData { + const int codec_id; + const char *codec_codec; + const char *codec_name; + const enum AVPixelFormat pix_fmt; + +}; +static struct CodecData codec_data[]; + Monitor *monitor; AVOutputFormat *out_format; AVFormatContext *oc; diff --git a/version b/version index 6367ac8f2..0be5d1204 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.39 +1.31.40 diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index a4f5f03db..942f25c87 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -15,7 +15,8 @@ private $defaults = array( 'Height' => null, 'Orientation' => null, 'AnalysisFPSLimit' => null, - 'OutputCodec' => 'h264', + 'OutputCodec' => '0', + 'Encoder' => 'auto', 'OutputContainer' => 'auto', 'ZoneCount' => 0, 'Triggers' => null, diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 50aaad267..d00496f93 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -470,13 +470,21 @@ $videowriteropts = array( 'X264 Encode' => 1, 'H264 Camera Passthrough' => 2 ); -$videowriter_codecs = array( - '' => translate('Disabled'), +$videowriter_encoders = array( + '' => translate('Auto'), + 'h264_omx' => 'h264_omx', 'h264' => 'h264', 'mjpeg' => 'mjpeg', 'mpeg1' => 'mpeg1', 'mpeg2' => 'mpeg2', ); +$videowriter_codecs = array( + '0' => translate('Disabled'), + '220' => 'h264', + '8' => 'mjpeg', + '1' => 'mpeg1', + '2' => 'mpeg2', +); $videowriter_containers = array( '' => translate('Auto'), 'mp4' => 'mp4', @@ -609,6 +617,7 @@ if ( $tab != 'storage' ) { + @@ -912,6 +921,7 @@ if ( $monitor->Type() == 'Local' ) { OutputCodec() );?> + Encoder() );?> OutputContainer() );?> RecordAudio() ) { ?> checked="checked"/> From a19e6b619c293580b0639909196f33ca5e0134fd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 28 Feb 2018 07:17:52 -0800 Subject: [PATCH 5/8] add Encoder to Monitors --- db/zm_update-1.31.40.sql | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 db/zm_update-1.31.40.sql diff --git a/db/zm_update-1.31.40.sql b/db/zm_update-1.31.40.sql new file mode 100644 index 000000000..66b98eb66 --- /dev/null +++ b/db/zm_update-1.31.40.sql @@ -0,0 +1,14 @@ +ALTER TABLE `Monitors` MODIFY `OutputCodec` int(10) UNSIGNED NOT NULL default 0; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'Encoder' + ) > 0, +"SELECT 'Column Encoder already exists in Monitors'", +"ALTER TABLE `Monitors` ADD `Encoder` enum('auto','h264','h264_omx','mjpeg','mpeg1','mpeg2') AFTER `OutputCodec`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + From 646f0dd65ecbd4a553cfa67aaaf10b86fdd77c44 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 3 Mar 2018 13:31:55 -0800 Subject: [PATCH 6/8] fix merge --- src/zm_ffmpeg_camera.cpp | 303 --------------------------------------- src/zm_ffmpeg_input.cpp | 70 +-------- 2 files changed, 6 insertions(+), 367 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 58fd115b1..154e6fb37 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -131,15 +131,7 @@ int FfmpegCamera::PrimeCapture() { } int FfmpegCamera::PreCapture() { -<<<<<<< HEAD return 0; -======= - // If Reopen was called, then ffmpeg is closed and we need to reopen it. - if ( ! mCanCapture ) - return OpenFfmpeg(); - // Nothing to do here - return( 0 ); ->>>>>>> storageareas } int FfmpegCamera::Capture( ZMPacket &zm_packet ) { @@ -483,301 +475,6 @@ int FfmpegCamera::CloseFfmpeg() { } return 0; -<<<<<<< HEAD } // end int FfmpegCamera::CloseFfmpeg() -======= -} // end FfmpegCamera::Close - -//Function to handle capture and store -int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event_file ) { - if ( ! mCanCapture ) { - return -1; - } - int ret; - static char errbuf[AV_ERROR_MAX_STRING_SIZE]; - - int frameComplete = false; - while ( ! frameComplete ) { - av_init_packet( &packet ); - - ret = av_read_frame( mFormatContext, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf ); - return -1; - } - - int keyframe = packet.flags & AV_PKT_FLAG_KEY; - dumpPacket(&packet); - - //Video recording - if ( recording.tv_sec ) { - - uint32_t last_event_id = monitor->GetLastEventId() ; - uint32_t video_writer_event_id = monitor->GetVideoWriterEventId(); - - if ( last_event_id != video_writer_event_id ) { - Debug(2, "Have change of event. last_event(%d), our current (%d)", last_event_id, video_writer_event_id ); - - if ( videoStore ) { - Info("Re-starting video storage module"); - - // I don't know if this is important or not... but I figure we might as well write this last packet out to the store before closing it. - // Also don't know how much it matters for audio. - if ( packet.stream_index == mVideoStreamId ) { - //Write the packet to our video store - int ret = videoStore->writeVideoFramePacket( &packet ); - if ( ret < 0 ) { //Less than zero and we skipped a frame - Warning("Error writing last packet to videostore."); - } - } // end if video - - delete videoStore; - videoStore = NULL; - have_video_keyframe = false; - - monitor->SetVideoWriterEventId( 0 ); - } // end if videoStore - } // end if end of recording - - if ( last_event_id and ! videoStore ) { - //Instantiate the video storage module - - if ( record_audio ) { - if ( mAudioStreamId == -1 ) { - Debug(3, "Record Audio on but no audio stream found"); - videoStore = new VideoStore((const char *) event_file, "mp4", - mFormatContext->streams[mVideoStreamId], - NULL, - startTime, - this->getMonitor()); - - } else { - Debug(3, "Video module initiated with audio stream"); - videoStore = new VideoStore((const char *) event_file, "mp4", - mFormatContext->streams[mVideoStreamId], - mFormatContext->streams[mAudioStreamId], - startTime, - this->getMonitor()); - } - } else { - if ( mAudioStreamId >= 0 ) { - Debug(3, "Record_audio is false so exclude audio stream"); - } - videoStore = new VideoStore((const char *) event_file, "mp4", - mFormatContext->streams[mVideoStreamId], - NULL, - startTime, - this->getMonitor()); - } // end if record_audio - - if ( ! videoStore->open() ) { - delete videoStore; - videoStore = NULL; - - } else { - monitor->SetVideoWriterEventId( last_event_id ); - - // Need to write out all the frames from the last keyframe? - // No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe. - unsigned int packet_count = 0; - ZMPacket *queued_packet; - - // Clear all packets that predate the moment when the recording began - packetqueue.clear_unwanted_packets( &recording, mVideoStreamId ); - - while ( ( queued_packet = packetqueue.popPacket() ) ) { - AVPacket *avp = queued_packet->av_packet(); - - packet_count += 1; - //Write the packet to our video store - Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue.size() ); - if ( avp->stream_index == mVideoStreamId ) { - ret = videoStore->writeVideoFramePacket( avp ); - have_video_keyframe = true; - } else if ( avp->stream_index == mAudioStreamId ) { - ret = videoStore->writeAudioFramePacket( avp ); - } else { - Warning("Unknown stream id in queued packet (%d)", avp->stream_index ); - ret = -1; - } - if ( ret < 0 ) { - //Less than zero and we skipped a frame - } - delete queued_packet; - } // end while packets in the packetqueue - Debug(2, "Wrote %d queued packets", packet_count ); - } - } // end if ! was recording - - } else { - // Not recording - if ( videoStore ) { - Info("Deleting videoStore instance"); - delete videoStore; - videoStore = NULL; - have_video_keyframe = false; - monitor->SetVideoWriterEventId( 0 ); - } - - // Buffer video packets, since we are not recording. - // All audio packets are keyframes, so only if it's a video keyframe - if ( packet.stream_index == mVideoStreamId ) { - if ( keyframe ) { - Debug(3, "Clearing queue"); - packetqueue.clearQueue( monitor->GetPreEventCount(), mVideoStreamId ); - packetqueue.queuePacket( &packet ); - } else if ( packetqueue.size() ) { - // it's a keyframe or we already have something in the queue - packetqueue.queuePacket( &packet ); - } - } else if ( packet.stream_index == mAudioStreamId ) { - // The following lines should ensure that the queue always begins with a video keyframe -//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() ); - if ( record_audio && packetqueue.size() ) { - // if it's audio, and we are doing audio, and there is already something in the queue - packetqueue.queuePacket( &packet ); - } - } - } // end if recording or not - - if ( packet.stream_index == mVideoStreamId ) { - // only do decode if we have had a keyframe, should save a few cycles. - if ( have_video_keyframe || keyframe ) { - - if ( videoStore ) { - - //Write the packet to our video store - int ret = videoStore->writeVideoFramePacket( &packet ); - if ( ret < 0 ) { //Less than zero and we skipped a frame - zm_av_packet_unref( &packet ); - return 0; - } - have_video_keyframe = true; - } - } // end if keyframe or have_video_keyframe - - Debug(4, "about to decode video" ); - -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - ret = avcodec_send_packet( mVideoCodecContext, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } -#if HAVE_AVUTIL_HWCONTEXT_H - if ( hwaccel ) { - ret = avcodec_receive_frame( mVideoCodecContext, hwFrame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - ret = av_hwframe_transfer_data(mRawFrame, hwFrame, 0); - if (ret < 0) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to transfer frame at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - } else { -#endif - ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Warning( "Unable to receive frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - -#if HAVE_AVUTIL_HWCONTEXT_H - } -#endif - - frameComplete = 1; -# else - ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to decode frame at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } -#endif - - if ( frameComplete ) { - Debug( 4, "Got frame %d", frameCount ); - - uint8_t* directbuffer; - - /* Request a writeable buffer of the target image */ - directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); - if ( directbuffer == NULL ) { - Error("Failed requesting writeable buffer for the captured image."); - zm_av_packet_unref( &packet ); - return (-1); - } -#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, width, height, 1); -#else - avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); -#endif - - - if (sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, - 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) { - Error("Unable to convert raw format %u to target format %u at frame %d", - mVideoCodecContext->pix_fmt, imagePixFormat, frameCount); - return -1; - } - - frameCount++; - } else { - Debug( 3, "Not framecomplete after av_read_frame" ); - } // end if frameComplete - } else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams - frameComplete = 1; - if ( videoStore ) { - if ( record_audio ) { - if ( have_video_keyframe ) { - Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", mAudioStreamId, packet.stream_index ); - //Write the packet to our video store - //FIXME no relevance of last key frame - int ret = videoStore->writeAudioFramePacket( &packet ); - if ( ret < 0 ) {//Less than zero and we skipped a frame - Warning("Failure to write audio packet."); - zm_av_packet_unref( &packet ); - return 0; - } - } else { - Debug(3, "Not recording audio yet because we don't have a video keyframe yet"); - } - } else { - Debug(4, "Not doing recording of audio packet" ); - } - } else { - Debug(4, "Have audio packet, but not recording atm" ); - } - zm_av_packet_unref( &packet ); - return 0; - } else { -#if LIBAVUTIL_VERSION_CHECK(56, 23, 0, 23, 0) - Debug( 3, "Some other stream index %d, %s", packet.stream_index, av_get_media_type_string( mFormatContext->streams[packet.stream_index]->codecpar->codec_type) ); -#else - Debug( 3, "Some other stream index %d", packet.stream_index ); -#endif - } // end if is video or audio or something else - - // the packet contents are ref counted... when queuing, we allocate another packet and reference it with that one, so we should always need to unref here, which should not affect the queued version. - zm_av_packet_unref( &packet ); - } // end while ! frameComplete - return frameCount; -} // end FfmpegCamera::CaptureAndRecord - - ->>>>>>> storageareas #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index ed47cbd41..063e2eb46 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -135,77 +135,21 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id, int frame_number ) { // Check for Connection failure. (ret == -110) ) { - Info( "av_read_frame returned %s.", av_make_error_string(ret).c_str() ); + Info("av_read_frame returned %s.", av_make_error_string(ret).c_str()); } else { - Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, - av_make_error_string(ret).c_str() ); + Error("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, + av_make_error_string(ret).c_str()); } return NULL; } -<<<<<<< HEAD if ( ( stream_id < 0 ) || ( packet.stream_index != stream_id ) ) { Warning("Packet is not for our stream (%d)", packet.stream_index); return NULL; -======= - if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) { - Debug(3,"Packet is for our stream (%d)", packet.stream_index ); - - AVCodecContext *context = streams[packet.stream_index].context; - -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - ret = avcodec_send_packet( context, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } else { - Debug(1, "Success getting a packet"); } -#if HAVE_AVUTIL_HWCONTEXT_H - if ( hwaccel ) { - ret = avcodec_receive_frame( context, hwFrame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - ret = av_hwframe_transfer_data(frame, hwFrame, 0); - if (ret < 0) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - } else { -#endif - Debug(1,"Getting a frame?"); - ret = avcodec_receive_frame( context, frame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - -#if HAVE_AVUTIL_HWCONTEXT_H ->>>>>>> storageareas - } - -<<<<<<< HEAD - if ( ! zm_receive_frame( streams[packet.stream_index].context, frame, packet ) ) { + if ( ! zm_receive_frame(streams[packet.stream_index].context, frame, packet) ) { Error("Unable to get frame %d, continuing", streams[packet.stream_index].frame_count); -======= - frameComplete = 1; -# else - ret = zm_avcodec_decode_video(context, frame, &frameComplete, &packet); - if ( ret < 0 ) { - av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); - Error( "Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); ->>>>>>> storageareas zm_av_packet_unref( &packet ); continue; } else { @@ -213,12 +157,10 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id, int frame_number ) { streams[packet.stream_index].frame_count += 1; } - zm_av_packet_unref( &packet ); + zm_av_packet_unref(&packet); if ( frame_number == -1 ) break; } // end while frame_number > streams.frame_count return frame; -} // end AVFrame *FFmpeg_Input::get_frame - - +} // end AVFrame *FFmpeg_Input::get_frame From 2291816f4e3575ad6c299fcc3d8aa4aad428bc31 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 10 Mar 2018 10:12:12 -0500 Subject: [PATCH 7/8] fix memleak --- src/zm_monitor.cpp | 10 +++++----- src/zm_videostore.cpp | 5 ----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 564779a0c..e7c6cae1e 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -605,11 +605,6 @@ bool Monitor::connect() { Monitor::~Monitor() { - if ( event_delete_thread ) { - event_delete_thread->join(); - delete event_delete_thread; - event_delete_thread = NULL; - } if ( timestamps ) { @@ -634,6 +629,11 @@ Monitor::~Monitor() { Info("%s: image_count:%d - Closing event %d, shutting down", name, image_count, event->Id() ); closeEvent(); } + if ( event_delete_thread ) { + event_delete_thread->join(); + delete event_delete_thread; + event_delete_thread = NULL; + } if ( deinterlacing_value == 4 ) { delete next_buffer.image; diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index a448b1ddc..767dca962 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -214,11 +214,6 @@ bool VideoStore::open() { } } // end if orientation } else { - /** Create a new frame to store the */ - if ( !(video_in_frame = zm_av_frame_alloc()) ) { - Error("Could not allocate video_in frame"); - return false; - } for ( unsigned int i = 0; i < sizeof(codec_data) / sizeof(*codec_data); i++ ) { if ( codec_data[i].codec_id != monitor->OutputCodec() ) continue; From 3ae9993975f9696784a5d4563200ea57aee41e5c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 16 Mar 2018 11:58:50 -0400 Subject: [PATCH 8/8] allow errors, and add a missing fi --- distros/ubuntu1604/zoneminder.postinst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distros/ubuntu1604/zoneminder.postinst b/distros/ubuntu1604/zoneminder.postinst index d731b8764..605bdc193 100644 --- a/distros/ubuntu1604/zoneminder.postinst +++ b/distros/ubuntu1604/zoneminder.postinst @@ -1,6 +1,6 @@ #! /bin/sh -set -e +set +e if [ "$1" = "configure" ]; then @@ -56,6 +56,7 @@ if [ "$1" = "configure" ]; then echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql else echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + fi else echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.'