// // ZoneMinder Video Storage Implementation // Written by Chris Wiggins // http://chriswiggins.co.nz // Modification by Steve Gilvarry // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include #include "zm.h" #include "zm_videostore.h" extern "C"{ #include "libavutil/time.h" } VideoStore::VideoStore(const char *filename_in, const char *format_in, AVStream *input_st, AVStream *inpaud_st, int64_t nStartTime) { //store inputs in variables local to class filename = filename_in; format = format_in; keyframeMessage = false; keyframeSkipNumber = 0; Info("Opening video storage stream %s\n", filename); //Init everything we need int ret; av_register_all(); //Allocate the output media context based on the filename of the context avformat_alloc_output_context2(&oc, NULL, NULL, filename); //Couldn't deduce format from filename, trying from format name if (!oc) { avformat_alloc_output_context2(&oc, NULL, format, filename); if (!oc) { Fatal("Could not create video storage stream %s as no output context" " could not be assigned based on filename or format %s", filename, format); } } fmt = oc->oformat; video_st = avformat_new_stream(oc, input_st->codec->codec); if (!video_st) { Fatal("Unable to create video out stream\n"); } ret = avcodec_copy_context(video_st->codec, input_st->codec); if (ret < 0) { Fatal("Unable to copy input video context to output video context " "%s\n", av_make_error_string(ret).c_str()); } video_st->codec->codec_tag = 0; if (oc->oformat->flags & AVFMT_GLOBALHEADER) { video_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; } if (inpaud_st) { audio_st = avformat_new_stream(oc, inpaud_st->codec->codec); if (!audio_st) { Fatal("Unable to create audio out stream\n"); } ret=avcodec_copy_context(audio_st->codec, inpaud_st->codec); if (ret < 0) { Fatal("Unable to copy audio context %s\n", av_make_error_string(ret).c_str()); } audio_st->codec->codec_tag = 0; if (oc->oformat->flags & AVFMT_GLOBALHEADER) { audio_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; } } else { audio_st = NULL; } /* open the output file, if needed */ if (!(fmt->flags & AVFMT_NOFILE)) { ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE,NULL,NULL); if (ret < 0) { Fatal("Could not open output file '%s': %s\n", filename, av_make_error_string(ret).c_str()); } } /* Write the stream header, if any. */ ret = avformat_write_header(oc, NULL); if (ret < 0) { Fatal("Error occurred when writing output file header: %s\n", av_make_error_string(ret).c_str()); } startPts = 0; startDts = 0; filter_in_rescale_delta_last = AV_NOPTS_VALUE; startTime=av_gettime()-nStartTime;//oc->start_time; Info("VideoStore startTime=%d\n",startTime); } VideoStore::~VideoStore(){ /* Write the trailer before close */ av_write_trailer(oc); avcodec_close(video_st->codec); if (audio_st) { avcodec_close(audio_st->codec); } if (!(fmt->flags & AVFMT_NOFILE)) { /* Close the output file. */ avio_close(oc->pb); } /* free the stream */ avformat_free_context(oc); } int VideoStore::writeVideoFramePacket(AVPacket *ipkt, AVStream *input_st){//, AVPacket *lastKeyframePkt){ int64_t ost_tb_start_time = av_rescale_q(startTime, AV_TIME_BASE_Q, video_st->time_base); AVPacket opkt; AVPicture pict; av_init_packet(&opkt); //Scale the PTS of the outgoing packet to be the correct time base if (ipkt->pts != AV_NOPTS_VALUE) opkt.pts = av_rescale_q(ipkt->pts-startPts, input_st->time_base, video_st->time_base) - ost_tb_start_time; else opkt.pts = AV_NOPTS_VALUE; //Scale the DTS of the outgoing packet to be the correct time base if(ipkt->dts == AV_NOPTS_VALUE) opkt.dts = av_rescale_q(input_st->cur_dts-startDts, AV_TIME_BASE_Q, video_st->time_base); else opkt.dts = av_rescale_q(ipkt->dts-startDts, input_st->time_base, video_st->time_base); opkt.dts -= ost_tb_start_time; opkt.duration = av_rescale_q(ipkt->duration, input_st->time_base, video_st->time_base); opkt.flags = ipkt->flags; opkt.pos=-1; opkt.data = ipkt->data; opkt.size = ipkt->size; opkt.stream_index = ipkt->stream_index; /*opkt.flags |= AV_PKT_FLAG_KEY;*/ if (video_st->codec->codec_type == AVMEDIA_TYPE_VIDEO && (fmt->flags & AVFMT_RAWPICTURE)) { /* store AVPicture in AVPacket, as expected by the output format */ avpicture_fill(&pict, opkt.data, video_st->codec->pix_fmt, video_st->codec->width, video_st->codec->height); opkt.data = (uint8_t *)&pict; opkt.size = sizeof(AVPicture); opkt.flags |= AV_PKT_FLAG_KEY; } int ret; ret = av_interleaved_write_frame(oc, &opkt); if(ret<0){ Fatal("Error encoding video frame packet: %s\n", av_make_error_string(ret).c_str()); } av_free_packet(&opkt); return 0; } int VideoStore::writeAudioFramePacket(AVPacket *ipkt, AVStream *input_st){ if(!audio_st) return -1;//FIXME -ve return codes do not free packet in ffmpeg_camera at the moment /*if(!keyframeMessage) return -1;*/ int64_t ost_tb_start_time = av_rescale_q(startTime, AV_TIME_BASE_Q, video_st->time_base); AVPacket opkt; av_init_packet(&opkt); //Scale the PTS of the outgoing packet to be the correct time base if (ipkt->pts != AV_NOPTS_VALUE) opkt.pts = av_rescale_q(ipkt->pts-startPts, input_st->time_base, audio_st->time_base) - ost_tb_start_time; else opkt.pts = AV_NOPTS_VALUE; //Scale the DTS of the outgoing packet to be the correct time base if(ipkt->dts == AV_NOPTS_VALUE) opkt.dts = av_rescale_q(input_st->cur_dts-startDts, AV_TIME_BASE_Q, audio_st->time_base); else opkt.dts = av_rescale_q(ipkt->dts-startDts, input_st->time_base, audio_st->time_base); opkt.dts -= ost_tb_start_time; if (audio_st->codec->codec_type == AVMEDIA_TYPE_AUDIO && ipkt->dts != AV_NOPTS_VALUE) { int duration = av_get_audio_frame_duration(input_st->codec, ipkt->size); if(!duration) duration = input_st->codec->frame_size; //FIXME where to get filter_in_rescale_delta_last //FIXME av_rescale_delta doesn't exist in ubuntu vivid libavtools opkt.dts = opkt.pts = av_rescale_delta(input_st->time_base, ipkt->dts, (AVRational){1, input_st->codec->sample_rate}, duration, &filter_in_rescale_delta_last, audio_st->time_base) - ost_tb_start_time; } opkt.duration = av_rescale_q(ipkt->duration, input_st->time_base, audio_st->time_base); opkt.pos=-1; opkt.flags = ipkt->flags; opkt.data = ipkt->data; opkt.size = ipkt->size; opkt.stream_index = ipkt->stream_index; /*opkt.flags |= AV_PKT_FLAG_KEY;*/ int ret; ret = av_interleaved_write_frame(oc, &opkt); if(ret<0){ Fatal("Error encoding audio frame packet: %s\n", av_make_error_string(ret).c_str()); } av_free_packet(&opkt); return 0; }