/// close video output stream and free associated memory void CvVideoWriter_FFMPEG::close() { unsigned i; // nothing to do if already released if ( !picture ) return; /* no more frame to compress. The codec has a latency of a few frames if using B frames, so we get the last frames by passing the same picture again */ // TODO -- do we need to account for latency here? /* write the trailer, if any */ av_write_trailer(oc); // free pictures #if LIBAVFORMAT_BUILD > 4628 if( video_st->codec->pix_fmt != input_pix_fmt){ #else if( video_st->codec.pix_fmt != input_pix_fmt){ #endif cvFree(&(picture->data[0])); } av_free(picture); if (input_picture) { av_free(input_picture); } /* close codec */ #if LIBAVFORMAT_BUILD > 4628 avcodec_close(video_st->codec); #else avcodec_close(&(video_st->codec)); #endif av_free(outbuf); /* free the streams */ for(i = 0; i < oc->nb_streams; i++) { av_freep(&oc->streams[i]->codec); av_freep(&oc->streams[i]); } if (!(fmt->flags & AVFMT_NOFILE)) { /* close the output file */ #if LIBAVCODEC_VERSION_INT >= ((51<<16)+(49<<8)+0) url_fclose(oc->pb); #else url_fclose(&oc->pb); #endif } /* free the stream */ av_free(oc); cvReleaseImage( &temp_image ); init(); } /// Create a video writer object that uses FFMPEG bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, double fps, CvSize frameSize, bool is_color ) { CV_FUNCNAME("CvVideoWriter_FFMPEG::open"); CodecID codec_id = CODEC_ID_NONE; int err, codec_pix_fmt, bitrate_scale=64; __BEGIN__; close(); // check arguments assert (filename); assert (fps > 0); assert (frameSize.width > 0 && frameSize.height > 0); // tell FFMPEG to register codecs av_register_all (); /* auto detect the output format from the name and fourcc code. */ fmt = guess_format(NULL, filename, NULL); if (!fmt) { CV_ERROR( CV_StsUnsupportedFormat, "FFMPEG does not recognize the given file extension"); } /* determine optimal pixel format */ if (is_color) { input_pix_fmt = PIX_FMT_BGR24; } else { input_pix_fmt = PIX_FMT_GRAY8; } // alloc memory for context oc = av_alloc_format_context(); assert (oc); /* set file name */ oc->oformat = fmt; snprintf(oc->filename, sizeof(oc->filename), "%s", filename); /* set some options */ oc->max_delay = (int)(0.7*AV_TIME_BASE); /* This reduces buffer underrun warnings with MPEG */ /* Lookup codec_id for given fourcc */ #if LIBAVCODEC_VERSION_INT<((51<<16)+(49<<8)+0) if( (codec_id = codec_get_bmp_id( fourcc )) == CODEC_ID_NONE ){ CV_ERROR( CV_StsUnsupportedFormat, "FFMPEG could not find a codec matching the given FOURCC code. Use fourcc=CV_FOURCC_DEFAULT for auto selection." ); } #else { const struct AVCodecTag * tags[] = { codec_bmp_tags, NULL}; if( (codec_id = av_codec_get_id(tags, fourcc)) == CODEC_ID_NONE ){ CV_ERROR( CV_StsUnsupportedFormat, "FFMPEG could not find a codec matching the given FOURCC code. Use fourcc=CV_FOURCC_DEFAULT for auto selection." ); } } #endif // set a few optimal pixel formats for lossless codecs of interest.. switch (codec_id) { #if LIBAVCODEC_VERSION_INT>((50<<16)+(1<<8)+0) case CODEC_ID_JPEGLS: // BGR24 or GRAY8 depending on is_color... codec_pix_fmt = input_pix_fmt; break; #endif case CODEC_ID_HUFFYUV: codec_pix_fmt = PIX_FMT_YUV422P; break; case CODEC_ID_MJPEG: case CODEC_ID_LJPEG: codec_pix_fmt = PIX_FMT_YUVJ420P; bitrate_scale = 128; break; case CODEC_ID_RAWVIDEO: default: // good for lossy formats, MPEG, etc. codec_pix_fmt = PIX_FMT_YUV420P; break; } // TODO -- safe to ignore output audio stream? video_st = icv_add_video_stream_FFMPEG(oc, codec_id, frameSize.width, frameSize.height, frameSize.width*frameSize.height*bitrate_scale, fps, codec_pix_fmt); /* set the output parameters (must be done even if no parameters). */ if (av_set_parameters(oc, NULL) < 0) { CV_ERROR(CV_StsBadArg, "Invalid output format parameters"); } dump_format(oc, 0, filename, 1); /* now that all the parameters are set, we can open the audio and video codecs and allocate the necessary encode buffers */ if (!video_st){ CV_ERROR(CV_StsBadArg, "Couldn't open video stream"); } AVCodec *codec; AVCodecContext *c; #if LIBAVFORMAT_BUILD > 4628 c = (video_st->codec); #else c = &(video_st->codec); #endif c->codec_tag = fourcc; /* find the video encoder */ codec = avcodec_find_encoder(c->codec_id); if (!codec) { CV_ERROR(CV_StsBadArg, "codec not found"); } /* open the codec */ if ( (err=avcodec_open(c, codec)) < 0) { char errtext[256]; sprintf(errtext, "Could not open codec '%s': %s", codec->name, icvFFMPEGErrStr(err)); CV_ERROR(CV_StsBadArg, errtext); } outbuf = NULL; if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { /* allocate output buffer */ /* assume we will never get codec output with more than 4 bytes per pixel... */ outbuf_size = frameSize.width*frameSize.height*4; outbuf = (uint8_t *) av_malloc(outbuf_size); } bool need_color_convert; need_color_convert = (c->pix_fmt != input_pix_fmt); /* allocate the encoded raw picture */ picture = icv_alloc_picture_FFMPEG(c->pix_fmt, c->width, c->height, need_color_convert); if (!picture) { CV_ERROR(CV_StsNoMem, "Could not allocate picture"); } /* if the output format is not our input format, then a temporary picture of the input format is needed too. It is then converted to the required output format */ input_picture = NULL; if ( need_color_convert ) { input_picture = icv_alloc_picture_FFMPEG(input_pix_fmt, c->width, c->height, false); if (!input_picture) { CV_ERROR(CV_StsNoMem, "Could not allocate picture"); } } /* open the output file, if needed */ if (!(fmt->flags & AVFMT_NOFILE)) { if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) { CV_ERROR(CV_StsBadArg, "Couldn't open output file for writing"); } } /* write the stream header, if any */ av_write_header( oc ); __END__; return true; }
/// Create a video writer object that uses FFMPEG inline bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, double fps, int width, int height, bool is_color ) { icvInitFFMPEG_internal(); CodecID codec_id = CODEC_ID_NONE; int err, codec_pix_fmt; double bitrate_scale = 1; close(); // check arguments if( !filename ) return false; if(fps <= 0) return false; // we allow frames of odd width or height, but in this case we truncate // the rightmost column/the bottom row. Probably, this should be handled more elegantly, // but some internal functions inside FFMPEG swscale require even width/height. width &= -2; height &= -2; if( width <= 0 || height <= 0 ) return false; /* auto detect the output format from the name and fourcc code. */ #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 2, 0) fmt = av_guess_format(NULL, filename, NULL); #else fmt = guess_format(NULL, filename, NULL); #endif if (!fmt) return false; /* determine optimal pixel format */ if (is_color) { input_pix_fmt = PIX_FMT_BGR24; } else { input_pix_fmt = PIX_FMT_GRAY8; } /* Lookup codec_id for given fourcc */ #if LIBAVCODEC_VERSION_INT<((51<<16)+(49<<8)+0) if( (codec_id = codec_get_bmp_id( fourcc )) == CODEC_ID_NONE ) return false; #else const struct AVCodecTag * tags[] = { codec_bmp_tags, NULL}; if( (codec_id = av_codec_get_id(tags, fourcc)) == CODEC_ID_NONE ) return false; #endif // alloc memory for context #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 2, 0) oc = avformat_alloc_context(); #else oc = av_alloc_format_context(); #endif assert (oc); /* set file name */ oc->oformat = fmt; _snprintf(oc->filename, sizeof(oc->filename), "%s", filename); /* set some options */ oc->max_delay = (int)(0.7*AV_TIME_BASE); /* This reduces buffer underrun warnings with MPEG */ // set a few optimal pixel formats for lossless codecs of interest.. switch (codec_id) { #if LIBAVCODEC_VERSION_INT>((50<<16)+(1<<8)+0) case CODEC_ID_JPEGLS: // BGR24 or GRAY8 depending on is_color... codec_pix_fmt = input_pix_fmt; break; #endif case CODEC_ID_HUFFYUV: codec_pix_fmt = PIX_FMT_YUV422P; break; case CODEC_ID_MJPEG: case CODEC_ID_LJPEG: codec_pix_fmt = PIX_FMT_YUVJ420P; bitrate_scale = 3; break; case CODEC_ID_RAWVIDEO: codec_pix_fmt = input_pix_fmt == PIX_FMT_GRAY8 || input_pix_fmt == PIX_FMT_GRAY16LE || input_pix_fmt == PIX_FMT_GRAY16BE ? input_pix_fmt : PIX_FMT_YUV420P; break; default: // good for lossy formats, MPEG, etc. codec_pix_fmt = PIX_FMT_YUV420P; break; } double bitrate = MIN(bitrate_scale*fps*width*height, (double)INT_MAX/2); // TODO -- safe to ignore output audio stream? video_st = icv_add_video_stream_FFMPEG(oc, codec_id, width, height, (int)(bitrate + 0.5), fps, codec_pix_fmt); /* set the output parameters (must be done even if no parameters). */ #if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(53, 2, 0) if (av_set_parameters(oc, NULL) < 0) { return false; } #endif #if 0 #if FF_API_DUMP_FORMAT dump_format(oc, 0, filename, 1); #else av_dump_format(oc, 0, filename, 1); #endif #endif /* now that all the parameters are set, we can open the audio and video codecs and allocate the necessary encode buffers */ if (!video_st){ return false; } AVCodec *codec; AVCodecContext *c; #if LIBAVFORMAT_BUILD > 4628 c = (video_st->codec); #else c = &(video_st->codec); #endif c->codec_tag = fourcc; /* find the video encoder */ codec = avcodec_find_encoder(c->codec_id); if (!codec) { fprintf(stderr, "Could not find encoder for codec id %d: %s", c->codec_id, icvFFMPEGErrStr( #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 2, 0) AVERROR_ENCODER_NOT_FOUND #else -1 #endif )); return false; } s64 lbit_rate = (s64)c->bit_rate; lbit_rate += (bitrate / 2); lbit_rate = std::min(lbit_rate, (s64)INT_MAX); c->bit_rate_tolerance = (int)lbit_rate; c->bit_rate = (int)lbit_rate; /* open the codec */ if ((err= #if LIBAVCODEC_VERSION_INT >= ((53<<16)+(8<<8)+0) avcodec_open2(c, codec, NULL) #else avcodec_open(c, codec) #endif ) < 0) { fprintf(stderr, "Could not open codec '%s': %s", codec->name, icvFFMPEGErrStr(err)); return false; } outbuf = NULL; if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { /* allocate output buffer */ /* assume we will never get codec output with more than 4 bytes per pixel... */ outbuf_size = width*height*4; outbuf = (uint8_t *) av_malloc(outbuf_size); } bool need_color_convert; need_color_convert = (c->pix_fmt != input_pix_fmt); /* allocate the encoded raw picture */ picture = icv_alloc_picture_FFMPEG(c->pix_fmt, c->width, c->height, need_color_convert); if (!picture) { return false; } /* if the output format is not our input format, then a temporary picture of the input format is needed too. It is then converted to the required output format */ input_picture = NULL; if ( need_color_convert ) { input_picture = icv_alloc_picture_FFMPEG(input_pix_fmt, c->width, c->height, false); if (!input_picture) { return false; } } /* open the output file, if needed */ if (!(fmt->flags & AVFMT_NOFILE)) { #if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(53, 2, 0) if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) #else if (avio_open(&oc->pb, filename, AVIO_FLAG_WRITE) < 0) #endif { return false; } } #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0) /* write the stream header, if any */ err=avformat_write_header(oc, NULL); #else err=av_write_header( oc ); #endif if(err < 0) { close(); remove(filename); return false; } frame_width = width; frame_height = height; ok = true; return true; }