Logo Search packages:      
Sourcecode: zoneminder version File versions  Download package

zm_mpeg.cpp

/*
 * ZoneMinder MPEG class implementation, $Date: 2006/01/17 10:56:30 $, $Revision: 1.12 $
 * Copyright (C) 2003, 2004, 2005, 2006  Philip Coombes
 * 
 * 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 <stdlib.h>
#include <string.h>

#include "zm.h"
#include "zm_mpeg.h"

#if HAVE_LIBAVCODEC

bool VideoStream::initialised = false;

VideoStream::MimeData VideoStream::mime_data[] = {
      { "asf", "video/x-ms-asf" },
      { "swf", "application/x-shockwave-flash" },
      { "mp4", "video/mp4" },
      { "move", "video/quicktime" }
};

void VideoStream::Initialise()
{
      av_register_all();
      initialised = true;
}

void VideoStream::SetupFormat( const char *p_filename, const char *p_format )
{
      filename = p_filename;
      format = p_format;

      /* auto detect the output format from the name. default is mpeg. */
      of = guess_format( format, NULL, NULL);
      if ( !of )
      {
            Warning(( "Could not deduce output format from file extension: using MPEG." ));
            of = guess_format("mpeg", NULL, NULL);
      }
      if ( !of )
      {
            Fatal(( "Could not find suitable output format" ));
      }
      
      /* allocate the output media context */
      ofc = (AVFormatContext *)av_mallocz(sizeof(AVFormatContext));
      if ( !ofc )
      {
            Fatal(( "Memory error" ));
      }
      ofc->oformat = of;
      snprintf( ofc->filename, sizeof(ofc->filename), "%s", filename );
}

void VideoStream::SetupCodec( int colours, int width, int height, int bitrate, int frame_rate )
{
      pf = (colours==1?PIX_FMT_GRAY8:PIX_FMT_RGB24);

      /* add the video streams using the default format codecs
         and initialize the codecs */
      ost = NULL;
      if (of->video_codec != CODEC_ID_NONE)
      {
            ost = av_new_stream(ofc, 0);
            if (!ost)
            {
                  Fatal(( "Could not alloc stream" ));
            }
            
#if ZM_FFMPEG_CVS
            AVCodecContext *c = ost->codec;
#else
            AVCodecContext *c = &ost->codec;
#endif

            c->codec_id = of->video_codec;
            c->codec_type = CODEC_TYPE_VIDEO;

            /* put sample parameters */
            c->bit_rate = bitrate;
            c->pix_fmt = PIX_FMT_YUV420P;
            /* resolution must be a multiple of two */
            c->width = width;
            c->height = height;
#if ZM_FFMPEG_CVS
            /* time base: this is the fundamental unit of time (in seconds) in terms
               of which frame timestamps are represented. for fixed-fps content,
               timebase should be 1/framerate and timestamp increments should be
               identically 1. */
            c->time_base.den = frame_rate;
            c->time_base.num = 1;
#else
            /* frames per second */
            c->frame_rate = frame_rate;
            c->frame_rate_base = 1;
#endif
            c->gop_size = frame_rate/2; /* emit one intra frame every half second or so */
            c->gop_size = 12;
            if ( c->gop_size < 3 )
                  c->gop_size = 3;
            // some formats want stream headers to be seperate
            if(!strcmp(ofc->oformat->name, "mp4") || !strcmp(ofc->oformat->name, "mov") || !strcmp(ofc->oformat->name, "3gp"))
                  c->flags |= CODEC_FLAG_GLOBAL_HEADER;
      }
}

void VideoStream::SetParameters()
{
      /* set the output parameters (must be done even if no
         parameters). */
      if ( av_set_parameters(ofc, NULL) < 0 )
      {
            Fatal(( "Invalid output format parameters" ));
      }
      //dump_format(ofc, 0, filename, 1);
}

const char *VideoStream::MimeType() const
{
      for ( int i = 0; i < sizeof(mime_data)/sizeof(*mime_data); i++ )
      {
            if ( strcmp( format, mime_data[i].format ) == 0 )
            {
                  return( mime_data[i].mime_type );
            }
      }
      const char *mime_type = of->mime_type;
      if ( !mime_type )
      {
            mime_type = "video/mpeg";
            Warning(( "Unable to determine mime type for '%s' format, using '%s' as default", format, mime_type ));
      }

      return( mime_type );
}

