/* * xtoffmpeg.c * * Copyright (C) 2003-06 Karl H. Beckers, Frankfurt * EMail: khb@jarre-de-the.net * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifdef HAVE_CONFIG_H # include #endif // HAVE_CONFIG_H #define DEBUGFILE "xtoffmpeg.c" #ifdef USE_FFMPEG #include #include #include #include #include #include #include #include // for timeval struct and related // functions #include #include // xvidcap specific #include "app_data.h" #include "job.h" #include "colors.h" #include "frame.h" #include "codecs.h" #include "xvidcap-intl.h" // ffmpeg stuff #include #include #include "swscale.h" #include "rgb2rgb.h" #include "framehook.h" #include "dsputil.h" #include "opt.h" #include "fifo.h" #define PIX_FMT_ARGB32 PIX_FMT_RGBA32 // this is just my personal // convenience /* * globals * * first libavcodec related stuff */ extern xvCodec tCodecs[NUMCODECS]; extern xvFFormat tFFormats[NUMCODECS]; extern xvAuCodec tAuCodecs[NUMAUCODECS]; static AVCodec *codec; // params for the codecs video static AVFrame *p_inpic; // a AVFrame as wrapper around the // original image data static AVFrame *p_outpic; // and one for the image converted to // yuv420p static uint8_t *outpic_buf; // data buffer for output frame static uint8_t *outbuf; // output buffer for encoded frame static int outbuf_size; // the size of the outbuf may be // other than image_size static AVFormatContext *output_file; // output file via avformat static AVOutputFormat *file_oformat; // ... plus related data static AVStream *out_st = NULL; // ... static struct SwsContext *img_resample_ctx; // for image resampling static int image_size, input_pixfmt; // size of yuv image, pix_fmt of // original image static int out_size; // size of the encoded frame to write to // file static double audio_pts, video_pts; static ColorInfo c_info; static uint8_t *scratchbuf8bit; #ifdef DEBUG void dump8bit(XImage * image, u_int32_t * ct); void dump32bit(XImage * input); void x2ffmpeg_dump_ximage_info(XImage * img, FILE * fp); #endif // DEBUG #ifdef HAVE_FFMPEG_AUDIO #define MAX_AUDIO_PACKET_SIZE (128 * 1024) #include #include typedef struct AVOutputStream { int file_index; /* file index */ int index; /* stream index in the output file */ int source_index; /* AVInputStream index */ AVStream *st; /* stream in the output file */ int encoding_needed; /* true if encoding needed for this stream */ int frame_number; /* input pts and corresponding output pts for A/V sync */ // double sync_ipts; /* dts from the AVPacket of the demuxer in second // // // units */ struct AVInputStream *sync_ist; /* input stream to sync against */ int64_t sync_opts; /* output frame counter, could be changed * to some true timestamp */ /* video only */ int video_resample; AVFrame pict_tmp; /* temporary image for resampling */ struct SwsContext *img_resample_ctx; /* for image resampling */ int resample_height; int video_crop; int topBand; /* cropping area sizes */ int leftBand; int video_pad; int padtop; /* padding area sizes */ int padbottom; int padleft; int padright; /* audio only */ int audio_resample; ReSampleContext *resample; /* for audio resampling */ AVFifoBuffer fifo; /* for compression: one audio fifo per * codec */ FILE *logfile; } AVOutputStream; typedef struct AVInputStream { int file_index; int index; AVStream *st; int discard; /* true if stream data should be discarded */ int decoding_needed; /* true if the packets must be decoded in * 'raw_fifo' */ int64_t sample_index; /* current sample */ int64_t start; /* time when read started */ unsigned long frame; /* current frame */ int64_t next_pts; /* synthetic pts for cases where pkt.pts * is not defined */ int64_t pts; /* current pts */ int is_start; /* is 1 at the start and after a * discontinuity */ } AVInputStream; // external variables for thread synchronization extern pthread_mutex_t recording_mutex; extern pthread_cond_t recording_condition_unpaused; // FIXME: check if this all needs to be global static AVCodec *au_codec; static AVCodecContext *au_c; static AVFormatContext *ic; static AVInputFormat *grab_iformat; static AVOutputStream *au_out_st; static AVInputStream *au_in_st; static uint8_t *audio_buf = NULL; static uint8_t *audio_out = NULL; static pthread_attr_t tattr; static pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER; static pthread_t tid = 0; static int tret = 0; // functions ... static void add_audio_stream(Job * job) { #define DEBUGFUNCTION "add_audio_stream()" Boolean grab_audio = TRUE; AVFormatParameters params, *ap = ¶ms; // audio stream params int err, ret; #ifdef DEBUG printf("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG if (!strcmp(job->snd_device, "-")) { job->snd_device = "pipe:"; grab_audio = FALSE; } else { grab_audio = TRUE; } // prepare input stream memset(ap, 0, sizeof(*ap)); ap->device = job->snd_device; // this is required to guarantee import of the audio input format // context // for some strange reason this only works here and not further down // with av_register_all // audio_init(); if (grab_audio) { ap->sample_rate = job->snd_rate; ap->channels = job->snd_channels; grab_iformat = av_find_input_format("audio_device"); #ifdef DEBUG printf("%s %s: grab iformat %p\n", DEBUGFILE, DEBUGFUNCTION, grab_iformat); if (grab_iformat) printf("%s %s: grab iformat name %s\n", DEBUGFILE, DEBUGFUNCTION, grab_iformat->name); #endif // DEBUG if (av_open_input_file(&ic, "", grab_iformat, 0, ap) < 0) { fprintf(stderr, _("%s %s:Could not find audio grab device %s\n"), DEBUGFILE, DEBUGFUNCTION, job->snd_device); exit(1); } } else { err = av_open_input_file(&ic, ap->device, NULL, 0, ap); if (err < 0) { fprintf(stderr, _("%s %s: error opening input file %s: %i\n"), DEBUGFILE, DEBUGFUNCTION, job->snd_device, err); exit(1); } } au_in_st = av_mallocz(sizeof(AVInputStream)); if (!au_in_st) { fprintf(stderr, _("%s %s: Could not alloc input stream ... aborting\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } au_in_st->st = ic->streams[0]; // If not enough info to get the stream parameters, we decode // the first frames to get it. (used in mpeg case for example) ret = av_find_stream_info(ic); if (ret < 0) { fprintf(stderr, _("%s %s: %s: could not find codec parameters\n"), DEBUGFILE, DEBUGFUNCTION, job->snd_device); exit(1); } // init pts stuff au_in_st->next_pts = 0; au_in_st->is_start = 1; #ifdef DEBUG dump_format(ic, 0, job->snd_device, 0); #endif // DEBUG // OUTPUT // setup output codec au_c = avcodec_alloc_context(); // put sample parameters au_c->codec_id = tAuCodecs[job->au_targetCodec].ffmpeg_id; au_c->codec_type = CODEC_TYPE_AUDIO; au_c->bit_rate = job->snd_smplsize; au_c->sample_rate = job->snd_rate; au_c->channels = job->snd_channels; // au_c->debug = 0x00000FFF; // prepare output stream au_out_st = av_mallocz(sizeof(AVOutputStream)); if (!au_out_st) { fprintf(stderr, _("%s %s: Could not alloc stream ... aborting\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } au_out_st->st = av_new_stream(output_file, 1); if (!au_out_st->st) { fprintf(stderr, _("%s %s: Could not alloc stream\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } au_out_st->st->codec = au_c; if (av_fifo_init(&au_out_st->fifo, 2 * MAX_AUDIO_PACKET_SIZE)) { fprintf(stderr, _("%s %s: Can't initialize fifo for audio recording\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } // This bit is important for inputs other than self-sampled. // The sample rates and no of channels a user asks for // are the ones he/she wants in the encoded mpeg. For self- // sampled audio, these are also the values used for sampling. // Hence there is no resampling necessary (case 1). // When dubbing from a pipe or a different // file, we might have different sample rates or no of // channels // in the input file..... if (au_c->channels == au_in_st->st->codec->channels && au_c->sample_rate == au_in_st->st->codec->sample_rate) { au_out_st->audio_resample = 0; } else { if (au_c->channels != au_in_st->st->codec->channels && au_in_st->st->codec->codec_id == CODEC_ID_AC3) { // Special case for 5:1 AC3 input // and mono or stereo output // Request specific number of channels au_in_st->st->codec->channels = au_c->channels; if (au_c->sample_rate == au_in_st->st->codec->sample_rate) au_out_st->audio_resample = 0; else { au_out_st->audio_resample = 1; au_out_st->resample = audio_resample_init(au_c->channels, au_in_st->st->codec-> channels, au_c->sample_rate, au_in_st->st->codec->sample_rate); if (!au_out_st->resample) { printf(_("%s %s: Can't resample. Aborting.\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } } // Request specific number of channels au_in_st->st->codec->channels = au_c->channels; } else { au_out_st->audio_resample = 1; au_out_st->resample = audio_resample_init(au_c->channels, au_in_st->st->codec->channels, au_c->sample_rate, au_in_st->st->codec->sample_rate); if (!au_out_st->resample) { printf(_("%s %s: Can't resample. Aborting.\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } } } au_in_st->decoding_needed = 1; au_out_st->encoding_needed = 1; // open encoder au_codec = avcodec_find_encoder(au_out_st->st->codec->codec_id); if (avcodec_open(au_out_st->st->codec, au_codec) < 0) { fprintf(stderr, _("%s %s: Error while opening codec for output stream\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } // open decoder au_codec = avcodec_find_decoder(ic->streams[0]->codec->codec_id); if (!au_codec) { fprintf(stderr, _("%s %s: Unsupported codec (id=%d) for input stream\n"), DEBUGFILE, DEBUGFUNCTION, ic->streams[0]->codec->codec_id); exit(1); } if (avcodec_open(ic->streams[0]->codec, au_codec) < 0) { fprintf(stderr, _("%s %s: Error while opening codec for input stream\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } #ifdef DEBUG printf("%s %s: Leaving with %i streams in oc\n", DEBUGFILE, DEBUGFUNCTION, output_file->nb_streams); #endif // DEBUG #undef DEBUGFUNCTION } static void do_audio_out(AVFormatContext * s, AVOutputStream * ost, AVInputStream * ist, unsigned char *buf, int size) { #define DEBUGFUNCTION "do_audio_out()" uint8_t *buftmp; const int audio_out_size = 4 * MAX_AUDIO_PACKET_SIZE; int size_out, frame_bytes; AVCodecContext *enc; // SC: dynamic allocation of buffers if (!audio_buf) audio_buf = av_malloc(2 * MAX_AUDIO_PACKET_SIZE); if (!audio_out) audio_out = av_malloc(audio_out_size); if (!audio_out || !audio_buf) return; // Should signal an error ! enc = ost->st->codec; // resampling is only used for pipe input here if (ost->audio_resample) { buftmp = audio_buf; size_out = audio_resample(ost->resample, (short *) buftmp, (short *) buf, size / (ist->st->codec->channels * 2)); size_out = size_out * enc->channels * 2; } else { buftmp = buf; size_out = size; } // now encode as many frames as possible if (enc->frame_size > 1) { // output resampled raw samples av_fifo_write(&ost->fifo, buftmp, size_out); frame_bytes = enc->frame_size * 2 * enc->channels; while (av_fifo_read(&ost->fifo, audio_buf, frame_bytes) == 0) { AVPacket pkt; // initialize audio output packet av_init_packet(&pkt); pkt.size = avcodec_encode_audio(enc, audio_out, audio_out_size, (short *) audio_buf); if (enc->coded_frame) { pkt.pts = av_rescale_q(enc->coded_frame->pts, enc->time_base, ost->st->time_base); } pkt.flags |= PKT_FLAG_KEY; pkt.stream_index = ost->st->index; pkt.data = audio_out; if (pthread_mutex_trylock(&mp) == 0) { // write the compressed frame in the media file if (av_interleaved_write_frame(s, &pkt) != 0) { fprintf(stderr, _("%s %s: Error while writing audio frame\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } if (pthread_mutex_unlock(&mp) > 0) { fprintf(stderr, _ ("%s %s: Couldn't unlock mutex lock for writing audio frame\n"), DEBUGFILE, DEBUGFUNCTION); } } } } #undef DEBUGFUNCTION } void cleanup_thread_when_stopped() { #define DEBUGFUNCTION "cleanup_thread_when_stopped()" int retval = 0; #ifdef DEBUG printf("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG if (audio_out) { av_free(audio_out); audio_out = NULL; } if (audio_buf) { av_free(audio_buf); audio_buf = NULL; } grab_iformat = NULL; if (au_in_st) { av_free(au_in_st); au_in_st = NULL; } av_close_input_file(ic); #ifdef DEBUG printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG pthread_exit(&retval); #undef DEBUGFUNCTION } void capture_audio_thread(Job * job) { #define DEBUGFUNCTION "capture_audio_thread()" unsigned long start, stop, start_s, stop_s; struct timeval thr_curr_time; long sleep; int retval = -1, len, data_size; uint8_t *ptr, *data_buf; short samples[AVCODEC_MAX_AUDIO_FRAME_SIZE / 2]; AVPacket pkt; signal(SIGUSR1, cleanup_thread_when_stopped); while (TRUE) { // get start time gettimeofday(&thr_curr_time, NULL); start_s = thr_curr_time.tv_sec; start = thr_curr_time.tv_usec; if ((job->state & VC_PAUSE) && !(job->state & VC_STEP)) { pthread_mutex_lock(&recording_mutex); pthread_cond_wait(&recording_condition_unpaused, &recording_mutex); pthread_mutex_unlock(&recording_mutex); } else if (job->state == VC_REC) { audio_pts = (double) /* au_out_st->st->pts.val * * * * av_q2d(au_out_st->st->time_base); */ au_out_st->st->pts.val * au_out_st->st->time_base.num / au_out_st->st->time_base.den; video_pts = (double) out_st->pts.val * out_st->time_base.num / out_st->time_base.den; // sometimes we need to pause writing audio packets for a/v // sync (when audio_pts >= video_pts) // now, if we're reading from a file/pipe, we stop sampling or // // // // else the audio track in the // video would become choppy (packets missing where they were // read but not written) // for real-time sampling we can't do that because otherwise // the input packets queue up // and will eventually be sampled (only later) and lead to // out-of-sync audio (video faster) if (audio_pts < video_pts) { // read a packet from it and output it in the fifo if (av_read_packet(ic, &pkt) < 0) { fprintf(stderr, _("%s %s: error reading audio packet\n"), DEBUGFILE, DEBUGFUNCTION); } len = pkt.size; ptr = pkt.data; while (len > 0) { // decode the packet if needed data_buf = NULL; /* fail safe */ data_size = 0; if (au_in_st->decoding_needed) { /* * XXX: could avoid copy if PCM 16 bits with same * endianness as CPU */ retval = avcodec_decode_audio(au_in_st->st->codec, samples, &data_size, ptr, len); if (retval < 0) { fprintf(stderr, _ ("%s %s: couldn't decode captured audio packet\n"), DEBUGFILE, DEBUGFUNCTION); break; } // Some bug in mpeg audio decoder gives // data_size < 0, it seems they are overflows if (data_size <= 0) { // no audio frame #ifdef DEBUG fprintf(stderr, _("%s %s: no audio frame\n"), DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG ptr += retval; len -= retval; continue; } data_buf = (uint8_t *) samples; au_in_st->next_pts += ((int64_t) AV_TIME_BASE / 2 * data_size) / (au_in_st->st->codec->sample_rate * au_in_st->st->codec->channels); } else { // FIXME: dunno about the following au_in_st->next_pts += ((int64_t) AV_TIME_BASE / 2 * data_size) / (au_in_st->st->codec->sample_rate * au_in_st->st->codec->channels); } ptr += retval; len -= retval; do_audio_out(output_file, au_out_st, au_in_st, data_buf, data_size); } // discard packet av_free_packet(&pkt); } // end outside if pts ... else { if (strcmp(job->snd_device, "pipe:") < 0) if (av_read_packet(ic, &pkt) < 0) { fprintf(stderr, _("error reading audio packet\n")); } #ifdef DEBUG printf(_("%s %s: dropping audio frame %f %f\n"), DEBUGFILE, DEBUGFUNCTION, audio_pts, video_pts); #endif // DEBUG } } // end if VC_REC // get end time gettimeofday(&thr_curr_time, NULL); stop_s = thr_curr_time.tv_sec; stop = thr_curr_time.tv_usec; stop += ((stop_s - start_s) * 1000000); sleep = (1000000 / job->snd_rate) - (stop - start); // FIXME: perhaps this whole stuff is irrelevant. Haven't really // seen a situation // where the encoding was faster than the time needed for a // decent frame-rate // need to look into more details about audio/video sync in // libavcodec/-format // the strange thing, however is: If I leave away the getting of // start/end time and // the usleep stuff, normal audio capture works, piped audio // doesn't *BLINK* if (sleep < 0) sleep = 0; usleep(sleep); } // end while(TRUE) loop retval = 1; pthread_exit(&retval); #undef DEBUGFUNCTION } #endif // HAVE_FFMPEG_AUDIO static void do_video_out(AVFormatContext * s, AVStream * ost, unsigned char *buf, int size) { #define DEBUGFUNCTION "do_video_out()" AVCodecContext *enc; AVPacket pkt; #ifdef DEBUG printf ("%s %s: Entering with format context %p output stream %p buffer %p size %i\n", DEBUGFILE, DEBUGFUNCTION, s, ost, buf, size); #endif // DEBUG enc = ost->codec; // initialize video output packet av_init_packet(&pkt); if (enc->coded_frame) { pkt.pts = av_rescale_q(enc->coded_frame->pts, enc->time_base, ost->time_base); if (enc->coded_frame->key_frame) pkt.flags |= PKT_FLAG_KEY; } pkt.stream_index = ost->index; pkt.data = buf; pkt.size = size; if (av_interleaved_write_frame(s, &pkt) != 0) { fprintf(stderr, _("%s %s: Error while writing video frame\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } #ifdef DEBUG printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG #undef DEBUGFUNCTION } // // convert bgra32 to rgba32 // needed on Solaris/SPARC because the ffmpeg version used doesn't know // PIX_FMT_ABGR32 // void myABGR32toARGB32(XImage * image) { #define DEBUGFUNCTION "myABGR32toARGB32()" char *pdata, *counter; #ifdef DEBUG printf("%s %s: Entering with image %p\n", DEBUGFILE, DEBUGFUNCTION, image); #endif // DEBUG pdata = image->data; for (counter = pdata; counter < (pdata + (image->width * image->height * 4)); counter += 4) { char swap; // , swap1, swap2, swap3, swap4; if (image->byte_order) { // MSBFirst has order argb -> abgr // = rgba32 swap = *(counter + 1); *(counter + 1) = *(counter + 3); *(counter + 3) = swap; } else { // LSBFirst has order bgra -> rgba swap = *counter; *counter = *(counter + 2); *(counter + 2) = swap; } } #ifdef DEBUG printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG #undef DEBUGFUNCTION } // // convert pal8 to rgba32 // libswscale does not support pal8 input // void myPAL8toRGB24(XImage * image, AVFrame * p_inpic, Job * job) { #define DEBUGFUNCTION "myABGR32toRGB24()" u_int32_t *color_table = job->color_table; int y = 0, x = 0; uint8_t *out_cursor = NULL; uint8_t *out = (uint8_t *) p_inpic->data[0]; /* * 8bit pseudo-color images may have lines padded by excess bytes * these need to be removed before conversion FIXME: other formats * might also have this problem */ for (y = 0; y < image->height; y++) { out_cursor = (uint8_t *) image->data + (y * image->bytes_per_line); for (x = 0; x < image->width; x++) { *out++ = ((color_table[*out_cursor] & 0x00FF0000) >> 16); *out++ = ((color_table[*out_cursor] & 0x0000FF00) >> 8); *out++ = (color_table[*out_cursor] & 0x000000FF); out_cursor++; } } #undef DEBUGFUNCTION } /* * prepare the color table for pseudo color input to libavcodec's imgconvert * I'm downsampling the 16 bit color entries to 8 bit as it expects * 32 bit (=4 * 8) * from looking at imgconvert_template.c I'd say libavcodec expects argb * logically, not byte-wise */ u_int32_t *FFMPEGcolorTable(XColor * colors, int ncolors) { #define DEBUGFUNCTION "FFMPEGcolorTable()" u_int32_t *color_table, *pixel; int i; // , n; #ifdef DEBUG printf("%s %s: Entering with colors %p and %i colors\n", DEBUGFILE, DEBUGFUNCTION, colors, ncolors); #endif // DEBUG color_table = malloc(256 * 4); if (!color_table) return (NULL); pixel = color_table; for (i = 0; i < ncolors; i++) { u_int32_t color_table_entry, swap; color_table_entry = 0; // alpha alway zero swap = colors[i].red; swap &= 0x0000FF00; // color is 16 bits, delete ls 8 bits swap <<= 8; // then shift ms 8 bits into position color_table_entry = (color_table_entry | swap); swap = colors[i].green; swap &= 0x0000FF00; // color is 16 bits, ms 8 bits already in // position, delete ls 8 bits color_table_entry = (color_table_entry | swap); swap = colors[i].blue; swap >>= 8; color_table_entry = (color_table_entry | swap); *pixel = color_table_entry; pixel++; } #ifdef DEBUG printf("%s %s: color_table pointer: %p\n", DEBUGFILE, DEBUGFUNCTION, color_table); printf("%s %s: color_table third entry: 0x%.8X\n", DEBUGFILE, DEBUGFUNCTION, *(color_table + 2)); // first and second black & white #endif // DEBUG #ifdef DEBUG printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG return (color_table); #undef DEBUGFUNCTION } void prepareOutputFile(char *jFileName, AVFormatContext * oc, int number) { #define DEBUGFUNCTION "prepareOutputFile()" #ifdef DEBUG printf ("%s %s: Entering with filename %s format context %p and number %i\n", DEBUGFILE, DEBUGFUNCTION, jFileName, oc, number); #endif // DEBUG // prepare output file for format context if ((strcasecmp(jFileName, "-") == 0) || (strcasecmp(jFileName, "pipe:") == 0)) { jFileName = "pipe:"; snprintf(oc->filename, sizeof(oc->filename), "pipe:"); // register_protocol (&pipe_protocol); } else { char first; char tmp_buf[PATH_MAX + 1]; first = jFileName[0]; sprintf(tmp_buf, jFileName, number); // if the filename's first char is a / we have an absolute path // and we want one for the file URL. If we don't have one, we // construct one if (first != '/') { sprintf(oc->filename, "file://%s/%s", getenv("PWD"), tmp_buf); } else { sprintf(oc->filename, "file://%s", tmp_buf); } // register_protocol (&file_protocol); } #ifdef DEBUG printf("%s %s: Leaving with filename %s\n", DEBUGFILE, DEBUGFUNCTION, oc->filename); #endif // DEBUG #undef DEBUGFUNCTION } /* * add a video output stream */ AVStream *add_video_stream(AVFormatContext * oc, XImage * image, int input_pixfmt, int codec_id, Job * job) { #define DEBUGFUNCTION "add_video_stream()" AVStream *st; int pix_fmt_mask = 0, i = 0; int fps = job->fps / 100, quality = job->quality; #ifdef DEBUG printf("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG st = av_new_stream(oc, 0); if (!st) { fprintf(stderr, _("%s %s: Could not alloc output stream\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } st->codec->codec_id = codec_id; st->codec->codec_type = CODEC_TYPE_VIDEO; // find the video encoder codec = avcodec_find_encoder(st->codec->codec_id); if (!codec) { fprintf(stderr, _("%s %s: video codec not found\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } // put sample parameters st->codec->bit_rate = 400000; // resolution must be a multiple of two ... this is taken care of // elsewhere but needs to be ensured for rescaled dimensions, too if (job->rescale != 100) { int n; double r; r = sqrt(100 / job->rescale); n = image->width / r; if (n % 2 > 0) n--; st->codec->width = n; n = image->height / r; if (n % 2 > 0) n--; st->codec->height = n; } else { st->codec->width = image->width; st->codec->height = image->height; } // 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. st->codec->time_base.den = fps; st->codec->time_base.num = 1; // emit one intra frame every fifty frames at most st->codec->gop_size = 50; // find suitable pix_fmt for codec if (&(codec->pix_fmts) != NULL) { for (i = 0; codec->pix_fmts[i] != -1; i++) { pix_fmt_mask |= (1 << codec->pix_fmts[i]); } st->codec->pix_fmt = avcodec_find_best_pix_fmt(pix_fmt_mask, input_pixfmt, (input_pixfmt == PIX_FMT_ARGB32 ? 1 : 0), NULL); if (st->codec->pix_fmt < 0) st->codec->pix_fmt = PIX_FMT_YUV420P; } else st->codec->pix_fmt = PIX_FMT_YUV420P; #ifdef DEBUG printf ("%s %s: pix_fmt_mask %i, has alpha %i, input_pixfmt %i, output pixfmt %i\n", DEBUGFILE, DEBUGFUNCTION, pix_fmt_mask, (input_pixfmt == PIX_FMT_ARGB32 ? 1 : 0), input_pixfmt, st->codec->pix_fmt); #endif // DEBUG // flags st->codec->flags |= (CODEC_FLAG_TRELLIS_QUANT | CODEC_FLAG2_FAST); st->codec->flags &= ~CODEC_FLAG_OBMC; // some formats want stream headers to be seperate if (oc->oformat->flags & AVFMT_GLOBALHEADER) st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; // bit rate calculation st->codec->bit_rate = (st->codec->width * st->codec->height * (((((st->codec->height + st->codec->width) / 100) - 5) >> 1) + 10) * quality) / 100; if (st->codec->bit_rate < 300000) st->codec->bit_rate = 300000; #ifdef DEBUG printf("%s %s: bitrate = %i\n", DEBUGFILE, DEBUGFUNCTION, st->codec->bit_rate); #endif st->codec->mb_decision = 2; /* st->codec->me_method = ME_ZERO; st->codec->qmin = 1; * st->codec->qmax = 1; */ // st->codec->debug = 0x00000FFF; /* out_st->time_base.num = out_st->codec->time_base.num; * out_st->time_base.den = out_st->codec->time_base.den; * out_st->pts.val = (double)out_st->pts.val * out_st->time_base.num / * * * out_st->time_base.den; */ #ifdef DEBUG printf("%s %s: Leaving with %i streams in oc\n", DEBUGFILE, DEBUGFUNCTION, oc->nb_streams); #endif // DEBUG return st; #undef DEBUGFUNCTION } int guess_input_pix_fmt(XImage * image, ColorInfo * c_info) { #define DEBUGFUNCTION "guess_input_pix_fmt()" int input_pixfmt; switch (image->bits_per_pixel) { case 8: #ifdef DEBUG printf("%s %s: 8 bit pallete\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG input_pixfmt = PIX_FMT_PAL8; break; case 16: if (image->red_mask == 0xF800 && image->green_mask == 0x07E0 && image->blue_mask == 0x1F) { #ifdef DEBUG printf("%s %s: 16 bit RGB565\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG input_pixfmt = PIX_FMT_BGR565; } else if (image->red_mask == 0x7C00 && image->green_mask == 0x03E0 && image->blue_mask == 0x1F) { #ifdef DEBUG printf("%s %s: 16 bit RGB555\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG input_pixfmt = PIX_FMT_BGR555; } else { fprintf(stderr, _ ("%s %s: rgb ordering at image depth %i not supported ... aborting\n"), DEBUGFILE, DEBUGFUNCTION, image->bits_per_pixel); fprintf(stderr, "%s %s: color masks: r 0x%.6lX g 0x%.6lX b 0x%.6lX\n", DEBUGFILE, DEBUGFUNCTION, image->red_mask, image->green_mask, image->blue_mask); exit(1); } break; case 24: if (image->red_mask == 0xFF0000 && image->green_mask == 0xFF00 && image->blue_mask == 0xFF) { input_pixfmt = PIX_FMT_BGR24; } else if (image->red_mask == 0xFF && image->green_mask == 0xFF00 && image->blue_mask == 0xFF0000) { input_pixfmt = PIX_FMT_RGB24; } else { fprintf(stderr, _ ("%s %s: rgb ordering at image depth %i not supported ... aborting\n"), DEBUGFILE, DEBUGFUNCTION, image->bits_per_pixel); fprintf(stderr, "%s %s: color masks: r 0x%.6lX g 0x%.6lX b 0x%.6lX\n", DEBUGFILE, DEBUGFUNCTION, image->red_mask, image->green_mask, image->blue_mask); exit(1); } break; case 32: if (c_info->alpha_mask == 0xFF000000 && image->green_mask == 0xFF00) { // byte order is relevant here, not endianness // endianness is handled by avcodec, but atm no such thing // as having ABGR, instead of ARGB in a word. Since we // need this for Solaris/SPARC, but need to do the // conversion // for every frame we do it outside of this loop, cf. // below // this matches both ARGB32 and ABGR32 input_pixfmt = PIX_FMT_ARGB32; } else { fprintf(stderr, _ ("%s %s: image depth %i not supported ... aborting\n"), DEBUGFILE, DEBUGFUNCTION, image->bits_per_pixel); exit(1); } break; default: fprintf(stderr, _("%s %s: image depth %i not supported ... aborting\n"), DEBUGFILE, DEBUGFUNCTION, image->bits_per_pixel); exit(1); } return input_pixfmt; #undef DEBUGFUNCTION } /* * write ximage as mpeg file to 'fp' */ void XImageToFFMPEG(FILE * fp, XImage * image, Job * job) { #define DEBUGFUNCTION "XImageToFFMPEG()" int ret; #ifdef DEBUG printf("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG // encoder needs to be prepared only once .. if (job->state & VC_START) { // it's the first call #ifdef DEBUG printf("%s %s: doing x2ffmpeg init for targetCodec %i\n", DEBUGFILE, DEBUGFUNCTION, job->targetCodec); #endif // DEBUG // determine input picture format // // FIXME: test for exotic formats // endianness is treated by avcodec // color info only needs to be retrieved once for true color X ... // // // // dunno about pseudo color xvc_get_color_info(image, &c_info); #ifdef DEBUG { FILE *errout; printf("%s %s: got color info\n", DEBUGFILE, DEBUGFUNCTION); errout = fdopen(2, "w"); // x2ffmpeg_dump_ximage_info(image, errout); printf("%s %s: alpha_mask: 0x%.8X\n", DEBUGFILE, DEBUGFUNCTION, c_info.alpha_mask); printf("%s %s: alpha_shift: %li\n", DEBUGFILE, DEBUGFUNCTION, c_info.alpha_shift); printf("%s %s: red_shift: %li\n", DEBUGFILE, DEBUGFUNCTION, c_info.red_shift); printf("%s %s: green_shift: %li\n", DEBUGFILE, DEBUGFUNCTION, c_info.green_shift); printf("%s %s: blue_shift: %li\n", DEBUGFILE, DEBUGFUNCTION, c_info.blue_shift); } #endif // DEBUG #ifdef DEBUG printf("%s %s: image->byte_order: %i, msb=%i, lsb=%i\n", DEBUGFILE, DEBUGFUNCTION, image->byte_order, MSBFirst, LSBFirst); #endif // DEBUG // determin input picture format input_pixfmt = guess_input_pix_fmt(image, &c_info); // register all libav* related stuff av_register_all(); /* // next call the right init() function for the file format * (e.g. // avienc_init()) if ( tFFormats[job->target].init ) * (*tFFormats[job->target].init) (); */ // guess AVOutputFormat if (job->target >= CAP_MF) file_oformat = guess_format(tFFormats[job->target].ffmpeg_name, NULL, NULL); else { char tmp_fn[30]; snprintf(tmp_fn, 29, "test-%%d.%s", xvc_next_element(tFFormats[job->target].extensions)); file_oformat = guess_format(NULL, tmp_fn, NULL); } if (!file_oformat) { fprintf(stderr, _ ("%s %s: Couldn't determin output format ... aborting\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } #ifdef DEBUG printf ("%s %s: found AVOutputFormat %s it expects a number in the filename (0=no/1=yes) %i\n", DEBUGFILE, DEBUGFUNCTION, file_oformat->name, (file_oformat->flags & AVFMT_NEEDNUMBER)); printf ("%s %s: found based on: target %i - targetCodec %i - ffmpeg codec id %i\n", DEBUGFILE, DEBUGFUNCTION, job->target, job->targetCodec, tCodecs[job->targetCodec].ffmpeg_id); #endif // DEBUG // prepare AVFormatContext output_file = av_alloc_format_context(); if (!output_file) { fprintf(stderr, _ ("%s %s: Error allocating memory for format context ... aborting\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } output_file->oformat = file_oformat; if (output_file->oformat->priv_data_size > 0) { output_file->priv_data = av_mallocz(output_file->oformat->priv_data_size); // FIXME: do I need to free this? if (!output_file->priv_data) { fprintf(stderr, _ ("%s %s: Error allocating private data for format context ... aborting\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } } // output_file->packet_size= mux_packet_size; // output_file->mux_rate= mux_rate; output_file->preload = (int) (0.5 * AV_TIME_BASE); output_file->max_delay = (int) (0.7 * AV_TIME_BASE); // output_file->loop_output = loop_output; // add the video stream and initialize the codecs // // prepare stream out_st = add_video_stream(output_file, image, (input_pixfmt == PIX_FMT_PAL8 ? PIX_FMT_RGB24 : input_pixfmt), tCodecs[job->targetCodec].ffmpeg_id, job); // FIXME: set params // memset (p_fParams, 0, sizeof(*p_fParams)); // p_fParams->image_format = image_format; // p_fParams->time_base.den = out_st->codec->time_base.den; // p_fParams->time_base.num = out_st->codec->time_base.num; // p_fParams->width = out_st->codec->width; // p_fParams->height = out_st->codec->height; // if (av_set_parameters (output_file, p_fParams) < 0) { if (av_set_parameters(output_file, NULL) < 0) { fprintf(stderr, _("%s %s: Invalid encoding parameters ... aborting\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } // open the codec // c = out_st->codec; if (avcodec_open(out_st->codec, codec) < 0) { fprintf(stderr, _("%s %s: could not open video codec\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } #ifdef HAVE_FFMPEG_AUDIO if ((job->flags & FLG_REC_SOUND) && (job->au_targetCodec > 0)) { add_audio_stream(job); #ifdef DEBUG dump_format(output_file, 0, output_file->filename, 1); #endif // DEBUG // initialize a mutex lock to its default value ret = pthread_mutex_init(&mp, NULL); // create and start capture thread // initialized with default attributes tret = pthread_attr_init(&tattr); // create the thread tret = pthread_create(&tid, &tattr, (void *) capture_audio_thread, job); } #endif // HAVE_FFMPEG_AUDIO /* * prepare pictures */ // input picture p_inpic = avcodec_alloc_frame(); if (input_pixfmt == PIX_FMT_PAL8) { scratchbuf8bit = malloc(avpicture_get_size (PIX_FMT_RGB24, image->width, image->height)); if (!scratchbuf8bit) { fprintf(stderr, "%s %s: Could not allocate buffer for 8bit palette conversion\n", DEBUGFILE, DEBUGFUNCTION); exit(1); } avpicture_fill((AVPicture *) p_inpic, scratchbuf8bit, input_pixfmt, image->width, image->height); p_inpic->data[0] = scratchbuf8bit; p_inpic->linesize[0] = image->width * 3; } else { avpicture_fill((AVPicture *) p_inpic, (uint8_t *) image->data, input_pixfmt, image->width, image->height); } // output picture p_outpic = avcodec_alloc_frame(); image_size = avpicture_get_size(out_st->codec->pix_fmt, out_st->codec->width, out_st->codec->height); outpic_buf = av_malloc(image_size); if (!outpic_buf) { fprintf(stderr, _ ("%s %s: Could not allocate buffer for output frame! ... aborting\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } avpicture_fill((AVPicture *) p_outpic, outpic_buf, out_st->codec->pix_fmt, out_st->codec->width, out_st->codec->height); /* * prepare output buffer for encoded frames */ if ((image_size + 200) < FF_MIN_BUFFER_SIZE) outbuf_size = FF_MIN_BUFFER_SIZE; else outbuf_size = image_size + 200; outbuf = malloc(outbuf_size); if (!outbuf) { fprintf(stderr, _ ("%s %s: Could not allocate buffer for encoded frame (outbuf)! ... aborting\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } // img resampling if (!img_resample_ctx) { img_resample_ctx = sws_getContext(image->width, image->height, (input_pixfmt == PIX_FMT_PAL8 ? PIX_FMT_RGB24 : input_pixfmt), out_st->codec->width, out_st->codec->height, out_st->codec->pix_fmt, 1, NULL, NULL, NULL); // sws_rgb2rgb_init(SWS_CPU_CAPS_MMX*0); } // file preparation needs to be done once for multi-frame capture // and multiple times for single-frame capture if (job->target >= CAP_MF) { // prepare output filenames and register protocols // after this output_file->filename should have the right // filename prepareOutputFile(job->file, output_file, job->movie_no); // open the file if (url_fopen (&output_file->pb, output_file->filename, URL_WRONLY) < 0) { fprintf(stderr, _("%s %s: Could not open '%s' ... aborting\n"), DEBUGFILE, DEBUGFUNCTION, output_file->filename); exit(1); } if (av_write_header(output_file) < 0) { dump_format(output_file, 0, output_file->filename, 1); fprintf(stderr, _ ("%s %s: Could not write header for output file (incorrect codec paramters ?) ... aborting\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } } #ifdef DEBUG printf("%s %s: leaving xffmpeg init\n", DEBUGFILE, DEBUGFUNCTION); printf("%s %s: codec %p\n", DEBUGFILE, DEBUGFUNCTION, codec); printf("%s %s: p_inpic %p\n", DEBUGFILE, DEBUGFUNCTION, p_inpic); printf("%s %s: p_outpic %p\n", DEBUGFILE, DEBUGFUNCTION, p_outpic); printf("%s %s: outpic_buf %p\n", DEBUGFILE, DEBUGFUNCTION, outpic_buf); printf("%s %s: outbuf %p\n", DEBUGFILE, DEBUGFUNCTION, outbuf); printf("%s %s: output_file %p\n", DEBUGFILE, DEBUGFUNCTION, output_file); printf("%s %s: file_oformat %p\n", DEBUGFILE, DEBUGFUNCTION, file_oformat); printf("%s %s: out_st %p\n", DEBUGFILE, DEBUGFUNCTION, out_st); printf("%s %s: image size %i - input pixfmt %i - out_size %i\n", DEBUGFILE, DEBUGFUNCTION, image_size, input_pixfmt, out_size); printf("%s %s: audio_pts %.f - video_pts %.f\n", DEBUGFILE, DEBUGFUNCTION, audio_pts, video_pts); printf("%s %s: c_info %p - scratchbuf8bit %p\n", DEBUGFILE, DEBUGFUNCTION, &c_info, scratchbuf8bit); #endif // DEBUG } if (job->target < CAP_MF) { // prepare output filenames and register protocols // after this output_file->filename should have the right filename prepareOutputFile(job->file, output_file, job->pic_no); // open the file if (url_fopen(&output_file->pb, output_file->filename, URL_WRONLY) < 0) { fprintf(stderr, _("%s %s: Could not open '%s' ... aborting\n"), DEBUGFILE, DEBUGFUNCTION, output_file->filename); exit(1); } if (av_write_header(output_file) < 0) { dump_format(output_file, 0, output_file->filename, 1); fprintf(stderr, _ ("%s %s: Could not write header for output file (incorrect codec paramters ?) ... aborting\n"), DEBUGFILE, DEBUGFUNCTION); exit(1); } } /* * convert input pic to pixel format the encoder expects */ #ifdef DEBUG if (input_pixfmt == PIX_FMT_ARGB32) dump32bit(image); if (input_pixfmt == PIX_FMT_PAL8) dump8bit(image, (u_int32_t *) job->color_table); #endif // DEBUG // FIXME: test if this is still necessary if (input_pixfmt == PIX_FMT_ARGB32 && c_info.alpha_mask == 0xFF000000 && image->red_mask == 0xFF && image->green_mask == 0xFF00 && image->blue_mask == 0xFF0000) { myABGR32toARGB32(image); } else if (input_pixfmt == PIX_FMT_PAL8) { myPAL8toRGB24(image, p_inpic, job); } // img resampling and conversion if (sws_scale(img_resample_ctx, p_inpic->data, p_inpic->linesize, 0, image->height, p_outpic->data, p_outpic->linesize) < 0) { fprintf(stderr, _ ("%s %s: error converting or resampling frame: context %p, iwidth %i, iheight %i, owidth %i, oheight %i, inpfmt %i opfmt %i\n"), DEBUGFILE, DEBUGFUNCTION, img_resample_ctx, image->width, image->height, out_st->codec->width, out_st->codec->height, input_pixfmt, out_st->codec->pix_fmt); exit(1); } /* * encode the image */ #ifdef DEBUG printf ("%s %s: calling encode_video with codec %p, outbuf %p, outbuf size %i, output frame %p\n", DEBUGFILE, DEBUGFUNCTION, out_st->codec, outbuf, outbuf_size, p_outpic); #endif // DEBUG out_size = avcodec_encode_video(out_st->codec, outbuf, outbuf_size, p_outpic); if (out_size < 0) { fprintf(stderr, _ ("%s %s: error encoding frame: c %p, outbuf %p, size %i, frame %p\n"), DEBUGFILE, DEBUGFUNCTION, out_st->codec, outbuf, outbuf_size, p_outpic); exit(1); } #ifdef HAVE_FFMPEG_AUDIO if (job->flags & FLG_REC_SOUND) { if (pthread_mutex_lock(&mp) > 0) { fprintf(stderr, _ ("mutex lock for writing video frame failed ... aborting\n")); exit(1); } } #endif // HAVE_FFMPEG_AUDIO /* * write frame to file */ if (out_size > 0) { // if ( job->target >= CAP_MF ) do_video_out(output_file, out_st, outbuf, out_size); // else // fwrite(outbuf, image_size, 1, fp); } if (job->target < CAP_MF) url_fclose(&output_file->pb); #ifdef HAVE_FFMPEG_AUDIO /* * release the mutex */ if (job->flags & FLG_REC_SOUND) { // printf("releasing the mutex lock\n"); if (pthread_mutex_unlock(&mp) > 0) { fprintf(stderr, _ ("couldn't release the mutex for writing video frame ... aborting\n")); } } #endif // HAVE_FFMPEG_AUDIO #undef DEBUGFUNCTION } /* * KHB: FFMPEG cleanup */ void FFMPEGClean(Job * job) { #define DEBUGFUNCTION "FFMPEGClean()" #ifdef DEBUG printf("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG #ifdef HAVE_FFMPEG_AUDIO // if (job->flags & FLG_REC_SOUND && tid > 0) { { int tret; tret = pthread_kill(tid, SIGUSR1); pthread_join(tid, NULL); tid = 0; } } #endif // HAVE_FFMPEG_AUDIO if (output_file) { /* * write trailer */ av_write_trailer(output_file); } if (output_file) { int i; /* * close file if multi-frame capture ... otherwise closed already */ if (job->target >= CAP_MF) url_fclose(&output_file->pb); /* * free streams */ for (i = 0; i < output_file->nb_streams; i++) { if (output_file->streams[i]->codec) { avcodec_close(output_file->streams[i]->codec); av_free(output_file->streams[i]->codec); output_file->streams[i]->codec = NULL; } av_free(output_file->streams[i]); if (out_st) out_st = NULL; #ifdef HAVE_FFMPEG_AUDIO if (au_out_st) au_out_st = NULL; #endif // HAVE_FFMPEG_AUDIO } /* * free format context */ av_free(output_file->priv_data); output_file->priv_data = NULL; av_free(output_file); output_file = NULL; } if (img_resample_ctx) { sws_freeContext(img_resample_ctx); // img_resample_close(img_resample_ctx); img_resample_ctx = NULL; } if (outpic_buf) { av_free(outpic_buf); outpic_buf = NULL; } av_free(outbuf); /* avcodec seems to do that job */ outbuf = NULL; /* if (frame) { free(frame); frame = NULL; } */ av_free(p_inpic); p_inpic = NULL; av_free(outpic_buf); outpic_buf = NULL; av_free(p_outpic); p_outpic = NULL; if (input_pixfmt == PIX_FMT_PAL8 && scratchbuf8bit) { free(scratchbuf8bit); scratchbuf8bit = NULL; } codec = NULL; #ifdef HAVE_FFMPEG_AUDIO au_codec = NULL; #endif // HAVE_FFMPEG_AUDIO // av_free_static(); #ifdef DEBUG printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG #undef DEBUGFUNCTION } #ifdef DEBUG // // dump info about XImage - for debugging purposes // void x2ffmpeg_dump_ximage_info(XImage * img, FILE * fp) { #define DEBUGFUNCTION "x2ffmpeg_dump_ximage_info()" #ifdef DEBUG printf("%s %s: Entering with image %p and file pointer %p\n", DEBUGFILE, DEBUGFUNCTION, img, fp); #endif // DEBUG fprintf(fp, " width %d\n", img->width); fprintf(fp, " height %d\n", img->height); fprintf(fp, " xoffset %d\n", img->xoffset); fprintf(fp, " format %d\n", img->format); fprintf(fp, " data addr 0x%X\n", (int) img->data); fprintf(fp, " first four bytes of data 0x%X 0x%X 0x%X 0x%X\n", (unsigned char) (img->data[0]), (unsigned char) (img->data[1]), (unsigned char) img->data[2], (unsigned char) img->data[3]); fprintf(fp, " byte_order %s\n", img->byte_order ? "MSBFirst" : "LSBFirst"); fprintf(fp, " bitmap_unit %d\n", img->bitmap_unit); fprintf(fp, " bitmap_bit_order %s\n", img->bitmap_bit_order ? "MSBFirst" : "LSBFirst"); fprintf(fp, " bitmap_pad %d\n", img->bitmap_pad); fprintf(fp, " depth %d\n", img->depth); fprintf(fp, " bytes_per_line %d\n", img->bytes_per_line); fprintf(fp, " bits_per_pixel %d\n", img->bits_per_pixel); fprintf(fp, " red_mask 0x%.8lX\n", img->red_mask); fprintf(fp, " green_mask 0x%.8lX\n", img->green_mask); fprintf(fp, " blue_mask 0x%.8lX\n", img->blue_mask); #ifdef DEBUG printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG #undef DEBUGFUNCTION } // // dump 32bit image to pnm // this is just used for debugging purposes // void dump32bit(XImage * input) { #define DEBUGFUNCTION "dump32bit()" int row, col; static char head[256]; static FILE *fp2 = NULL; uint8_t *ptr2, *output; long size; register unsigned int rm = input->red_mask, gm = input->green_mask, bm = input->blue_mask, rs = c_info.red_shift, gs = c_info.green_shift, bs = c_info.blue_shift, *p32 = (unsigned int *) input->data; #ifdef DEBUG printf("%s %s: Entering with image %p\n", DEBUGFILE, DEBUGFUNCTION, input); #endif // DEBUG sprintf(head, "P6\n%d %d\n%d\n", input->width, input->height, 255); size = ((input->bytes_per_line * input->height) / 4) * 3; output = malloc(size); ptr2 = output; for (row = 0; row < input->height; row++) { for (col = 0; col < input->width; col++) { *output++ = ((*p32 & rm) >> rs); *output++ = ((*p32 & gm) >> gs); *output++ = ((*p32 & bm) >> bs); p32++; // ignore alpha values } // // eat paded bytes, for better speed we use shifting, // (bytes_per_line - bits_per_pixel / 8 * width ) / 4 // p32 += (input->bytes_per_line - (input->bits_per_pixel >> 3) * input->width) >> 2; } fp2 = fopen("/tmp/pic.rgb.pnm", "w"); fwrite(head, strlen(head), 1, fp2); // // x2ffmpeg_dump_ximage_info (input, fp2); // fwrite(ptr2, size, 1, fp2); fclose(fp2); free(ptr2); #ifdef DEBUG printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG #undef DEBUGFUNCTION } // // dump 8 bit image to ppm // this is just used for debugging purposes // void dump8bit(XImage * image, u_int32_t * ct) { #define DEBUGFUNCTION "dump8bit()" static char head[256]; static unsigned int image_size; register unsigned char *line_ptr, *col_ptr; unsigned char *pnm_image = NULL; int row, col; static FILE *fp2 = NULL; #ifdef DEBUG printf("%s %s: Entering with image %p\n", DEBUGFILE, DEBUGFUNCTION, image); #endif // DEBUG sprintf(head, "P6\n%d %d\n%d\n", image->width, image->height, 255); image_size = image->width * 3 * image->height; // RGB pnm_image = (unsigned char *) malloc(image_size); fp2 = fopen("/tmp/pic.rgb.pnm", "w"); fwrite(head, strlen(head), 1, fp2); line_ptr = pnm_image; for (row = 0; row < image->height; row++) { col_ptr = (unsigned char *) image->data + (row * image->bytes_per_line); for (col = 0; col < image->width; col++) { *line_ptr++ = ((ct[*col_ptr] & 0x00FF0000) >> 16); *line_ptr++ = ((ct[*col_ptr] & 0x0000FF00) >> 8); *line_ptr++ = (ct[*col_ptr] & 0x000000FF); col_ptr++; } } fwrite(pnm_image, image_size, 1, fp2); // // x2ffmpeg_dump_ximage_info (input, fp2); // fclose(fp2); free(pnm_image); #ifdef DEBUG printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION); #endif // DEBUG #undef DEBUGFUNCTION } #endif // DEBUG #endif // USE_FFMPEG