#include #include #include #include #include #include #include static enum AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE; static int check_hw_device_type(enum AVHWDeviceType type) { enum AVHWDeviceType t = AV_HWDEVICE_TYPE_NONE; while (1) { t = av_hwdevice_iterate_types(t); if (t == AV_HWDEVICE_TYPE_NONE) { break; } if (t == type) { return 0; } } return -1; } static enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) { for (size_t i = 0; pix_fmts[i] != AV_PIX_FMT_NONE; i++) { if (pix_fmts[i] == hw_pix_fmt) { return hw_pix_fmt; } } fprintf(stderr, "Failed to find HW pixel format\n"); return AV_PIX_FMT_NONE; } static int decode_frame(AVFormatContext *input_ctx, int video_stream, AVCodecContext *decoder_ctx, AVFrame **frame_ptr) { int ret; while (1) { AVPacket packet; ret = av_read_frame(input_ctx, &packet); if (ret < 0) { fprintf(stderr, "Failed to read frames into packet\n"); return ret; } if (video_stream != packet.stream_index) { av_packet_unref(&packet); continue; } ret = avcodec_send_packet(decoder_ctx, &packet); if (ret < 0) { fprintf(stderr, "Failed to send packet to decoder\n"); return ret; } av_packet_unref(&packet); AVFrame *frame = av_frame_alloc(); if (!frame) { fprintf(stderr, "Failed to allocate frame\n"); return AVERROR(ENOMEM); } while (1) { ret = avcodec_receive_frame(decoder_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { fprintf(stderr, "Failed to receive frame from decoder\n"); return ret; } if (frame->format != hw_pix_fmt) { continue; } *frame_ptr = frame; return 0; } av_frame_free(&frame); } return AVERROR_EOF; } int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "usage: %s \n", argv[0]); return 1; } enum AVHWDeviceType type = AV_HWDEVICE_TYPE_VAAPI; int ret = check_hw_device_type(type); if (ret != 0) { fprintf(stderr, "VA-API not supported\n"); return 1; } AVFormatContext *input_ctx = NULL; if (avformat_open_input(&input_ctx, argv[1], NULL, NULL) != 0) { fprintf(stderr, "Failed to open input file\n"); return 1; } if (avformat_find_stream_info(input_ctx, NULL) < 0) { fprintf(stderr, "Failed to find input stream info\n"); return 1; } AVCodec *decoder = NULL; int video_stream = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); if (video_stream < 0) { fprintf(stderr, "Failed to find video stream in input file\n"); return 1; } fprintf(stderr, "Selected decoder %s\n", decoder->name); int i = 0; while (1) { const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i); if (!config) { fprintf(stderr, "Decoder %s doesn't support device type %s\n", decoder->name, av_hwdevice_get_type_name(type)); return 1; } if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) && config->device_type == type) { hw_pix_fmt = config->pix_fmt; break; } i++; } fprintf(stderr, "Selected pixel format %s\n", av_get_pix_fmt_name(hw_pix_fmt)); AVCodecContext *decoder_ctx = avcodec_alloc_context3(decoder); if (!decoder_ctx) { fprintf(stderr, "Failed to allocate decoder context\n"); return 1; } AVStream *video = input_ctx->streams[video_stream]; if (avcodec_parameters_to_context(decoder_ctx, video->codecpar) < 0) { fprintf(stderr, "Failed to fill decoder context from video parameters\n"); return 1; } decoder_ctx->get_format = get_hw_format; AVBufferRef *hw_device_ctx = NULL; ret = av_hwdevice_ctx_create(&hw_device_ctx, type, NULL, NULL, 0); if (ret < 0) { fprintf(stderr, "Failed to create HW device context\n"); return 1; } decoder_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); ret = avcodec_open2(decoder_ctx, decoder, NULL); if (ret < 0) { fprintf(stderr, "Failed to open codec for stream #%u\n", video_stream); return 1; } AVFrame *frame = NULL; ret = decode_frame(input_ctx, video_stream, decoder_ctx, &frame); if (ret < 0) { return 1; } fprintf(stderr, "Got %dx%d frame!\n", frame->width, frame->height); AVFrame *drm_frame = av_frame_alloc(); drm_frame->format = AV_PIX_FMT_DRM_PRIME; drm_frame->hw_frames_ctx = av_buffer_ref(frame->hw_frames_ctx); // Convert the VA-API frame into a DMA-BUF frame ret = av_hwframe_map(drm_frame, frame, 0); if (ret < 0) { fprintf(stderr, "Failed to map frame: %s\n", av_err2str(ret)); return 1; } AVDRMFrameDescriptor *drm_frame_desc = (void *)drm_frame->data[0]; fprintf(stderr, "DRM frame:\n"); for (int i = 0; i < drm_frame_desc->nb_objects; i++) { AVDRMObjectDescriptor *drm_object = &drm_frame_desc->objects[i]; fprintf(stderr, " object #%i: fd = %d, size = %zu, " "format_modifier = 0x%lX\n", i, drm_object->fd, drm_object->size, drm_object->format_modifier); } for (int i = 0; i < drm_frame_desc->nb_layers; i++) { AVDRMLayerDescriptor *drm_layer = &drm_frame_desc->layers[i]; fprintf(stderr, " layer #%d: format = 0x%X\n", i, drm_layer->format); for (int j = 0; j < drm_layer->nb_planes; j++) { AVDRMPlaneDescriptor *drm_plane = &drm_layer->planes[j]; fprintf(stderr, " plane #%d: object_index = %d, " "offset = %ld, pitch = %ld\n", j, drm_plane->object_index, drm_plane->offset, drm_plane->pitch); } } for (int i = 0; i < drm_frame_desc->nb_objects; i++) { close(drm_frame_desc->objects[i].fd); drm_frame_desc->objects[i].fd = -1; } av_frame_free(&drm_frame); av_frame_free(&frame); avcodec_free_context(&decoder_ctx); avformat_close_input(&input_ctx); av_buffer_unref(&hw_device_ctx); return 0; }