void VideoStream::OpenStream()
{
      /* now that all the parameters are set, we can open the 
         video codecs and allocate the necessary encode buffers */
      if ( ost )
      {
#if ZM_FFMPEG_CVS
            AVCodecContext *c = ost->codec;
#else
            AVCodecContext *c = &ost->codec;
#endif

            /* find the video encoder */
            AVCodec *codec = avcodec_find_encoder(c->codec_id);
            if ( !codec )
            {
                  Fatal(( "codec not found" ));
            }

            /* open the codec */
            if ( avcodec_open(c, codec) < 0 )
            {
                  Fatal(( "Could not open codec" ));
            }

            /* allocate the encoded raw picture */
            opicture = avcodec_alloc_frame();
            if ( !opicture )
            {
                  Fatal(( "Could not allocate opicture" ));
            }
            int size = avpicture_get_size( c->pix_fmt, c->width, c->height);
            uint8_t *opicture_buf = (uint8_t *)malloc(size);
            if ( !opicture_buf )
            {
                  av_free(opicture);
                  Fatal(( "Could not allocate opicture" ));
            }
            avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt, c->width, c->height );

            /* if the output format is not RGB24, then a temporary RGB24
               picture is needed too. It is then converted to the required
               output format */
            tmp_opicture = NULL;
            if ( c->pix_fmt != pf )
            {
                  tmp_opicture = avcodec_alloc_frame();
                  if ( !tmp_opicture )
                  {
                        Fatal(( "Could not allocate temporary opicture" ));
                  }
                  int size = avpicture_get_size( pf, c->width, c->height);
                  uint8_t *tmp_opicture_buf = (uint8_t *)malloc(size);
                  if (!tmp_opicture_buf)
                  {
                        av_free( tmp_opicture );
                        Fatal(( "Could not allocate temporary opicture" ));
                  }
                  avpicture_fill( (AVPicture *)tmp_opicture, tmp_opicture_buf, pf, c->width, c->height );
            }
      }

      /* open the output file, if needed */
      if ( !(of->flags & AVFMT_NOFILE) )
      {
            if ( url_fopen(&ofc->pb, filename, URL_WRONLY) < 0 )
            {
                  Fatal(( "Could not open '%s'", filename ));
            }
      }

      video_outbuf = NULL;
      if ( !(ofc->oformat->flags & AVFMT_RAWPICTURE) )
      {
            /* allocate output buffer */
            /* XXX: API change will be done */
            video_outbuf_size = 200000;
            video_outbuf = (uint8_t *)malloc(video_outbuf_size);
      }

      /* write the stream header, if any */
      av_write_header(ofc);
}

VideoStream::VideoStream( const char *filename, const char *format, int bitrate, int frame_rate, int colours, int width, int height )
{
      if ( !initialised )
      {
            Initialise();
      }

      SetupFormat( filename, format );
      SetupCodec( colours, width, height, bitrate, frame_rate );
      SetParameters();
}

VideoStream::~VideoStream()
{
      /* close each codec */
      if (ost)
      {
#if ZM_FFMPEG_CVS
            avcodec_close(ost->codec);
#else
            avcodec_close(&ost->codec);
#endif
            av_free(opicture->data[0]);
            av_free(opicture);
            if (tmp_opicture)
            {
                  av_free(tmp_opicture->data[0]);
                  av_free(tmp_opicture);
            }
            av_free(video_outbuf);
      }

      /* write the trailer, if any */
      av_write_trailer(ofc);
      
      /* free the streams */
      for( int i = 0; i < ofc->nb_streams; i++)
      {
            av_freep(&ofc->streams[i]);
      }

      if (!(of->flags & AVFMT_NOFILE))
      {
            /* close the output file */
            url_fclose(&ofc->pb);
      }

      /* free the stream */
      av_free(ofc);
}

double VideoStream::EncodeFrame( uint8_t *buffer, int buffer_size, bool add_timestamp, unsigned int timestamp )
{
      double pts = 0.0;

      if (ost)
      {
#if ZM_FFMPEG_048
            pts = (double)ost->pts.val * ofc->pts_num / ofc->pts_den;
#else
            pts = (double)ost->pts.val * ost->time_base.num / ost->time_base.den;
#endif
      }

#if ZM_FFMPEG_CVS
      AVCodecContext *c = ost->codec;
#else
      AVCodecContext *c = &ost->codec;
#endif
      if (c->pix_fmt != pf)
      {
            memcpy( tmp_opicture->data[0], buffer, buffer_size );
            img_convert((AVPicture *)opicture, c->pix_fmt, 
                              (AVPicture *)tmp_opicture, pf,
                              c->width, c->height);
      }
      else
      {
            memcpy( opicture->data[0], buffer, buffer_size );
      }
      AVFrame *opicture_ptr = opicture;

      int ret = 0;
      if (ofc->oformat->flags & AVFMT_RAWPICTURE)
      {
#if ZM_FFMPEG_048
            ret = av_write_frame(ofc, ost->index, (uint8_t *)opicture_ptr, sizeof(AVPicture));
#else
            AVPacket pkt;
            av_init_packet(&pkt);

            pkt.flags |= PKT_FLAG_KEY;
            pkt.stream_index = ost->index;
            pkt.data = (uint8_t *)opicture_ptr;
            pkt.size = sizeof(AVPicture);

            ret = av_write_frame(ofc, &pkt);
#endif
      }
      else
      {
            if ( add_timestamp )
                  ost->pts.val = timestamp;
            int out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, opicture_ptr);
            if (out_size > 0)
            {
#if ZM_FFMPEG_048
                  ret = av_write_frame(ofc, ost->index, video_outbuf, out_size);
#else
                  AVPacket pkt;
                  av_init_packet(&pkt);

#if ZM_FFMPEG_049
                  pkt.pts = c->coded_frame->pts;
#else
                  pkt.pts= av_rescale_q( c->coded_frame->pts, c->time_base, ost->time_base );
#endif
                  if(c->coded_frame->key_frame)
                        pkt.flags |= PKT_FLAG_KEY;
                  pkt.stream_index = ost->index;
                  pkt.data = video_outbuf;
                  pkt.size = out_size;

                  ret = av_write_frame(ofc, &pkt);
#endif
            }
      }
      if (ret != 0)
      {
            Fatal(( "Error while writing video frame" ));
      }
      return( pts );
}

#endif // HAVE_LIBAVCODEC

Generated by  Doxygen 1.6.0   Back to index