tomwalters@116: // Copyright 2007, Thomas Walters tomwalters@116: // tomwalters@116: // AIM-C: A C++ implementation of the Auditory Image Model tomwalters@116: // http://www.acousticscale.org/AIMC tomwalters@116: // tomwalters@116: // Licensed under the Apache License, Version 2.0 (the "License"); tomwalters@116: // you may not use this file except in compliance with the License. tomwalters@116: // You may obtain a copy of the License at tomwalters@116: // tomwalters@116: // http://www.apache.org/licenses/LICENSE-2.0 tomwalters@116: // tomwalters@116: // Unless required by applicable law or agreed to in writing, software tomwalters@116: // distributed under the License is distributed on an "AS IS" BASIS, tomwalters@116: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. tomwalters@116: // See the License for the specific language governing permissions and tomwalters@116: // limitations under the License. tomwalters@116: tomwalters@116: /*! tomwalters@116: * \file tomwalters@116: * \brief Output device for output direct to a movie via local calls to libavcodec tomwalters@116: * tomwalters@116: * \author Tom Walters tomwalters@116: * \date created 2007/10/05 tomwalters@116: * \version \$Id: $ tomwalters@116: */ tomwalters@116: tomwalters@116: #include "Support/Common.h" tomwalters@116: tomwalters@116: #include tomwalters@116: #include tomwalters@116: #include tomwalters@116: #include tomwalters@116: tom@119: #include "Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovieDirect.h" tom@119: tom@119: namespace aimc { tomwalters@116: tomwalters@116: GraphicsOutputDeviceMovieDirect::GraphicsOutputDeviceMovieDirect(Parameters *params) tomwalters@117: : GraphicsOutputDeviceMovie(params) { tomwalters@117: m_sMovieFile[0] = '\0'; tomwalters@117: m_sSoundFile[0] = '\0'; tomwalters@116: } tomwalters@116: tomwalters@116: bool GraphicsOutputDeviceMovieDirect::Initialize(const char *sSoundFile, tomwalters@116: const char *sMovieFile) { tomwalters@117: // We want pnm for direct movie conversion as the data format is nice and simple tomwalters@117: //! \bug This may change the user preference in GUI, hmm what to do? See TODO.txt tomwalters@117: //m_pParam->SetString("output.img.format", "pnm"); tomwalters@116: tomwalters@117: // Initialise GraphicsOutputDevicePlotutils for memory buffer use tomwalters@117: if(!GraphicsOutputDeviceCairo::Initialize()) tomwalters@116: return false; tomwalters@116: tomwalters@116: int width = m_pParam->GetUInt("output.img.width"); tomwalters@117: int height = m_pParam->GetUInt("output.img.height"); tomwalters@117: //float framerate = 1000.0f/m_pParam->GetFloat("output.frameperiod"); tomwalters@117: float framerate=1000.0f/20.0f; tomwalters@116: tomwalters@116: m_pOutputMovie = new LibavformatWriter; tomwalters@117: m_pOutputMovie->Init(sMovieFile, width, height, framerate); tomwalters@116: tomwalters@117: return true; tomwalters@116: } tomwalters@116: tomwalters@116: void GraphicsOutputDeviceMovieDirect::Stop() { tomwalters@117: // Make sure Plotutils is really done writing. tomwalters@117: GraphicsOutputDeviceCairo::Stop(); tomwalters@117: m_pOutputMovie->End(); tomwalters@117: delete m_pOutputMovie; tomwalters@116: tomwalters@116: } tomwalters@116: tomwalters@116: void GraphicsOutputDeviceMovieDirect::gRelease() { tomwalters@116: // write the buffer tomwalters@116: unsigned char *buf = GraphicsOutputDeviceCairo::GetBuffer(); tomwalters@116: tomwalters@116: if (buf != NULL) tomwalters@116: m_pOutputMovie->WriteFrame(buf); tomwalters@116: tomwalters@116: GraphicsOutputDeviceCairo::gRelease(); tomwalters@116: } tomwalters@116: tomwalters@116: tomwalters@116: // Everything below here is hacked from the Libavformat API example: tomwalters@116: /* tomwalters@116: * Libavformat API example: Output a media file in any supported tomwalters@116: * libavformat format. The default codecs are used. tomwalters@116: * tomwalters@116: * Copyright (c) 2003 Fabrice Bellard tomwalters@116: * tomwalters@116: * Permission is hereby granted, free of charge, to any person obtaining a copy tomwalters@116: * of this software and associated documentation files (the "Software"), to deal tomwalters@116: * in the Software without restriction, including without limitation the rights tomwalters@116: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell tomwalters@116: * copies of the Software, and to permit persons to whom the Software is tomwalters@116: * furnished to do so, subject to the following conditions: tomwalters@116: * tomwalters@116: * The above copyright notice and this permission notice shall be included in tomwalters@116: * all copies or substantial portions of the Software. tomwalters@116: * tomwalters@116: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR tomwalters@116: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, tomwalters@116: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL tomwalters@116: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER tomwalters@116: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, tomwalters@116: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN tomwalters@116: * THE SOFTWARE. tomwalters@116: */ tomwalters@116: tomwalters@116: tomwalters@116: LibavformatWriter::LibavformatWriter() { tomwalters@116: sws_flags = SWS_BICUBIC; tomwalters@116: pixfmt= PIX_FMT_RGB24; tomwalters@116: fmt = NULL; tomwalters@116: } tomwalters@116: tomwalters@116: bool LibavformatWriter::Init(const char *sMovieFile, tomwalters@116: int width, tomwalters@116: int height, tomwalters@116: float framerate) { tomwalters@116: /* initialize libavcodec, and register all codecs and formats */ tomwalters@116: av_register_all(); tomwalters@116: tomwalters@116: /* auto detect the output format from the name. default is mpeg. */ tomwalters@116: fmt = guess_format(NULL, sMovieFile, NULL); tomwalters@116: if (!fmt) { tomwalters@116: printf("Could not deduce output format from file extension: using MPEG.\n"); tomwalters@116: fmt = guess_format("mpeg", NULL, NULL); tomwalters@116: } tomwalters@116: if (!fmt) { tomwalters@116: fprintf(stderr, "Could not find suitable output format\n"); tomwalters@116: return false; tomwalters@116: } tomwalters@116: tomwalters@116: /* allocate the output media context */ tomwalters@116: oc = av_alloc_format_context(); tomwalters@116: if (!oc) { tomwalters@116: fprintf(stderr, "Memory error\n"); tomwalters@116: return false; tomwalters@116: } tomwalters@116: oc->oformat = fmt; tomwalters@116: snprintf(oc->filename, sizeof(oc->filename), "%s", sMovieFile); tomwalters@116: tomwalters@116: /* add the audio and video streams using the default format codecs tomwalters@116: and initialize the codecs */ tomwalters@116: video_st = NULL; tomwalters@116: fmt->video_codec=CODEC_ID_PNG; tomwalters@116: if (fmt->video_codec != CODEC_ID_NONE) { tomwalters@116: video_st = add_video_stream(oc, fmt->video_codec, width, height, framerate); tomwalters@116: } tomwalters@116: tomwalters@116: /* set the output parameters (must be done even if no tomwalters@116: parameters). */ tomwalters@116: if (av_set_parameters(oc, NULL) < 0) { tomwalters@116: fprintf(stderr, "Invalid output format parameters\n"); tomwalters@116: return false; tomwalters@116: } tomwalters@116: tomwalters@116: dump_format(oc, 0, sMovieFile, 1); tomwalters@116: tomwalters@116: /* now that all the parameters are set, we can open the audio and tomwalters@116: video codecs and allocate the necessary encode buffers */ tomwalters@116: if (video_st) { tomwalters@116: open_video(oc, video_st); tomwalters@116: } tomwalters@116: tomwalters@116: /* open the output file, if needed */ tomwalters@116: if (!(fmt->flags & AVFMT_NOFILE)) { tomwalters@116: if (url_fopen(&oc->pb, sMovieFile, URL_WRONLY) < 0) { tomwalters@116: fprintf(stderr, "Could not open '%s'\n", sMovieFile); tomwalters@116: return false; tomwalters@116: } tomwalters@116: } tomwalters@116: tomwalters@116: /* write the stream header, if any */ tomwalters@116: av_write_header(oc); tomwalters@116: return true; tomwalters@116: } tomwalters@116: tomwalters@116: tomwalters@116: /**************************************************************/ tomwalters@116: /* video output */ tomwalters@116: tomwalters@116: /* add a video output stream */ tomwalters@116: AVStream* LibavformatWriter::add_video_stream(AVFormatContext *oc, tomwalters@116: CodecID codec_id, tomwalters@116: int width, tomwalters@116: int height, tomwalters@116: float framerate) { tomwalters@116: AVCodecContext *c; tomwalters@116: AVStream *st; tomwalters@116: tomwalters@116: st = av_new_stream(oc, 0); tomwalters@116: if (!st) { tomwalters@116: fprintf(stderr, "Could not alloc stream\n"); tomwalters@116: return NULL; tomwalters@116: } tomwalters@116: tomwalters@116: c = st->codec; tomwalters@116: c->codec_id = codec_id; tomwalters@116: c->codec_type = CODEC_TYPE_VIDEO; tomwalters@116: tomwalters@116: /* put sample parameters */ tomwalters@116: /* resolution must be a multiple of two */ tomwalters@116: c->width = width; tomwalters@116: c->height = height; tomwalters@116: /* time base: this is the fundamental unit of time (in seconds) in terms tomwalters@116: of which frame timestamps are represented. for fixed-fps content, tomwalters@116: timebase should be 1/framerate and timestamp increments should be tomwalters@116: identically 1. */ tomwalters@116: c->time_base.den = (int)framerate; tomwalters@116: c->time_base.num = 1; tomwalters@116: c->gop_size = 12; /* emit one intra frame every twelve frames at most */ tomwalters@116: c->pix_fmt = pixfmt; tomwalters@116: // some formats want stream headers to be separate tomwalters@116: if(!strcmp(oc->oformat->name, "mp4") tomwalters@116: || !strcmp(oc->oformat->name, "mov") tomwalters@116: || !strcmp(oc->oformat->name, "3gp")) { tomwalters@116: c->flags |= CODEC_FLAG_GLOBAL_HEADER; tomwalters@116: } tomwalters@116: return st; tomwalters@116: } tomwalters@116: tomwalters@116: AVFrame* LibavformatWriter::alloc_picture(int pix_fmt, tomwalters@116: int width, tomwalters@116: int height) { tomwalters@116: AVFrame *picture; tomwalters@116: uint8_t *picture_buf; tomwalters@116: int size; tomwalters@116: tomwalters@116: picture = avcodec_alloc_frame(); tomwalters@116: if (!picture) { tomwalters@116: return NULL; tomwalters@116: } tomwalters@116: size = avpicture_get_size(pix_fmt, width, height); tomwalters@116: picture_buf = (uint8_t*)av_malloc(size); tomwalters@116: if (!picture_buf) { tomwalters@116: av_free(picture); tomwalters@116: return NULL; tomwalters@116: } tomwalters@116: avpicture_fill((AVPicture *)picture, picture_buf, tomwalters@116: pix_fmt, width, height); tomwalters@116: return picture; tomwalters@116: } tomwalters@116: tomwalters@116: void LibavformatWriter::open_video(AVFormatContext *oc, AVStream *st) { tomwalters@116: AVCodec *codec; tomwalters@116: AVCodecContext *c; tomwalters@116: c = st->codec; tomwalters@116: tomwalters@116: /* find the video encoder */ tomwalters@116: codec = avcodec_find_encoder(c->codec_id); tomwalters@116: if (!codec) { tomwalters@116: fprintf(stderr, "codec not found\n"); tomwalters@116: exit(1); tomwalters@116: } tomwalters@116: tomwalters@116: /* open the codec */ tomwalters@116: if (avcodec_open(c, codec) < 0) { tomwalters@116: fprintf(stderr, "could not open codec\n"); tomwalters@116: exit(1); tomwalters@116: } tomwalters@116: tomwalters@116: video_outbuf = NULL; tomwalters@116: if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { tomwalters@116: /* allocate output buffer */ tomwalters@116: /* XXX: API change will be done */ tomwalters@116: /* buffers passed into lav* can be allocated any way you prefer, tomwalters@116: as long as they're aligned enough for the architecture, and tomwalters@116: they're freed appropriately (such as using av_free for buffers tomwalters@116: allocated with av_malloc) */ tomwalters@116: video_outbuf_size = 200000; tomwalters@116: video_outbuf = (uint8_t*)av_malloc(video_outbuf_size); tomwalters@116: } tomwalters@116: tomwalters@116: /* allocate the encoded raw picture */ tomwalters@116: picture = alloc_picture(c->pix_fmt, c->width, c->height); tomwalters@116: if (!picture) { tomwalters@116: fprintf(stderr, "Could not allocate picture\n"); tomwalters@116: exit(1); tomwalters@116: } tomwalters@116: tomwalters@116: /* if the output format is not RGB32, then a temporary RGB32 tomwalters@116: picture is needed too. It is then converted to the required tomwalters@116: output format */ tomwalters@116: tmp_picture = NULL; tomwalters@116: if (c->pix_fmt != PIX_FMT_RGB32) { tomwalters@116: tmp_picture = alloc_picture(PIX_FMT_RGB32, c->width, c->height); tomwalters@116: if (!tmp_picture) { tomwalters@116: fprintf(stderr, "Could not allocate temporary picture\n"); tomwalters@116: exit(1); tomwalters@116: } tomwalters@116: } tomwalters@116: } tomwalters@116: tomwalters@116: tomwalters@116: void LibavformatWriter::close_video(AVFormatContext *oc, tomwalters@116: AVStream *st) { tomwalters@116: avcodec_close(st->codec); tomwalters@116: av_free(picture->data[0]); tomwalters@116: av_free(picture); tomwalters@116: if (tmp_picture) { tomwalters@116: av_free(tmp_picture->data[0]); tomwalters@116: av_free(tmp_picture); tomwalters@116: } tomwalters@116: av_free(video_outbuf); tomwalters@116: } tomwalters@116: tomwalters@116: void LibavformatWriter::WriteFrame(unsigned char *pFrameBuffer) { tomwalters@116: /* compute current video time */ tomwalters@116: if (video_st) tomwalters@116: video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den; tomwalters@116: else tomwalters@116: video_pts = 0.0; tomwalters@116: tomwalters@116: int out_size, ret; tomwalters@116: AVCodecContext *c; tomwalters@116: static struct SwsContext *img_convert_ctx; tomwalters@116: c = video_st->codec; tomwalters@116: if (c->pix_fmt != PIX_FMT_RGB32) { tomwalters@116: /* as we only generate a RGB32 picture, we must convert it tomwalters@116: to the codec pixel format if needed */ tomwalters@116: if (img_convert_ctx == NULL) { tomwalters@116: img_convert_ctx = sws_getContext(c->width, tomwalters@116: c->height, tomwalters@116: PIX_FMT_RGB32, tomwalters@116: c->width, tomwalters@116: c->height, tomwalters@116: c->pix_fmt, tomwalters@116: sws_flags, tomwalters@116: NULL, tomwalters@116: NULL, tomwalters@116: NULL); tomwalters@116: if (img_convert_ctx == NULL) { tomwalters@116: fprintf(stderr, "Cannot initialize the conversion context\n"); tomwalters@116: exit(1); tomwalters@116: } tomwalters@116: } tomwalters@116: fill_image(tmp_picture, pFrameBuffer, c->width, c->height); tomwalters@116: sws_scale(img_convert_ctx, tmp_picture->data, tmp_picture->linesize, tomwalters@116: 0, c->height, picture->data, picture->linesize); tomwalters@116: } else { tomwalters@116: fill_image(picture, pFrameBuffer, c->width, c->height); tomwalters@116: } tomwalters@116: /* encode the image */ tomwalters@116: out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture); tomwalters@116: /* if zero size, it means the image was buffered */ tomwalters@116: if (out_size > 0) { tomwalters@116: AVPacket pkt; tomwalters@116: av_init_packet(&pkt); tomwalters@116: tomwalters@116: pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, video_st->time_base); tomwalters@116: if(c->coded_frame->key_frame) { tomwalters@116: pkt.flags |= PKT_FLAG_KEY; tomwalters@116: pkt.stream_index= video_st->index; tomwalters@116: pkt.data= video_outbuf; tomwalters@116: pkt.size= out_size; tomwalters@116: /* write the compressed frame in the media file */ tomwalters@116: ret = av_write_frame(oc, &pkt); tomwalters@116: } tomwalters@116: } else { tomwalters@116: ret = 0; tomwalters@116: } tomwalters@116: if (ret != 0) { tomwalters@116: fprintf(stderr, "Error while writing video frame\n"); tomwalters@116: exit(1); tomwalters@116: } tomwalters@116: } tomwalters@116: tomwalters@116: void LibavformatWriter::End() { tomwalters@116: /* close each codec */ tomwalters@116: if (video_st) { tomwalters@116: close_video(oc, video_st); tomwalters@116: } tomwalters@116: /* write the trailer, if any */ tomwalters@116: av_write_trailer(oc); tomwalters@116: tomwalters@116: /* free the streams */ tomwalters@116: for(i = 0; i < oc->nb_streams; i++) { tomwalters@116: av_freep(&oc->streams[i]->codec); tomwalters@116: av_freep(&oc->streams[i]); tomwalters@116: } tomwalters@116: tomwalters@116: if (!(fmt->flags & AVFMT_NOFILE)) { tomwalters@116: /* close the output file */ tomwalters@116: url_fclose(&oc->pb); tomwalters@116: } tomwalters@116: /* free the stream */ tomwalters@116: av_free(oc); tomwalters@116: } tomwalters@116: tomwalters@116: void LibavformatWriter::fill_image(AVFrame *pict, tomwalters@116: unsigned char *pFrameBuffer, tomwalters@116: int width, tomwalters@116: int height) { tomwalters@117: memcpy((void*)&(pict->data[0][0]), (void*)pFrameBuffer, width*height*4); tomwalters@116: } tom@119: } // namespace aimc