X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Ftrunk%2Fsrc%2Fmodules%2Fmod_libavcodec%2Ftrgt_av.cpp;fp=synfig-core%2Ftrunk%2Fsrc%2Fmodules%2Fmod_libavcodec%2Ftrgt_av.cpp;h=0000000000000000000000000000000000000000;hb=a095981e18cc37a8ecc7cd237cc22b9c10329264;hp=5ad733aba2220061eee63975a0e280c3115e2217;hpb=9459638ad6797b8139f1e9f0715c96076dbf0890;p=synfig.git diff --git a/synfig-core/trunk/src/modules/mod_libavcodec/trgt_av.cpp b/synfig-core/trunk/src/modules/mod_libavcodec/trgt_av.cpp deleted file mode 100644 index 5ad733a..0000000 --- a/synfig-core/trunk/src/modules/mod_libavcodec/trgt_av.cpp +++ /dev/null @@ -1,983 +0,0 @@ -/* === S Y N F I G ========================================================= */ -/*! \file trgt_av.cpp -** \brief \writeme -** -** $Id$ -** -** \legal -** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley -** Copyright (c) 2008 Paul Wise -** Copyright (c) 2008 Gerco Ballintijn -** -** This package 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 package 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. -** \endlegal -*/ -/* ========================================================================= */ - -/* === H E A D E R S ======================================================= */ - -#define SYNFIG_NO_ANGLE - -#ifdef USING_PCH -# include "pch.h" -#else -#ifdef HAVE_CONFIG_H -# include -#endif - -#include "trgt_av.h" - -extern "C" -{ - -/* - ffmpeg library headers have historically had multiple locations. - We should check all of the locations to be more portable. -*/ - -#ifdef HAVE_LIBAVFORMAT_AVFORMAT_H -# include -#elif defined(HAVE_AVFORMAT_H) -# include -#elif defined(HAVE_FFMPEG_AVFORMAT_H) -# include -#endif - -#ifdef WITH_LIBSWSCALE -#ifdef HAVE_LIBSWSCALE_SWSCALE_H -# include -#elif defined(HAVE_SWSCALE_H) -# include -#elif defined(HAVE_FFMPEG_SWSCALE_H) -# include -#endif -#endif - -} - -#include - -#include -#include -#include -#endif - -#ifdef WIN32 -#define snprintf _snprintf -#endif - -/* === U S I N G =========================================================== */ - -using namespace synfig; -using namespace std; -using namespace etl; - -/* === I N F O ============================================================= */ - -SYNFIG_TARGET_INIT(Target_LibAVCodec); -SYNFIG_TARGET_SET_NAME(Target_LibAVCodec,"libav"); -SYNFIG_TARGET_SET_EXT(Target_LibAVCodec,"avi"); -SYNFIG_TARGET_SET_VERSION(Target_LibAVCodec,"0.1"); -SYNFIG_TARGET_SET_CVS_ID(Target_LibAVCodec,"$Id$"); - -/* === C L A S S E S & S T R U C T S ======================================= */ - -bool Target_LibAVCodec::registered = false; - -//for now compilation -//float STREAM_DURATION = 5.0f; - -struct VideoInfo -{ - int w,h; - int fps; - - int bitrate; -}; - -struct AudioInfo -{ - int samplerate; //in HZ - int samplesize; //in bytes -}; - -AVFrame *alloc_picture(int pix_fmt, int width, int height) -{ - AVFrame *picture; - uint8_t *picture_buf; - int size; - - picture = avcodec_alloc_frame(); - if (!picture) - return NULL; - size = avpicture_get_size(pix_fmt, width, height); - picture_buf = (uint8_t *)malloc(size); - if (!picture_buf) { - av_free(picture); - return NULL; - } - avpicture_fill((AVPicture *)picture, picture_buf, - pix_fmt, width, height); - return picture; -} - -void free_picture(AVFrame *pic) -{ - av_free(pic->data[0]); - av_free(pic); -} - -//the frame must be RGB24 -static void convert_surface_frame(AVFrame *pic, const Surface &s, const Gamma &gamma) -{ - unsigned int j; - Surface::const_pen p = s.begin(); - unsigned int w,h; - Color c; - - uint8_t *ptr; - int stride; - - w = s.size().x; - h = s.size().y; - - ptr = pic->data[0]; - stride = pic->linesize[0]; - - for(j = 0; j < h; j++, p.inc_y(), ptr += stride) - { - uint8_t *tptr = ptr; - - //use convert_color_format instead... - #if 0 - const int channels = 3; - - for(int i = 0; i < w; i++, p.inc_x(), tptr += channels) - { - c = p.get_value(); - - Color::value_type r = c.get_r(); - Color::value_type g = c.get_g(); - Color::value_type b = c.get_b(); - Color::value_type a = c.get_a(); - - //premultiply alpha - r *= a; - g *= a; - b *= a; - - //essentially treats it as if it has a background color of black - - //we must also clamp the rgb values [0,1] - r = min(1.0f,r); - g = min(1.0f,g); - b = min(1.0f,b); - - r = max(0.0f,r); - g = max(0.0f,g); - b = max(0.0f,b); - - //now scale to range of char [0,255] - tptr[0] = (int)(r*255); - tptr[1] = (int)(g*255); - tptr[2] = (int)(b*255); - } - - p.dec_x(w); - #else - - convert_color_format((unsigned char *)tptr,&p.get_value(),w,PF_RGB,gamma); - - #endif - } -} - -//Audio Streamer (abstracts the open, write and close operations for audio streams) -#if 0 -class AudioEncoder -{ -public: - - void *samples; - - vectoraudiobuffer; - - int audio_input_frame_size; - - bool open(AVFormatContext *formatc, AVStream *stream) - { - AVCodecContext *context; - AVCodec *codec; - - context = &stream->codec; - - //find audio encoder - codec = avcodec_find_encoder(context->codec_id); - if(!codec) - { - synfig::warning("audio-open: could not find codec"); - return 0; - } - - //open the codec - if(avcodec_open(context, codec) < 0) - { - synfig::warning("audio-open: could not open codec"); - return 0; - } - - /* hardcoded example for generating samples array*/ - /* - t = 0; - tincr = 2 * M_PI * 110.0 / c->sample_rate; - tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate;*/ - - audiobuffer.resize(10000); - - /* ugly hack for PCM codecs (will be removed ASAP with new PCM - support to compute the input frame size in samples */ - if (context->frame_size <= 1) { - audio_input_frame_size = audiobuffer.size() / context->channels; - switch(stream->codec.codec_id) { - case CODEC_ID_PCM_S16LE: - case CODEC_ID_PCM_S16BE: - case CODEC_ID_PCM_U16LE: - case CODEC_ID_PCM_U16BE: - audio_input_frame_size >>= 1; - break; - default: - break; - } - } else { - audio_input_frame_size = context->frame_size; - } - - //hardcoded array - //samples = (int16_t *)malloc(audio_input_frame_size * 2 * c->channels); - - return true; - } - - bool write_frame(AVFormatContext *formatc, AVStream *stream, void *samples) - { - int size; - AVCodecContext *context; - - context = &stream->codec; - - //hardcoded in example - //must read in from somewhere... - //get_audio_frame(samples, audio_input_frame_size, c->channels); - - //encode the audio - const short int*samps=(const short int *)samples; //assuming it's set from somewhere right now - - size = avcodec_encode_audio(context, &audiobuffer[0], audiobuffer.size(), samps); - - //write the compressed audio to a file - if(av_write_frame(formatc, stream->index, &audiobuffer[0], size) != 0) - { - synfig::warning("audio-write_frame: unable to write the entire audio frame"); - return 0; - } - - return true; - } - - void close(AVFormatContext *formatc, AVStream *stream) - { - //we may also want to catch delayed frames from here (don't for now) - - if(stream) - { - avcodec_close(&stream->codec); - } - - //if(samples)av_free(samples); - audiobuffer.resize(0); - } -}; - -#endif - -class VideoEncoder -{ -public: - AVFrame *encodable; //for compression and output to a file (in compatible pixel format) - - vector videobuffer; - - bool startedencoding; - - //int stream_nb_frames; - - bool open(AVFormatContext *formatc, AVStream *stream) - { - if(!formatc || !stream) - { - synfig::warning("Attempt to open a video codec with a bad format or stream"); - return false; - } - - //codec and context - AVCodec *codec; - AVCodecContext *context; - - //get from inside stream - context = stream->codec; - - //search for desired codec (contained in the stream) - codec = avcodec_find_encoder(context->codec_id); - if(!codec) - { - synfig::warning("Open_video: could not find desired codec"); - return 0; - } - - //try to open the codec - if(avcodec_open(context, codec) < 0) - { - synfig::warning("open_video: could not open desired codec"); - return 0; - } - - videobuffer.resize(0); - if(!(formatc->oformat->flags & AVFMT_RAWPICTURE)) - { - //resize buffer to desired buffersize - videobuffer.resize(200000); //TODO: need to figure out a good size - } - - //allocate the base picture which will be used to encode - /*picture = alloc_picture(PIX_FMT_RGBA32, context->width, context->height); - if(!picture) - { - synfig::warning("open_video: could not allocate the picture to be encoded"); - return 0; - }*/ - - //if our output (rgb) needs to be translated to a different coordinate system, need a temporary picture in that color space - - /* Should use defaults of RGB - Possible formats: - PIX_FMT_RGB24 - PIX_FMT_BGR24 - PIX_FMT_RGBA32 //stored in cpu endianness (!!!!) - - (possibly translate directly to required coordinate systems later on... less error) - */ - encodable = NULL; - if(context->pix_fmt != PIX_FMT_RGB24) - { - encodable = alloc_picture(context->pix_fmt, context->width, context->height); - if(!encodable) - { - synfig::warning("open_video: could not allocate encodable picture"); - return 0; - } - } - - return true; - } - - //write a frame with the frame passed in - bool write_frame(AVFormatContext *formatc, AVStream *stream, AVFrame *pict) - { - if(!formatc || !stream) - { - synfig::warning("Attempt to open a video codec with a bad format or stream"); - return false; - } - - int size, - ret = 0; - AVCodecContext *context = stream->codec; - - /* - If pict is invalid (NULL), then we are done compressing frames and we are trying to get - the buffer cleared out (or if it's already in the right format) so no transform necessary - */ - if ( pict ) - { - startedencoding = true; - } - - - if ( pict && context->pix_fmt != PIX_FMT_RGB24 ) - { - //We're using RGBA at the moment, write custom conversion code later (get less accuracy errors) -#ifdef WITH_LIBSWSCALE - struct SwsContext* img_convert_ctx = - sws_getContext(context->width, context->height, PIX_FMT_RGB24, - context->width, context->height, context->pix_fmt, - SWS_BICUBIC, NULL, NULL, NULL); - - sws_scale(img_convert_ctx, pict->data, pict->linesize, - - 0, context->height, encodable->data, - encodable->linesize); - - sws_freeContext (img_convert_ctx); -#else - img_convert((AVPicture *)encodable, context->pix_fmt, - (AVPicture *)pict, PIX_FMT_RGB24, - context->width, context->height); -#endif - - pict = encodable; - } - - AVPacket pkt; - av_init_packet(&pkt); - pkt.stream_index = stream->index; - pkt.data = (uint8_t *)pict; - pkt.size = sizeof(AVPicture); - if( context->coded_frame ) - pkt.pts = context->coded_frame->pts; - if( context->coded_frame && context->coded_frame->key_frame) - pkt.flags |= PKT_FLAG_KEY; - - //kluge for raw picture format (they said they'd fix) - if (formatc->oformat->flags & AVFMT_RAWPICTURE) - { - ret = av_write_frame(formatc, &pkt); - } - else - { - //encode our given image - size = avcodec_encode_video(context, &videobuffer[0], videobuffer.size(), pict); - - //if greater than zero we've got stuff to write - if (size > 0) - { - av_init_packet(&pkt); - pkt.stream_index = stream->index; - pkt.data = &videobuffer[0]; - pkt.size = size; - if( context->coded_frame ) - pkt.pts = context->coded_frame->pts; - if( context->coded_frame && context->coded_frame->key_frame) - pkt.flags |= PKT_FLAG_KEY; - - ret = av_write_frame(formatc, &pkt); - - //error detect - possibly throw later... - if(ret < 0) - { - synfig::warning("write_frame: error while writing video frame"); - return false; - } - } - //if 0, it was buffered (if invalid picture we don't have ANY data left) - else - { - //if we're clearing the buffers and there was no stuff to be written, we're done (like in codec example) - if(pict == NULL) - { - return false; - startedencoding = false; - } - - //buffered picture - } - } - - return true; - } - - void close(AVFormatContext */*formatc*/, AVStream *stream) - { - if(stream) - avcodec_close(stream->codec); - - if (encodable) - { - free_picture(encodable); - encodable = 0; - } - - videobuffer.resize(0); - } -}; - -class Target_LibAVCodec::LibAVEncoder -{ -public: - - bool initialized; - - AVOutputFormat *format; //reference to global, do not delete - - AVFormatContext *formatc; - - AVStream *video_st; - //AVStream *audio_st; - - double video_pts; - //double audio_pts; - - VideoEncoder vid; - VideoInfo vInfo; - - /*AudioEncoder aud; - AudioInfo aInfo;*/ - - AVFrame *picture; //for encoding to RGB24 (perhaps RGBA later) - - int frame_count; - int num_frames; - - LibAVEncoder() - { - format = 0; - formatc = 0; - - //video settings - video_st = 0; - video_pts = 0; - - vid.encodable = 0; - //vid.stream_nb_frames = 2; //reasonable default - - initialized = false; - picture = 0; - - frame_count = 0; - num_frames = 0; - - //aud.samples = 0; - //audio_st = 0; - //audio_pts = 0; - } - - ~LibAVEncoder() - { - CleanUp(); - } - - bool Initialize(const char *filename, const char *typestring) - { - //guess if we have a type string, otherwise use filename - if (typestring) - { - //formatptr guess_format(type, filename, MIME type) - format = guess_format(typestring,NULL,NULL); - } - else - { - format = guess_format(NULL, filename, NULL); - } - - if(!format) - { - synfig::warning("Unable to Guess the output, defaulting to mpeg"); - format = guess_format("mpeg", NULL, NULL); - } - - if(!format) - { - synfig::warning("Unable to find output format"); - return 0; - } - - //allocate the output context - formatc = (AVFormatContext *)av_mallocz(sizeof(AVFormatContext)); - if(!formatc) - { - synfig::warning("Memory error\n"); - return 0; - } - //set the output format to the one we found - formatc->oformat = format; - - //print the output filename - snprintf(formatc->filename, sizeof(formatc->filename), "%s", filename); - - //initial - video_st = NULL; - //audio_st = NULL; - - //video stream - if(format->video_codec != CODEC_ID_NONE) - { - video_st = add_video_stream(format->video_codec,vInfo); - if(!video_st) - { - av_free(formatc); - } - } - - //audio stream - /*if(format->audio_codec != CODEC_ID_NONE) - { - audio_st = add_audio_stream(format->audio_codec,aInfo); - }*/ - - //set output parameters: required in ALL cases - - video_st->codec->time_base= (AVRational){1,vInfo.fps}; - video_st->codec->width = vInfo.w; - video_st->codec->height = vInfo.h; - video_st->codec->pix_fmt = PIX_FMT_YUV420P; - - //dump the formatting information as the file header - dump_format(formatc, 0, filename, 1); - - //open codecs and allocate buffers - if(video_st) - { - if(!vid.open(formatc, video_st)) - { - synfig::warning("Could not open video encoder"); - return 0; - } - } - /*if(audio_st) - { - if(!aud.open(formatc, audio_st)) - { - synfig::warning("Could not open audio encoder"); - return 0; - } - }*/ - - //open output file - if(!(format->flags & AVFMT_NOFILE)) - { - //use libav's file open function (what does it do differently????) - if(url_fopen(&formatc->pb, filename, URL_WRONLY) < 0) - { - synfig::warning("Unable to open file: %s", filename); - return 0; - } - } - - //allocate the picture to render to - //may have to retrieve the width, height from the codec... for resizing... - picture = alloc_picture(PIX_FMT_RGB24,vInfo.w,vInfo.h);//video_st->codec.width, video_st->codec.height); - if(!picture) - { - synfig::warning("Unable to allocate the temporary AVFrame surface"); - return 0; - } - - initialized = true; - //vInfo.w = video_st->codec.width; - //vInfo.h = video_st->codec.height; - - //write the stream header - av_write_header(formatc); - - return true; - } - - void CleanUp() - { - unsigned int i; - - if(picture) free_picture(picture); - - //do all the clean up file rendering - if(formatc && video_st) - { - //want to scan in delayed frames until no longer needed (TODO) - if(vid.startedencoding) while( vid.write_frame(formatc, video_st, 0) ); - - //may want to move this... probably to the end of the last frame... - av_write_trailer(formatc); - } - - //close codecs - if (video_st) - vid.close(formatc,video_st); - /*if (audio_st) - aud.close(formatc,audio_st);*/ - - /* write the trailer, if any */ - if(formatc) - { - /* free the streams */ - for(i = 0; i < formatc->nb_streams; i++) - { - av_freep(&formatc->streams[i]); - } - - if(!(format->flags & AVFMT_NOFILE)) - { - /* close the output file */ -#if LIBAVFORMAT_VERSION_INT >= (52<<16) - url_fclose(formatc->pb); -#else - url_fclose(&formatc->pb); -#endif - } - - /* free the stream */ - av_free(formatc); - } - - initialized = false; - - format = 0; - formatc = 0; - - //video settings - video_st = 0; - video_pts = 0; - - vid.encodable = 0; - //vid.stream_nb_frames = 2; //reasonable default - - initialized = false; - picture = 0; - } - - //create a video output stream - AVStream *add_video_stream(int codec_id, const VideoInfo &info) - { - AVCodecContext *context; - AVStream *st; - - st = av_new_stream(formatc, 0); - if(!st) - { - synfig::warning("video-add_stream: Unable to allocate stream"); - return 0; - } - - context = st->codec; - context->codec_id = (CodecID)codec_id; - context->codec_type = CODEC_TYPE_VIDEO; - - //PARAMETERS MUST BE PASSED IN SOMEHOW (ANOTHER FUNCTION PARAMETER???) - - /* resolution must be a multiple of two */ - context->width = info.w; - context->height = info.h; - - //have another way to input these - context->bit_rate = info.bitrate; //TODO: Make dependant on the quality - - /* frames per second */ - // FIXME: Port next two lines to recent libavcodec versions - //context->frame_rate = info.fps; - //context->frame_rate_base = 1; - - /* "High Quality" */ - context->mb_decision=FF_MB_DECISION_BITS; - - context->gop_size = info.fps/4; /* emit one intra frame every twelve frames at most */ - - //HACK: MPEG requires b frames be set... any better way to do this? - if (context->codec_id == CODEC_ID_MPEG1VIDEO || - context->codec_id == CODEC_ID_MPEG2VIDEO) - { - /* just for testing, we also add B frames */ - context->max_b_frames = 2; - } - - return st; - } - - // add an audio output stream - AVStream *add_audio_stream(int codec_id,const AudioInfo &/*aInfo*/) - { - AVCodecContext *context; - AVStream *stream; - - stream = av_new_stream(formatc, 1); - if(!stream) - { - synfig::warning("could not alloc stream"); - return 0; - } - - context = stream->codec; - context->codec_id = (CodecID)codec_id; - context->codec_type = CODEC_TYPE_AUDIO; - - /* put sample parameters */ - context->bit_rate = 64000; - context->sample_rate = 44100; - context->channels = 2; - - return stream; - } -}; - -/* === M E T H O D S ======================================================= */ - -Target_LibAVCodec::Target_LibAVCodec(const char *Filename): - filename(Filename) -{ - if(!registered) - { - registered = true; - av_register_all(); - } - set_remove_alpha(); - - data = new LibAVEncoder; -} - -Target_LibAVCodec::~Target_LibAVCodec() -{ - data->CleanUp(); -} - -bool -Target_LibAVCodec::set_rend_desc(RendDesc *given_desc) -{ - // This is where you can determine how you want stuff - // to be rendered! given_desc is the suggestion, and - // you need to modify it to suit the needs of the codec. - // ie: Making the pixel dimensions divisible by 8, etc... - - desc=*given_desc; - - //resize surface (round even) - int w = desc.get_w(); - int h = desc.get_h(); - Point tl = desc.get_tl(); - Point br = desc.get_br(); - Real pw = desc.get_pw(); - Real ph = desc.get_ph(); - - //resize to the size it should be... - //desc.set_subwindow(-offx/2,-offy/2, desc.get_w() - offx?(offx + 8):0, desc.get_h() - offy?(offy + 8):0); - - //if resolution is broken, change the size... or something - //budge to nearest pixel values - if(w&1) - { - w += 1; - tl[0] -= pw/2; - br[0] += pw/2; - } - - if(h&1) - { - h += 1; - tl[1] -= ph/2; - br[1] += ph/2; - } - - desc.set_w(w); - desc.set_h(h); - desc.set_tl(tl); - desc.set_br(br); - - data->vInfo.w = w; - data->vInfo.h = h; - - //may want to round frame rate - data->vInfo.fps = (int)floor(desc.get_frame_rate()+0.5); -#define MEGABYTES_PER_HOUR(x) (((x)*1024/3600*1024*8)/*/640*w/480*h*/) - data->vInfo.bitrate = MEGABYTES_PER_HOUR(400); - //data->vInfo.bitrate = 800000; //make customizable somehow - - desc.set_frame_rate(data->vInfo.fps); - - data->frame_count = desc.get_frame_start(); - data->num_frames = desc.get_frame_end()+1; //number of frames should be 1 greater than the last one - - surface.set_wh(data->vInfo.w,data->vInfo.h); - - return true; -} - -void -Target_LibAVCodec::end_frame() -{ - //AVStream *audio_st = data->audio_st; - AVStream *video_st = data->video_st; - - AVFormatContext *formatc = data->formatc; - - //double &audio_pts = data->audio_pts; - //double &video_pts = data->video_pts; - - //ignore audio for now - /*if (audio_st) - audio_pts = (double)audio_st->pts.val * formatc->pts_num / formatc->pts_den; - else - audio_pts = 0.0; - - if (video_st) - video_pts = (double)video_st->pts.val * formatc->pts_num / formatc->pts_den; - else - video_pts = 0.0;*/ - - //hardcoded crappiness - /*if ((!audio_st || audio_pts >= STREAM_DURATION) && - (!video_st || video_pts >= STREAM_DURATION)) - break;*/ - - if(data->frame_count >= data->num_frames) return; - - //copy the current surface to the buffer - if(data->picture)convert_surface_frame(data->picture,surface,gamma()); - - //encode the frame and write it to the file - if(!data->vid.write_frame(formatc,video_st,data->picture)) - { - synfig::warning("Unable to write a frame"); - } - - data->frame_count++; - - if(data->frame_count >= data->num_frames) - { - data->CleanUp(); - } - - /* write interleaved audio and video frames */ - /*if (!video_st || (video_st && audio_st && audio_pts < video_pts)) { - data->aud.write_frame(formatc,audio_st); - } else { - data->vid.write_frame(formatc,video_st); - }*/ -} - -bool -Target_LibAVCodec::start_frame(synfig::ProgressCallback */*callback*/) -{ - //prepare all the color buffer stuff, etc. - - return true; -} - -Color * -Target_LibAVCodec::start_scanline(int scanline) -{ - return surface[scanline]; - - return 0; // This should kill the render process -} - -bool -Target_LibAVCodec::end_scanline() -{ - //don't need to do anything until the whole frame is done - return true; -} - -bool Target_LibAVCodec::init() -{ - //hardcoded test for mpeg - if(!data->Initialize(filename.c_str(),NULL)) - { - synfig::warning("Unable to Initialize the audio video encoders"); - return 0; - } - - return true; -}