ffmpeg多线程编解码

215 篇文章 4 订阅

 

http://www.voidcn.com/article/p-ktgfhmqk-gt.html

ffmpeg多线程调研

1 解码

经调研,ffmpeg对h264, hevc多线程解码,提供配置线程数的接口,如下红色部分, AV_CODEC_CAP_SLICE_THREADS为支持片级多线程,AV_CODEC_CAP_FRAME_THREADS为支持帧级多线程。

AVCodec ff_h264_decoder = {

   .name                  ="h264",

   .long_name             =NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),

   .type                  =AVMEDIA_TYPE_VIDEO,

   .id                    =AV_CODEC_ID_H264,

   .priv_data_size        =sizeof(H264Context),

   .init                  =ff_h264_decode_init,

   .close                 =h264_decode_end,

   .decode                =h264_decode_frame,

   .capabilities          =/*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |

                            AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |

                            AV_CODEC_CAP_FRAME_THREADS,

   .caps_internal         =FF_CODEC_CAP_INIT_THREADSAFE,

   .flush                 =flush_dpb,

   .init_thread_copy      =ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),

   .update_thread_context =ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),

   .profiles              =NULL_IF_CONFIG_SMALL(ff_h264_profiles),

   .priv_class            =&h264_class,

};

AVCodec ff_hevc_decoder = {

   .name                  ="hevc",

   .long_name             =NULL_IF_CONFIG_SMALL("HEVC (High Efficiency Video Coding)"),

   .type                  =AVMEDIA_TYPE_VIDEO,

   .id                    =AV_CODEC_ID_HEVC,

    .priv_data_size        = sizeof(HEVCContext),

   .priv_class            =&hevc_decoder_class,

   .init                  =hevc_decode_init,

   .close                 =hevc_decode_free,

   .decode                =hevc_decode_frame,

   .flush                 = hevc_decode_flush,

   .update_thread_context = hevc_update_thread_context,

   .init_thread_copy      =hevc_init_thread_copy,

   .capabilities          =AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |

                             AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS,

   .profiles              =NULL_IF_CONFIG_SMALL(ff_hevc_profiles),

};

以H264解码为例的测试情况如下:

机器:Intel(R) Xeon(R) CPU          E5620  @ 2.40GHz

CPU: 2CPU,1个CPU共4核,逻辑CPU的个数为16。

测试命令:

time ffmpeg -threads n  -thread_type n -i  /home/rec/264/s.264 -y    /home/rec/264/1.yuv

thread_type为帧加速(1)的测试结果如下:

线程数

用时

0(自动16)

real   0m27.831s           user   1m39.554s          sys   0m7.999s

1

real   0m50.932s     user  0m44.639s      sys   0m3.462s

2

real   0m50.616s     user  1m13.673s     sys   0m6.223s   

3

real   0m42.813s         user  1m14.316s             sys   0m6.542s

4

real   0m42.813s         user  1m14.316s             sys   0m6.542s

5

real   0m38.283s           user  1m14.257s             sys   0m6.368s

8

real   0m34.117s           user  1m22.018s             sys   0m6.985s

12

real   0m28.414s           user  1m35.914s             sys   0m7.889s

16

real   0m28.770s           user  1m39.370s             sys   0m8.875s

18

real   0m28.102s           user  1m41.025s             sys   0m8.331s

20

real   0m25.982s           user  1m41.025s             sys   0m8.331s

 

依上表有如下结论:

1 在多核的情况下,多线程解码比单线程解码用时要少。

2 线程并不是越多越好。

2编码

ffmpeg对h264, hevc多线程编码,不提供配置线程数的接口,如下红色部分,AVCodec ff_libx264_encoder = {

   .name             ="libx264",

   .long_name        =NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part10"),

   .type             =AVMEDIA_TYPE_VIDEO,

    .id               = AV_CODEC_ID_H264,

   .priv_data_size   =sizeof(X264Context),

   .init             = X264_init,

   .encode2          = X264_frame,

   .close            = X264_close,

   .capabilities     =AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS,

   .priv_class       =&x264_class,

   .defaults         = x264_defaults,

   .init_static_data = X264_init_static,

   .caps_internal    =FF_CODEC_CAP_INIT_THREADSAFE |

                       FF_CODEC_CAP_INIT_CLEANUP,

};

 

AVCodec ff_libx265_encoder = {

   .name             ="libx265",

   .long_name        =NULL_IF_CONFIG_SMALL("libx265 H.265 / HEVC"),

   .type             =AVMEDIA_TYPE_VIDEO,

   .id               =AV_CODEC_ID_HEVC,

   .init             = libx265_encode_init,

   .init_static_data = libx265_encode_init_csp,

   .encode2          =libx265_encode_frame,

   .close            =libx265_encode_close,

   .priv_data_size   =sizeof(libx265Context),

   .priv_class       = &class,

   .defaults         = x265_defaults,

   .capabilities     =AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS,

};

 

相关文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用FFmpeg进行多线程硬解码的示例代码: ```c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <libavcodec/avcodec.h> #include <libavutil/frame.h> #include <libavutil/hwcontext.h> #include <libavutil/pixdesc.h> #define THREAD_COUNT 4 typedef struct DecoderContext { AVCodecContext *codec_ctx; AVBufferRef *hw_device_ctx; AVFrame *decoded_frame; int video_stream_index; } DecoderContext; void *decode_thread(void *arg) { DecoderContext *ctx = (DecoderContext *)arg; AVPacket packet; int ret; int got_frame; AVFrame *frame = av_frame_alloc(); while (1) { ret = av_read_frame(ctx->codec_ctx->pb, &packet); if (ret < 0) { break; } if (packet.stream_index == ctx->video_stream_index) { ret = avcodec_send_packet(ctx->codec_ctx, &packet); if (ret < 0) { av_packet_unref(&packet); break; } while (1) { ret = avcodec_receive_frame(ctx->codec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { av_packet_unref(&packet); av_frame_unref(frame); goto fail; } if (frame->format == AV_PIX_FMT_CUDA) { printf("Thread %d decoded a CUDA frame\n", (int)pthread_self()); } av_frame_unref(frame); } } av_packet_unref(&packet); } fail: av_frame_free(&frame); return NULL; } int main(int argc, char **argv) { av_register_all(); avcodec_register_all(); AVFormatContext *fmt_ctx = NULL; int ret = avformat_open_input(&fmt_ctx, argv[1], NULL, NULL); if (ret < 0) { printf("Error opening input file: %s\n", av_err2str(ret)); return ret; } ret = avformat_find_stream_info(fmt_ctx, NULL); if (ret < 0) { printf("Error finding stream information: %s\n", av_err2str(ret)); avformat_close_input(&fmt_ctx); return ret; } int video_stream_index = -1; AVCodec *codec = NULL; AVCodecContext *codec_ctx = NULL; AVDictionary *opts = NULL; for (int i = 0; i < fmt_ctx->nb_streams; i++) { if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_stream_index = i; codec = avcodec_find_decoder_by_name("h264_cuvid"); if (!codec) { printf("Error creating decoder\n"); avformat_close_input(&fmt_ctx); return -1; } codec_ctx = avcodec_alloc_context3(codec); if (!codec_ctx) { printf("Error allocating codec context\n"); avformat_close_input(&fmt_ctx); return -1; } avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[i]->codecpar); codec_ctx->thread_count = THREAD_COUNT; codec_ctx->thread_type = FF_THREAD_FRAME | FF_THREAD_SLICE; av_dict_set(&opts, "threads", "auto", 0); av_dict_set(&opts, "refcounted_frames", "1", 0); if (avcodec_open2(codec_ctx, codec, &opts) < 0) { printf("Error opening codec\n"); avcodec_free_context(&codec_ctx); avformat_close_input(&fmt_ctx); return -1; } break; } } if (video_stream_index == -1) { printf("No video stream found\n"); avcodec_free_context(&codec_ctx); avformat_close_input(&fmt_ctx); return -1; } AVBufferRef *hw_device_ctx = NULL; AVHWDeviceType hw_device_type = av_hwdevice_find_type_by_name("cuda"); if (hw_device_type == AV_HWDEVICE_TYPE_NONE) { printf("No CUDA device found\n"); avcodec_free_context(&codec_ctx); avformat_close_input(&fmt_ctx); return -1; } ret = av_hwdevice_ctx_create(&hw_device_ctx, hw_device_type, NULL, NULL, 0); if (ret < 0) { printf("Error creating CUDA device context: %s\n", av_err2str(ret)); avcodec_free_context(&codec_ctx); avformat_close_input(&fmt_ctx); return ret; } DecoderContext *ctx = (DecoderContext *)malloc(sizeof(DecoderContext) * THREAD_COUNT); pthread_t *threads = (pthread_t *)malloc(sizeof(pthread_t) * THREAD_COUNT); for (int i = 0; i < THREAD_COUNT; i++) { ctx[i].codec_ctx = avcodec_alloc_context3(codec); avcodec_parameters_to_context(ctx[i].codec_ctx, fmt_ctx->streams[video_stream_index]->codecpar); ctx[i].hw_device_ctx = av_buffer_ref(hw_device_ctx); ctx[i].codec_ctx->hw_device_ctx = av_buffer_ref(ctx[i].hw_device_ctx); ctx[i].video_stream_index = video_stream_index; ctx[i].decoded_frame = av_frame_alloc(); if (pthread_create(&threads[i], NULL, decode_thread, &ctx[i]) != 0) { printf("Error creating thread\n"); avcodec_free_context(&codec_ctx); avformat_close_input(&fmt_ctx); return -1; } } for (int i = 0; i < THREAD_COUNT; i++) { pthread_join(threads[i], NULL); } for (int i = 0; i < THREAD_COUNT; i++) { avcodec_free_context(&ctx[i].codec_ctx); av_buffer_unref(&ctx[i].hw_device_ctx); av_frame_free(&ctx[i].decoded_frame); } av_buffer_unref(&hw_device_ctx); avcodec_free_context(&codec_ctx); avformat_close_input(&fmt_ctx); return 0; } ``` 这个示例代码使用了4个线程进行解码。每个线程都有一个`DecoderContext`结构体,其中包含了一个`AVCodecContext`结构体,一个硬件设备上下文`AVBufferRef`和一个解码后的帧`AVFrame`。在主线程中,我们为每个线程分配一个`DecoderContext`,然后启动线程并等待线程结束。在线程中,我们使用`avcodec_send_packet`和`avcodec_receive_frame`函数来进行硬解码,并在解码成功后打印一条消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值