@@ 1,25 @@
+BSD 2-Clause License
+
+Copyright (c) 2021, Valve Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ 1,222 @@
+#include <stdio.h>
+#include <unistd.h>
+
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libavutil/hwcontext.h>
+#include <libavutil/hwcontext_drm.h>
+#include <libavutil/pixdesc.h>
+
+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 <file>\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;
+}