X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Ftrunk%2Fsrc%2Fmodules%2Fmod_libavcodec%2Ftrgt_av.cpp;h=93f0d08b84326092e93cedc3686bf2fa70bec3dc;hb=bbf05c1d5f53f61ec5b033c5c305a497d8389d46;hp=fa0d7542c58941dffb2ffa0f2014e18bb1d6142c;hpb=e8a065f2385c219c511b57dac52786120bfa097d;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 index fa0d754..93f0d08 100644 --- a/synfig-core/trunk/src/modules/mod_libavcodec/trgt_av.cpp +++ b/synfig-core/trunk/src/modules/mod_libavcodec/trgt_av.cpp @@ -1,8 +1,8 @@ /* === S Y N F I G ========================================================= */ -/*! \file trgt.cpp +/*! \file trgt_av.cpp ** \brief \writeme ** -** $Id: trgt_av.cpp,v 1.1.1.1 2005/01/04 01:23:11 darco Exp $ +** $Id$ ** ** \legal ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley @@ -35,7 +35,7 @@ extern "C" { -#include "libavformat/avformat.h" +#include } #include @@ -61,7 +61,7 @@ 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: trgt_av.cpp,v 1.1.1.1 2005/01/04 01:23:11 darco Exp $"); +SYNFIG_TARGET_SET_CVS_ID(Target_LibAVCodec,"$Id$"); /* === C L A S S E S & S T R U C T S ======================================= */ @@ -74,8 +74,8 @@ struct VideoInfo { int w,h; int fps; - - int bitrate; + + int bitrate; }; struct AudioInfo @@ -89,7 +89,7 @@ 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; @@ -99,7 +99,7 @@ AVFrame *alloc_picture(int pix_fmt, int width, int height) av_free(picture); return NULL; } - avpicture_fill((AVPicture *)picture, picture_buf, + avpicture_fill((AVPicture *)picture, picture_buf, pix_fmt, width, height); return picture; } @@ -117,45 +117,45 @@ static void convert_surface_frame(AVFrame *pic, const Surface &s, const Gamma &g 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); @@ -165,12 +165,12 @@ static void convert_surface_frame(AVFrame *pic, const Surface &s, const Gamma &g 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 } } @@ -184,16 +184,16 @@ 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) @@ -201,22 +201,22 @@ public: 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) { @@ -234,48 +234,48 @@ public: } 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); } @@ -287,13 +287,13 @@ class VideoEncoder { public: AVFrame *encodable; //for compressiong 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) @@ -301,14 +301,14 @@ public: 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; - + context = stream->codec; + //search for desired codec (contained in the stream) codec = avcodec_find_encoder(context->codec_id); if(!codec) @@ -316,21 +316,21 @@ public: synfig::warning("Open_video: could not find desired codec"); return 0; } - + //try to open the codec - if(avcodec_open(context, codec) < 0) + if(avcodec_open(context, codec) < 0) { synfig::warning("open_video: could not open desired codec"); return 0; } - - videobuffer.resize(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) @@ -338,31 +338,31 @@ public: 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) + 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) { @@ -371,51 +371,70 @@ public: synfig::warning("Attempt to open a video codec with a bad format or stream"); return false; } - - int size, + + int size, ret = 0; - AVCodecContext *context = &stream->codec; - + 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 transofrm necessary + 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) - img_convert((AVPicture *)encodable, context->pix_fmt, + img_convert((AVPicture *)encodable, context->pix_fmt, (AVPicture *)pict, PIX_FMT_RGB24, context->width, context->height); - + 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; + //cludge for raw picture format (they said they'd fix) if (formatc->oformat->flags & AVFMT_RAWPICTURE) { - ret = av_write_frame(formatc, stream->index, (uint8_t *)pict, sizeof(AVPicture)); + ret = av_write_frame(formatc, &pkt); } - else + else { //encode our given image size = avcodec_encode_video(context, &videobuffer[0], videobuffer.size(), pict); - - //if not zero we've got stuff to write - if (size != 0) + + //if greater than zero we've got stuff to write + if (size > 0) { - ret = av_write_frame(formatc, stream->index, &videobuffer[0], size); - + 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) + if(ret < 0) { synfig::warning("write_frame: error while writing video frame"); - return false; + return false; } } //if 0, it was buffered (if invalid picture we don't have ANY data left) @@ -427,25 +446,25 @@ public: return false; startedencoding = false; } - + //buffered picture } } - + return true; } - + void close(AVFormatContext *formatc, AVStream *stream) { if(stream) - avcodec_close(&stream->codec); - - if (encodable) + avcodec_close(stream->codec); + + if (encodable) { free_picture(encodable); encodable = 0; } - + videobuffer.resize(0); } }; @@ -453,22 +472,22 @@ public: 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;*/ @@ -481,21 +500,21 @@ public: { 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; + num_frames = 0; + + //aud.samples = 0; //audio_st = 0; //audio_pts = 0; } @@ -504,7 +523,7 @@ public: { CleanUp(); } - + bool Initialize(const char *filename, const char *typestring) { //guess if we have a type string, otherwise use filename @@ -517,19 +536,19 @@ public: { 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) + + if(!format) { synfig::warning("Unable to find output format"); return 0; } - + //allocate the output context formatc = (AVFormatContext *)av_mallocz(sizeof(AVFormatContext)); if(!formatc) @@ -539,16 +558,16 @@ public: } //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) + if(format->video_codec != CODEC_ID_NONE) { video_st = add_video_stream(format->video_codec,vInfo); if(!video_st) @@ -556,32 +575,23 @@ public: 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 - - AVFormatParameters fmtparam,*ap = &fmtparam; - memset(ap, 0, sizeof(*ap)); - ap->frame_rate = vInfo.fps; - ap->frame_rate_base = 1; - ap->width = vInfo.w; - ap->height = vInfo.h; - //ap->pix_fmt = frame_pix_fmt; - - if(av_set_parameters(formatc, ap) < 0) - { - synfig::warning("Invalid output formatting parameters"); - return 0; - } - + + 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) { @@ -597,9 +607,9 @@ public: { synfig::warning("Could not open audio encoder"); return 0; - } + } }*/ - + //open output file if(!(format->flags & AVFMT_NOFILE)) { @@ -610,7 +620,7 @@ public: 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); @@ -619,39 +629,39 @@ public: 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() { 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) + //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) { @@ -660,31 +670,31 @@ public: { av_freep(&formatc->streams[i]); } - + if(!(format->flags & AVFMT_NOFILE)) { /* close the output file */ url_fclose(&formatc->pb); } - + /* 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; + picture = 0; } //create a video output stream @@ -692,36 +702,37 @@ public: { 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 = 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 */ - context->frame_rate = info.fps; - context->frame_rate_base = 1; - + // 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) @@ -729,32 +740,32 @@ public: /* 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 = 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; } }; @@ -770,7 +781,7 @@ Target_LibAVCodec::Target_LibAVCodec(const char *Filename): av_register_all(); } set_remove_alpha(); - + data = new LibAVEncoder; } @@ -786,9 +797,9 @@ Target_LibAVCodec::set_rend_desc(RendDesc *given_desc) // 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(); @@ -796,47 +807,47 @@ Target_LibAVCodec::set_rend_desc(RendDesc *given_desc) 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 + //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; + 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; } @@ -845,46 +856,46 @@ 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 + + //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) && + /*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); @@ -895,9 +906,9 @@ Target_LibAVCodec::end_frame() bool Target_LibAVCodec::start_frame(synfig::ProgressCallback *callback) -{ +{ //prepare all the color buffer stuff, etc. - + return true; } @@ -905,7 +916,7 @@ Color * Target_LibAVCodec::start_scanline(int scanline) { return surface[scanline]; - + return 0; // This should kill the render process } @@ -924,6 +935,6 @@ bool Target_LibAVCodec::init() synfig::warning("Unable to Initialize the audio video encoders"); return 0; } - + return true; }