[gimp-gap] minor fixes for the ffmpeg based video encoder
- From: Wolfgang Hofer <wolfgangh src gnome org>
- To: svn-commits-list gnome org
- Subject: [gimp-gap] minor fixes for the ffmpeg based video encoder
- Date: Fri, 24 Apr 2009 13:56:15 -0400 (EDT)
commit ab1a224329fdec21d40022cf7f26314810f62ed8
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date: Fri Apr 24 19:50:59 2009 +0200
minor fixes for the ffmpeg based video encoder
---
ChangeLog | 25 +++
vid_common/gap_cme_callbacks.c | 50 ++++++-
vid_common/gap_cme_gui.c | 4 +-
vid_enc_ffmpeg/gap_enc_ffmpeg_main.c | 274 ++++++++++++++++++++++++---------
4 files changed, 271 insertions(+), 82 deletions(-)
diff --git a/ChangeLog b/ChangeLog
old mode 100644
new mode 100755
index 3f618e2..7cdd08d
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2009-04-24 Wolfgang Hofer <hof gimp org>
+
+- ffmpeg based video encoder colormodel conversion to YUV420P is only done
+ for codecs that require this as input colormodel.
+ For codecs that do not want YUV420P
+ use ffmpeg's img_convert procedure to convert from rgb.
+ (old implementation always used YUV420P as input for the conversion
+ which may results in loss of quality)
+
+- ffmpeg based video encoder now terminates with error message
+ in case the auido input is not supported by the selected audio codec.
+ (the old implementation crashed in such cases)
+ this typically occurs for non standard samplerates
+ therfore the error message includes the hint to try
+ 48 kHz, 44.1 kHz or 32 KHz
+
+- master video encoder combo box for selection of commonly used samplerate
+ is now updated automatically when a commonly used samplerate is entered
+ in the corresponding spinbutton widget.
+
+ * vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
+
+ * vid_common/gap_cme_callbacks.c
+ * vid_common/gap_cme_gui.c
+
2009-04-14 Wolfgang Hofer <hof gimp org>
- fixed audio extract problem that delivered unusable results
diff --git a/vid_common/gap_cme_callbacks.c b/vid_common/gap_cme_callbacks.c
index e50fa0f..a451ee4 100644
--- a/vid_common/gap_cme_callbacks.c
+++ b/vid_common/gap_cme_callbacks.c
@@ -442,7 +442,12 @@ on_cme__combo_outsamplerate (GtkWidget *widget,
gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
- if(gap_debug) printf("CB: on_cme__combo_outsamplerate value: %d\n", (int)value);
+ if(gap_debug)
+ {
+ printf("CB: on_cme__combo_outsamplerate value: %d\n", (int)value);
+ }
+
+
if(gpp->cme__spinbutton_samplerate_adj)
{
@@ -462,7 +467,7 @@ on_cme__combo_vid_norm (GtkWidget *widget,
{
gint value;
- if(gap_debug) printf("CB: on_cme__combo_outsamplerate\n");
+ if(gap_debug) printf("CB: on_cme__combo_vid_norm\n");
if(gpp == NULL) return;
@@ -967,13 +972,46 @@ on_cme__spinbutton_samplerate_changed (GtkEditable *editable,
adj = GTK_ADJUSTMENT(gpp->cme__spinbutton_samplerate_adj);
if(adj)
{
- if(gap_debug) printf("samplerate spin value: %f\n", (float)adj->value );
-
- if((gint)adj->value != gpp->val.samplerate)
+ gint gintValue;
+
+ if(gap_debug)
{
- gpp->val.samplerate = (gint)adj->value;
+ printf("samplerate spin value: %f\n", (float)adj->value );
+ }
+ gintValue = (gint)adj->value;
+
+ if(gintValue != gpp->val.samplerate)
+ {
+ gpp->val.samplerate = gintValue;
gap_cme_gui_update_aud_labels (gpp);
}
+
+ switch (gintValue)
+ {
+ case 8000:
+ case 11025:
+ case 12000:
+ case 16000:
+ case 22050:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ if (gpp->cme__combo_outsamplerate != NULL)
+ {
+ if(gap_debug)
+ {
+ printf("detected a commonly used samplerate value: %d\n", (int)gintValue );
+ }
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (gpp->cme__combo_outsamplerate)
+ , gintValue);
+ }
+ break;
+ default:
+ break;
+ }
+
+
}
}
diff --git a/vid_common/gap_cme_gui.c b/vid_common/gap_cme_gui.c
index 3daba39..4b12932 100644
--- a/vid_common/gap_cme_gui.c
+++ b/vid_common/gap_cme_gui.c
@@ -3418,7 +3418,9 @@ p_create_audio_options_frame (GapCmeGlobalParams *gpp)
gtk_table_attach (GTK_TABLE (table), button, 2, 3, row, row+1,
(GtkAttachOptions) (GTK_FILL),
(GtkAttachOptions) (0), 2, 0);
- gimp_help_set_help_data (button, _("Convert audiofile to tmpfile"), NULL);
+ gimp_help_set_help_data (button, _("Convert audio input file to a temporary file\n"
+ "and feed the temporary file to the selected encoder\n"
+ "(the temporary file is deleted when encoding is done)"), NULL);
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (on_cme__button_gen_tmp_audfile_clicked),
gpp);
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
index 01a4a4c..9ffb980 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
@@ -13,7 +13,7 @@
(Video only seems OK)
*/
-/* gap_enc_main_ffmpeg.c
+/* gap_enc_ffmpeg_main.c
* by hof (Wolfgang Hofer)
*
* GAP ... Gimp Animation Plugins
@@ -265,14 +265,17 @@ static gboolean p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
, gint ii
, GapGveFFMpegGlobalParams *gpp
, long audio_samplerate
- , long audio_channels);
+ , long audio_channels
+ , long bits
+ );
static t_ffmpeg_handle * p_ffmpeg_open(GapGveFFMpegGlobalParams *gpp
, gint32 current_pass
, t_awk_array *awp
, gint video_tracks
);
static int p_ffmpeg_write_frame_chunk(t_ffmpeg_handle *ffh, gint32 encoded_size, gint vid_track);
-static int p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, gboolean force_keyframe, gint vid_track);
+static uint8_t * p_convert_colormodel(t_ffmpeg_handle *ffh, AVPicture *picture_codec, guchar *rgb_buffer, gint vid_track);
+static int p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, GimpDrawable *drawable, gboolean force_keyframe, gint vid_track);
static int p_ffmpeg_write_audioframe(t_ffmpeg_handle *ffh, guchar *audio_buf, int frame_bytes, gint aud_track);
static void p_ffmpeg_close(t_ffmpeg_handle *ffh);
static gint p_ffmpeg_encode(GapGveFFMpegGlobalParams *gpp);
@@ -1349,16 +1352,21 @@ p_sound_precalculations(t_ffmpeg_handle *ffh
if(ffh->ast[ii].aud_codec_context)
{
/* the audio codec gives us the correct audio frame size, in samples */
- if (gap_debug) printf("Codec ontext frame_size[%d]:%d channels:%d\n"
+ if (gap_debug)
+ {
+ printf("Audio Codec context frame_size[%d]:%d channels:%d\n"
, (int)ii
, (int)ffh->ast[ii].aud_codec_context->frame_size
, (int)ffh->ast[ii].aud_codec_context->channels
);
-
+ }
awp->awk[ii].audio_margin = ffh->ast[ii].aud_codec_context->frame_size * 2 * ffh->ast[ii].aud_codec_context->channels;
}
- if(gap_debug) printf("audio_margin[%d]: %d\n", (int)ii, (int)awp->awk[ii].audio_margin);
+ if(gap_debug)
+ {
+ printf("audio_margin[%d]: %d\n", (int)ii, (int)awp->awk[ii].audio_margin);
+ }
}
@@ -2268,13 +2276,20 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
, gint ii
, GapGveFFMpegGlobalParams *gpp
, long audio_samplerate
- , long audio_channels)
+ , long audio_channels
+ , long bits
+ )
{
GapGveFFMpegValues *epp;
AVCodecContext *audio_enc = NULL;
+ gboolean audioOK;
+ char *msg;
epp = &gpp->evl;
+ audioOK = TRUE;
+ msg = NULL;
+
/* ------------ Start Audio CODEC init ------- */
if(audio_channels > 0)
{
@@ -2283,13 +2298,15 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
ffh->ast[ii].aud_codec = avcodec_find_encoder_by_name(epp->acodec_name);
if(!ffh->ast[ii].aud_codec)
{
- printf("Unknown Audio CODEC: %s\n", epp->acodec_name);
+ audioOK = FALSE;
+ msg = g_strdup_printf(_("Unknown Audio CODEC: %s"), epp->acodec_name);
}
else
{
if(ffh->ast[ii].aud_codec->type != CODEC_TYPE_AUDIO)
{
- printf("CODEC: %s is no VIDEO CODEC!\n", epp->acodec_name);
+ audioOK = FALSE;
+ msg = g_strdup_printf(_("CODEC: %s is no AUDIO CODEC!"), epp->acodec_name);
ffh->ast[ii].aud_codec = NULL;
}
else
@@ -2315,33 +2332,71 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
audio_enc->channels = audio_channels; /* 1=mono, 2 = stereo (from wav file) */
- /* open audio codec */
- if (avcodec_open(ffh->ast[ii].aud_codec_context, ffh->ast[ii].aud_codec) < 0)
- {
- if(gpp->val.run_mode == GIMP_RUN_NONINTERACTIVE)
- {
- printf("could not avcodec_open audio-codec: %s\n", epp->acodec_name);
- }
- else
- {
- g_message("could not open audio codec: %s\n", epp->acodec_name);
- }
- ffh->ast[ii].aud_codec = NULL;
- }
-
/* the following options were added after ffmpeg 0.4.8 */
audio_enc->thread_count = epp->thread_count;
audio_enc->strict_std_compliance = epp->strict;
audio_enc->workaround_bugs = epp->workaround_bugs;
audio_enc->error_recognition = epp->error_recognition;
audio_enc->cutoff = epp->cutoff;
- audio_enc->channel_layout = epp->channel_layout;
+
+ if (bits == 16)
+ {
+ audio_enc->sample_fmt = SAMPLE_FMT_S16;
+ }
+ if (bits == 8)
+ {
+ audio_enc->sample_fmt = SAMPLE_FMT_U8;
+ }
+
+
+ switch (audio_channels)
+ {
+ case 1:
+ audio_enc->channel_layout = CH_LAYOUT_MONO;
+ break;
+ case 2:
+ audio_enc->channel_layout = CH_LAYOUT_STEREO; /* CH_LAYOUT_STEREO_DOWNMIX ? */
+ break;
+ default:
+ audio_enc->channel_layout = epp->channel_layout;
+ break;
+
+ }
+
+ /* open audio codec */
+ if (avcodec_open(ffh->ast[ii].aud_codec_context, ffh->ast[ii].aud_codec) < 0)
+ {
+ audioOK = FALSE;
+
+ msg = g_strdup_printf(_("could not open audio codec: %s\n"
+ "at audio_samplerate:%d channels:%d bits per channel:%d\n"
+ "(try to convert to 48 KHz, 44.1KHz or 32 kHz samplerate\n"
+ "that is supported by most codecs)")
+ , epp->acodec_name
+ , (int)audio_samplerate
+ , (int)audio_channels
+ , (int)bits
+ );
+ ffh->ast[ii].aud_codec = NULL;
+ }
}
}
}
- return (TRUE); /* OK */
+ if (msg != NULL)
+ {
+ if(gpp->val.run_mode == GIMP_RUN_NONINTERACTIVE)
+ {
+ printf("** Error: %s\n", msg);
+ }
+ else
+ {
+ g_message(msg);
+ }
+ g_free(msg);
+ }
+ return (audioOK);
} /* end p_init_and_open_audio_codec */
@@ -2481,6 +2536,7 @@ p_ffmpeg_open(GapGveFFMpegGlobalParams *gpp
, gpp
, awp->awk[ii].sample_rate
, awp->awk[ii].channels
+ , awp->awk[ii].bits
);
if(!codec_ok)
{
@@ -2722,6 +2778,92 @@ p_ffmpeg_write_frame_chunk(t_ffmpeg_handle *ffh, gint32 encoded_size, gint vid_t
} /* end p_ffmpeg_write_frame_chunk */
+/* -----------------------
+ * p_convert_colormodel
+ * -----------------------
+ * convert video frame specified in the rgb_buffer
+ * from PIX_FMT_BGR24 to the colormodel that is required
+ * by the video codec.
+ *
+ * conversion is done based on ffmpegs img_convert procedure.
+ */
+static uint8_t *
+p_convert_colormodel(t_ffmpeg_handle *ffh, AVPicture *picture_codec, guchar *rgb_buffer, gint vid_track)
+{
+ AVFrame *big_picture_rgb;
+ AVPicture *picture_rgb;
+ uint8_t *l_convert_buffer;
+ int ii;
+ int l_rc;
+
+ ii = ffh->vst[vid_track].video_stream_index;
+
+ /* source picture (RGB) */
+ big_picture_rgb = avcodec_alloc_frame();
+ picture_rgb = (AVPicture *)big_picture_rgb;
+
+
+ /* allocate buffer for image conversion large enough for for uncompressed RGBA32 colormodel */
+ l_convert_buffer = g_malloc(4 * ffh->frame_width * ffh->frame_height);
+
+ if(gap_debug)
+ {
+ printf("HAVE TO convert TO pix_fmt: %d\n"
+ , (int)ffh->vst[ii].vid_codec_context->pix_fmt
+ );
+ }
+
+
+
+ /* init destination picture structure (the codec context tells us what pix_fmt is needed)
+ */
+ avpicture_fill(picture_codec
+ ,l_convert_buffer
+ ,ffh->vst[ii].vid_codec_context->pix_fmt /* PIX_FMT_RGB24, PIX_FMT_RGBA32, PIX_FMT_BGRA32 */
+ ,ffh->frame_width
+ ,ffh->frame_height
+ );
+ /* source picture (has RGB24 colormodel) */
+ avpicture_fill(picture_rgb
+ ,rgb_buffer
+ ,PIX_FMT_RGB24
+ ,ffh->frame_width
+ ,ffh->frame_height
+ );
+
+ /* AVFrame is the new structure introduced in FFMPEG 0.4.6,
+ * (same as AVPicture but with additional members at the end)
+ */
+
+ if(gap_debug)
+ {
+ printf("before img_convert pix_fmt: %d (YUV420:%d)\n"
+ , (int)ffh->vst[ii].vid_codec_context->pix_fmt
+ , (int)PIX_FMT_YUV420P);
+ }
+
+ /* convert to pix_fmt needed by the codec */
+ l_rc = img_convert(picture_codec, ffh->vst[ii].vid_codec_context->pix_fmt /* dst */
+ ,picture_rgb, PIX_FMT_BGR24 /* src */
+ ,ffh->frame_width
+ ,ffh->frame_height
+ );
+
+ if(gap_debug)
+ {
+ printf("after img_convert: l_rc:%d\n", l_rc);
+ }
+
+ g_free(big_picture_rgb);
+
+
+ if(gap_debug)
+ {
+ printf("DONE p_convert_colormodel\n");
+ }
+
+ return (l_convert_buffer);
+} /* end p_convert_colormodel */
/* --------------------
* p_ffmpeg_write_frame
@@ -2730,10 +2872,8 @@ p_ffmpeg_write_frame_chunk(t_ffmpeg_handle *ffh, gint32 encoded_size, gint vid_t
* the encoded frame to the mediafile as packet.
*/
static int
-p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, gboolean force_keyframe, gint vid_track)
+p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, GimpDrawable *drawable, gboolean force_keyframe, gint vid_track)
{
- AVFrame *big_picture_yuv;
- AVPicture *picture_yuv;
AVPicture *picture_codec;
int encoded_size;
int ret;
@@ -2771,17 +2911,20 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, gboolean force_keyframe, gint vid_tra
/* picture to feed the codec */
picture_codec = (AVPicture *)ffh->vst[ii].big_picture_codec;
- /* source picture (YUV420) */
- big_picture_yuv = avcodec_alloc_frame();
- picture_yuv = (AVPicture *)big_picture_yuv;
if(ffh->vst[ii].vid_codec_context->pix_fmt == PIX_FMT_YUV420P)
{
if(gap_debug)
{
- printf("USE YUV420 (no pix_fmt convert needed)\n");
+ printf("USE PIX_FMT_YUV420P (no pix_fmt convert needed)\n");
}
+
+
+ /* fill the yuv420_buffer with current frame image data */
+ gap_gve_raw_YUV420P_drawable_encode(drawable, ffh->vst[0].yuv420_buffer);
+
+
/* most of the codecs wants YUV420
* (we can use the picture in ffh->vst[ii].yuv420_buffer without pix_fmt conversion
*/
@@ -2794,53 +2937,39 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, gboolean force_keyframe, gint vid_tra
}
else
{
- /* allocate buffer for image conversion large enough for for uncompressed RGBA32 colormodel */
- l_convert_buffer = g_malloc(4 * ffh->frame_width * ffh->frame_height);
+ guchar *rgb_buffer;
+ gint32 rgb_size;
- if(gap_debug)
- {
- printf("HAVE TO convert pix_fmt: %d\n (PIX_FMT_YUV420P %d)"
- , (int)ffh->vst[ii].vid_codec_context->pix_fmt
- , (int)PIX_FMT_YUV420P
- );
- }
+ rgb_buffer = gap_gve_raw_RGB_drawable_encode(drawable, &rgb_size, FALSE /* no vflip */
+ , NULL /* app0_buffer */
+ , 0 /* app0_length */
+ );
-
- /* init destination picture structure (the codec context tells us what pix_fmt is needed)
- */
- avpicture_fill(picture_codec
- ,l_convert_buffer
- ,ffh->vst[ii].vid_codec_context->pix_fmt /* PIX_FMT_RGB24, PIX_FMT_RGBA32, PIX_FMT_BGRA32 */
- ,ffh->frame_width
- ,ffh->frame_height
- );
- /* source picture (has YUV420P colormodel) */
- avpicture_fill(picture_yuv
- ,ffh->vst[ii].yuv420_buffer
- ,PIX_FMT_YUV420P
+ if (ffh->vst[ii].vid_codec_context->pix_fmt == PIX_FMT_BGR24)
+ {
+ if(gap_debug)
+ {
+ printf("USE PIX_FMT_BGR24 (no pix_fmt convert needed)\n");
+ }
+ avpicture_fill(picture_codec
+ ,rgb_buffer
+ ,PIX_FMT_BGR24 /* PIX_FMT_RGB24, PIX_FMT_RGBA32, PIX_FMT_BGRA32 */
,ffh->frame_width
,ffh->frame_height
);
-
- /* AVFrame is the new structure introduced in FFMPEG 0.4.6,
- * (same as AVPicture but with additional members at the end)
- */
+ }
+ else
+ {
+ l_convert_buffer = p_convert_colormodel(ffh, picture_codec, rgb_buffer, vid_track);
+ }
+
if(gap_debug)
{
- printf("before img_convert pix_fmt: %d (YUV420:%d)\n"
- , (int)ffh->vst[ii].vid_codec_context->pix_fmt
- , (int)PIX_FMT_YUV420P);
+ printf("before g_free rgb_buffer\n");
}
- /* convert to pix_fmt needed by the codec */
- img_convert(picture_codec, ffh->vst[ii].vid_codec_context->pix_fmt /* dst */
- ,picture_yuv, PIX_FMT_BGR24 /* src */
- ,ffh->frame_width
- ,ffh->frame_height
- );
-
- if(gap_debug) printf("after img_convert\n");
+ g_free(rgb_buffer);
}
/* AVFrame is the new structure introduced in FFMPEG 0.4.6,
@@ -2955,7 +3084,6 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, gboolean force_keyframe, gint vid_tra
if(gap_debug) printf("before free picture structures\n");
- g_free(big_picture_yuv);
if(l_convert_buffer) g_free(l_convert_buffer);
@@ -3561,14 +3689,10 @@ p_ffmpeg_encode_pass(GapGveFFMpegGlobalParams *gpp, gint32 current_pass, GapGveM
);
}
-
- /* fill the yuv420_buffer with current frame image data */
- gap_gve_raw_YUV420P_drawable_encode(l_drawable, ffh->vst[0].yuv420_buffer);
-
/* store the compressed video frame */
if (gap_debug) printf("GAP_FFMPEG: Writing frame nr. %d\n", (int)l_cur_frame_nr);
- p_ffmpeg_write_frame(ffh, l_force_keyframe, 0 /* vid_track */);
+ p_ffmpeg_write_frame(ffh, l_drawable, l_force_keyframe, 0 /* vid_track */);
gimp_drawable_detach (l_drawable);
/* destroy the tmp image */
gimp_image_delete(l_tmp_image_id);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]