2015-01-07 40 views
1

我想使用ffmpeg api顯示裁剪和縮放幀,並且我正在使用GTK + 3作爲GUI組件。從以下this tutorialffmpeg examples,我能夠顯示未過濾的幀,雖然有一些不穩定。過濾後的幀根本無法正確顯示。它主要產生完全黑色的輸出。我懷疑這是由於sws_scale(),但我還沒有發現爲什麼會發生這種情況。使用GTK顯示已過濾的ffmpeg幀

使用ffmpeg示例中的「平凡」顯示,我可以確認該幀正在被裁剪和正確縮放。

運行下面的代碼,我收到了一堆錯誤:

[swscaler @ 0x7fb58b025400] bad src image pointers 
[swscaler @ 0x7fb58b025400] bad dst image pointers 

我也收到此錯誤:

[swscaler @ 0x7fd05c025600] Warning: data is not aligned! This can lead to a speedloss 

我試圖使一個緩衝,這是16位對齊的,但事實並非如此似乎對結果有任何影響。

這是我的解碼框架和應用過濾器:

void decode(gpointer args) { 
    int ret; 
    AVPacket packet; 
    AVFrame *frame = av_frame_alloc(); 
    AVFrame *filt_frame = av_frame_alloc(); 
    int got_frame; 

    if(!frame || !filt_frame) { 
     perror("Could not allocate frame"); 
     exit(1); 
    } 

    /* read all packets */ 
    while (1) { 
     if ((ret = av_read_frame(fmt_ctx, &packet)) < 0) 
      break; 
     if (packet.stream_index == video_stream_index) { 
      got_frame = 0; 
      ret = avcodec_decode_video2(dec_ctx, frame, &got_frame, &packet); 
      if (ret < 0) { 
       av_log(NULL, AV_LOG_ERROR, "Error decoding video\n"); 
       break; 
      } 
      if (got_frame) { 
       frame->pts = av_frame_get_best_effort_timestamp(frame); 
       /* push the decoded frame into the filtergraph */ 
       if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) { 
        av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n"); 
        break; 
       } 
       /* pull filtered frames from the filtergraph */ 
       while (1) { 
        ret = av_buffersink_get_frame(buffersink_ctx, filt_frame); 
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) 
         break; 
        if (ret < 0) 
         goto end; 
        display_frame2(filt_frame, buffersink_ctx->inputs[0]->time_base); 
        av_frame_unref(filt_frame); 
       } 
       av_frame_unref(frame); 
      } 
     } 
     av_free_packet(&packet); 
    } 
end: 
    avfilter_graph_free(&filter_graph); 
    avcodec_close(dec_ctx); 
    avformat_close_input(&fmt_ctx); 
    av_frame_free(&frame); 
    av_frame_free(&filt_frame); 
    if (ret < 0 && ret != AVERROR_EOF) { 
     fprintf(stderr, "Error occurred: %s\n", av_err2str(ret)); 
     exit(1); 
    } 

} 

而且我這是怎麼顯示的幀。

void display_frame2(const AVFrame *frame, AVRational time_base) { 
    GdkPixbuf *pixbuf; 
    int64_t delay; 
    AVFrame *filt; 
    uint8_t *buffer; 
    int num_bytes, i; 
    buffer = NULL; 


    filt = av_frame_alloc(); 
    num_bytes = avpicture_get_size(PIX_FMT_RGB24, dec_ctx->width, dec_ctx->height); 
    buffer = (uint8_t *)av_malloc(num_bytes * sizeof(uint8_t)); 
    avpicture_fill((AVPicture *)filt, buffer, PIX_FMT_RGB24, dec_ctx->width, dec_ctx->height); 

    if (frame->pts != AV_NOPTS_VALUE) { 
     if (last_pts != AV_NOPTS_VALUE) { 
      /* sleep roughly the right amount of time; 
      * usleep is in microseconds, just like AV_TIME_BASE. */ 
      delay = av_rescale_q(frame->pts - last_pts, 
           time_base, AV_TIME_BASE_Q); 
      if (delay > 0 && delay < 1000000) 
       usleep(delay); 
     } 
     last_pts = frame->pts; 
    } 

    sws_scale( sws_ctx, 
       (uint8_t const * const *)frame->data, 
       frame->linesize, 
       0, 
       frame->height, 
       filt->data, 
       filt->linesize); 
    pixbuf = gdk_pixbuf_new_from_data( filt->data[0], GDK_COLORSPACE_RGB, 
             0, 8, dec_ctx->width, dec_ctx->height, 
             filt->linesize[0], NULL, NULL); 
    gtk_image_set_from_pixbuf((GtkImage *)image, pixbuf); 
    free(filt); 
    free(buffer); 

} 

