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"/>