編輯: 經過一番更多的思考和實驗得到了我要顯示的過濾框架,儘管在SDL,不是GTK +。我使用ffmpeg的代碼轉換示例來了解是否可以使用過濾器對視頻進行重新編碼,而且確實有效。在這個例子中,我基本上改變了filtergtaph的過濾器,大部分工作已經完成。此時,我所做的只是使用SDL顯示視頻,如危險教程中所示。裁剪過濾器會創建很多工件,但至少會顯示某些內容。

我必須做一些更多的工作來看看它是否可以與GTK +一起工作。我沒有仔細研究上述程序和轉碼示例中的區別,所以我還沒有弄清楚爲什麼我的舊代碼不顯示過濾的幀。這兩組代碼都使用sws_scale(),但我沒有得到新代碼的錯誤,所以這必定意味着有些不同。一旦我取得更多進展,我會更新這篇文章。

編輯2: 根據@ drahnr的請求添加了一個可編譯的小例子,它應該可以工作。我沒有機會嘗試替換GtkPixbuf。

#define _XOPEN_SOURCE 600 
#include <libavformat/avformat.h> 
#include <libavformat/avio.h> 
#include <libavfilter/avfiltergraph.h> 
#include <libavfilter/avcodec.h> 
#include <libavfilter/buffersink.h> 
#include <libavfilter/buffersrc.h> 
#include <libavcodec/avcodec.h> 
#include <libswscale/swscale.h> 
#include <libavutil/avstring.h> 
#include <libavutil/time.h> 
#include <libavutil/opt.h> 
#include <unistd.h> 

#include <gtk/gtk.h> 
#include <gdk/gdkx.h> 


GtkWidget *image; 
GtkWidget *window; 

struct SwsContext *sws_ctx; 
char *filter_descr = "crop=100:100,scale=640:360"; 
static AVFormatContext *fmt_ctx; 
static AVCodecContext *dec_ctx; 
AVFilterContext *buffersink_ctx; 
AVFilterContext *buffersrc_ctx; 
AVFilterGraph *filter_graph; 
static int video_stream_index = -1; 

static void open_input_file(const char *filename) 
{ 
    AVCodec *dec; 
    avformat_open_input(&fmt_ctx, filename, NULL, NULL); 
    avformat_find_stream_info(fmt_ctx, NULL); 
    video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0); 
    dec_ctx = fmt_ctx->streams[video_stream_index]->codec; 
    av_opt_set_int(dec_ctx, "refcounted_frames", 1, 0); 
    avcodec_open2(dec_ctx, dec, NULL); 
} 


static void init_filters(const char *filters_descr) 
{ 
    char args[512]; 
    AVFilter *buffersrc = avfilter_get_by_name("buffer"); 
    AVFilter *buffersink = avfilter_get_by_name("buffersink"); 
    AVFilterInOut *outputs = avfilter_inout_alloc(); 
    AVFilterInOut *inputs = avfilter_inout_alloc(); 
    AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base; 
    enum AVPixelFormat pix_fmts[] = { PIX_FMT_RGB24, AV_PIX_FMT_NONE }; 
    filter_graph = avfilter_graph_alloc(); 
    snprintf(args, sizeof(args), 
      "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", 
      dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, 
      time_base.num, time_base.den, 
      dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den); 
    avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph); 
    avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, NULL, filter_graph); 
    av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); 
    outputs->name  = av_strdup("in"); 
    outputs->filter_ctx = buffersrc_ctx; 
    outputs->pad_idx = 0; 
    outputs->next  = NULL; 
    inputs->name  = av_strdup("out"); 
    inputs->filter_ctx = buffersink_ctx; 
    inputs->pad_idx = 0; 
    inputs->next  = NULL; 
    avfilter_graph_parse_ptr(filter_graph, filters_descr, &inputs, &outputs, NULL); 
    avfilter_graph_config(filter_graph, NULL); 
} 

static void display_frame2(const AVFrame *frame, AVRational time_base) { 
    GdkPixbuf *pixbuf; 

    AVFrame *filt; 
    uint8_t *buffer; 
    int num_bytes; 
    buffer = NULL; 

    filt = av_frame_alloc(); 
    num_bytes = avpicture_get_size(PIX_FMT_RGB24, dec_ctx->width, dec_ctx->height); 
    buffer = (uint8_t *)av_malloc(num_bytes * sizeof(uint8_t)); 
    avpicture_fill((AVPicture *)filt, buffer, PIX_FMT_RGB24, dec_ctx->width, dec_ctx->height); 
    usleep(33670/4); 
    sws_scale( sws_ctx, 
       (uint8_t const * const *)frame->data, 
       frame->linesize, 
       0, 
       frame->height, 
       filt->data, 
       filt->linesize); 
    pixbuf = gdk_pixbuf_new_from_data( filt->data[0], GDK_COLORSPACE_RGB, 
             0, 8, dec_ctx->width, dec_ctx->height, 
             filt->linesize[0], NULL, NULL); 
    gtk_image_set_from_pixbuf((GtkImage *)image, pixbuf); 
    free(filt); 
    free(buffer); 

} 


void decode(gpointer args) { 
    int ret; 
    AVPacket packet; 
    AVFrame *frame  = av_frame_alloc(); 
    AVFrame *filt_frame = av_frame_alloc(); 
    int got_frame; 

    while (1) { 
     av_read_frame(fmt_ctx, &packet); 
     if (packet.stream_index == video_stream_index) { 
      got_frame = 0; 
      avcodec_decode_video2(dec_ctx, frame, &got_frame, &packet); 
      if (got_frame) { 
       frame->pts = av_frame_get_best_effort_timestamp(frame); 
       if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) { 
        av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n"); 
        break; 
       } 
       while (1) { 
        ret = av_buffersink_get_frame(buffersink_ctx, filt_frame); 
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) 
         break; 
        // Display original frame 
        display_frame2(frame, buffersink_ctx->inputs[0]->time_base); 
        // Display filtered frame 
        // display_frame2(filt_frame, buffersink_ctx->inputs[0]->time_base); 
        av_frame_unref(filt_frame); 
       } 
       av_frame_unref(frame); 
      } 
     } 
     av_free_packet(&packet); 
    } 

} 

static void realize_cb(GtkWidget *widget, gpointer data) { 
    GThread *tid; 
    tid = g_thread_new("video", decode, NULL); 
} 

static void destroy(GtkWidget *widget, gpointer data) { 
     gtk_main_quit(); 
} 

int main(int argc, char **argv) 
{ 
    av_register_all(); 
    avfilter_register_all(); 
    open_input_file(argv[1]); 
    init_filters(filter_descr); 
    sws_ctx = NULL; 
    sws_ctx = sws_getContext( dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, dec_ctx->width, dec_ctx->height, 
            PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL); 
    av_dump_format(fmt_ctx, 0, argv[1], 0); 

    gtk_init(&argc, &argv); 
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 
    g_signal_connect(window, "realize", G_CALLBACK(realize_cb), NULL); 
    g_signal_connect(window, "destroy", G_CALLBACK(destroy), NULL); 
    gtk_container_set_border_width(GTK_CONTAINER(window), 10); 
    image = gtk_image_new(); 
    gtk_widget_show(image); 
    gtk_container_add(GTK_CONTAINER(window), image); 
    gtk_widget_show(window); 
    gtk_main(); 
    return 0; 
} 
+0

你究竟在哪裏指定緩衝區?你爲什麼要把它調整到16bits?我期望16 **字節**對齊SSE的樂趣。 – drahnr

+0

@drahnr在'displayFrame2()'緩衝區是我聲明的第四個變量。我用這個緩衝區=(uint8_t *)av_malloc(num_bytes * sizeof(uint8_t));'它正在使用像這樣''avpicture_fill((AVPicture *)filt,buffer,PIX_FMT_RGB24,dec_ctx->寬,dec_ctx-> height);'This [post](http://forum.doom9.org/archive/index.php/t-167010.html)表示緩衝區需要16位對齊。使緩衝區成爲16的倍數似乎沒有什麼區別。 – Fjotten

+0

今晚我會看看它,但沒有承諾。 – drahnr

回答

0

正確的方式做,這是使用cairo_surface_t和裸機GtkDrawingArea。獲取其大小分配,相應地縮放內容(這裏是:您的視頻幀)。

那麼你什麼時候想畫?當然不是你現在所做的。

同步到幀時鐘,並讀取根據文檔頁面(是的,這是工作,但它會幫助你不少)https://developer.gnome.org/gdk3/stable/GdkFrameClock.html#gdk-frame-clock-begin-updating

和進度重繪每當一個新的幀準備好顯示。


主要錯誤我與你的上述程序看到的是,你從一個線程是不運行的,因爲只有g/gtk_電話是線程一個非常小的子集不允許gtk_main/g_event_loop線程訪問UI元素GtkImage *image安全。其中一個規避的是g_idle_add,它將數據回調排入主循環(在gtk文檔中搜索GSource)。


對於對齊,我不知道這裏有什麼問題,我也posix_memalign512字節對齊時,它得到了警告,甚至

編輯:,因爲它看來,你的步幅也必須是所需對齊的16個字節的倍數。


開始clean draft widget implementation at github,雖然這還不編譯 - 但它畢竟勾勒出漂亮的做法顯然。

+0

非常感謝您花時間看這個!我受到其他功能的影響。 – Fjotten