diff --git a/app/src/decoder.c b/app/src/decoder.c index 49d4ce86..ef805f05 100644 --- a/app/src/decoder.c +++ b/app/src/decoder.c @@ -1,6 +1,7 @@ #include "decoder.h" #include +#include #include #include #include @@ -15,6 +16,37 @@ #include "util/buffer_util.h" #include "util/log.h" +static int hw_decoder_init(struct decoder *decoder) +{ + int ret; + + ret = av_hwdevice_ctx_create(&decoder->hw_device_ctx, + AV_HWDEVICE_TYPE_VAAPI, + NULL, NULL, 0); + if (ret < 0) { + LOGE("Failed to create specified HW device"); + return ret; + } + + decoder->codec_ctx->hw_device_ctx = av_buffer_ref(decoder->hw_device_ctx); + + return ret; +} + +static enum AVPixelFormat get_vaapi_format(__attribute__((unused)) AVCodecContext *ctx, + const enum AVPixelFormat *pix_fmts) +{ + const enum AVPixelFormat *p; + + for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { + if (*p == AV_PIX_FMT_VAAPI) + return *p; + } + + LOGE("Unable to decode using VA-API"); + return AV_PIX_FMT_NONE; +} + // set the decoded frame as ready for rendering, and notify static void push_frame(struct decoder *decoder) { @@ -44,6 +76,9 @@ decoder_open(struct decoder *decoder, const AVCodec *codec) { return false; } + hw_decoder_init(decoder); + decoder->codec_ctx->get_format = get_vaapi_format; + if (avcodec_open2(decoder->codec_ctx, codec, NULL) < 0) { LOGE("Could not open codec"); avcodec_free_context(&decoder->codec_ctx); @@ -64,19 +99,32 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) { // the new decoding/encoding API has been introduced by: // #ifdef SCRCPY_LAVF_HAS_NEW_ENCODING_DECODING_API + struct video_buffer *vb = decoder->video_buffer; + AVFrame *rendering_frame = vb->rendering_frame; int ret; if ((ret = avcodec_send_packet(decoder->codec_ctx, packet)) < 0) { LOGE("Could not send video packet: %d", ret); return false; } ret = avcodec_receive_frame(decoder->codec_ctx, - decoder->video_buffer->decoding_frame); + vb->hw_frame); if (!ret) { // a frame was received + + ret = av_hwframe_transfer_data(vb->decoding_frame, + vb->hw_frame, 0); + if (ret < 0) { + LOGE("Failed to transfer data to output frame: %d", ret); + goto fail; + } + + av_image_fill_arrays(rendering_frame->data, rendering_frame->linesize, + vb->out_buffer, AV_PIX_FMT_NV12, + rendering_frame->width, rendering_frame->height, 1); + push_frame(decoder); } else if (ret != AVERROR(EAGAIN)) { - LOGE("Could not receive video frame: %d", ret); - return false; + goto fail; } #else int got_picture; @@ -93,6 +141,10 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) { } #endif return true; + +fail: + LOGE("Could not receive video frame: %d", ret); + return false; } void diff --git a/app/src/decoder.h b/app/src/decoder.h index f243812c..ae9cc6a0 100644 --- a/app/src/decoder.h +++ b/app/src/decoder.h @@ -10,6 +10,7 @@ struct video_buffer; struct decoder { struct video_buffer *video_buffer; + AVBufferRef *hw_device_ctx; AVCodecContext *codec_ctx; }; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 3d043b95..2ecd45ce 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -369,7 +369,7 @@ scrcpy(const struct scrcpy_options *options) { fps_counter_initialized = true; if (!video_buffer_init(&video_buffer, &fps_counter, - options->render_expired_frames)) { + options->render_expired_frames, &frame_size)) { goto end; } video_buffer_initialized = true; diff --git a/app/src/screen.c b/app/src/screen.c index fe2bc867..e4fc638f 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -203,7 +203,7 @@ static inline SDL_Texture * create_texture(struct screen *screen) { SDL_Renderer *renderer = screen->renderer; struct size size = screen->frame_size; - SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, + SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_NV12, SDL_TEXTUREACCESS_STREAMING, size.width, size.height); if (!texture) { @@ -442,10 +442,8 @@ prepare_for_frame(struct screen *screen, struct size new_frame_size) { // write the frame into the texture static void update_texture(struct screen *screen, const AVFrame *frame) { - SDL_UpdateYUVTexture(screen->texture, NULL, - frame->data[0], frame->linesize[0], - frame->data[1], frame->linesize[1], - frame->data[2], frame->linesize[2]); + SDL_UpdateTexture(screen->texture, NULL, + frame->data[0], frame->linesize[0]); if (screen->mipmaps) { assert(screen->use_opengl); diff --git a/app/src/video_buffer.c b/app/src/video_buffer.c index 629680d9..7401962b 100644 --- a/app/src/video_buffer.c +++ b/app/src/video_buffer.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "config.h" @@ -11,26 +12,37 @@ bool video_buffer_init(struct video_buffer *vb, struct fps_counter *fps_counter, - bool render_expired_frames) { + bool render_expired_frames, struct size *size) { vb->fps_counter = fps_counter; - if (!(vb->decoding_frame = av_frame_alloc())) { + if (!(vb->hw_frame = av_frame_alloc())) { goto error_0; } - if (!(vb->rendering_frame = av_frame_alloc())) { + if (!(vb->decoding_frame = av_frame_alloc())) { goto error_1; } + vb->decoding_frame->format = AV_PIX_FMT_NV12; + + if (!(vb->rendering_frame = av_frame_alloc())) { + goto error_2; + } + vb->rendering_frame->format = AV_PIX_FMT_NV12; + + vb->out_buffer = av_malloc(av_image_get_buffer_size(AV_PIX_FMT_NV12, size->width, size->height, 1)); + if (!vb->out_buffer) { + goto error_3; + } if (!(vb->mutex = SDL_CreateMutex())) { - goto error_2; + goto error_4; } vb->render_expired_frames = render_expired_frames; if (render_expired_frames) { if (!(vb->rendering_frame_consumed_cond = SDL_CreateCond())) { SDL_DestroyMutex(vb->mutex); - goto error_2; + goto error_4; } // interrupted is not used if expired frames are not rendered // since offering a frame will never block @@ -43,10 +55,14 @@ video_buffer_init(struct video_buffer *vb, struct fps_counter *fps_counter, return true; -error_2: +error_4: + av_free(vb->out_buffer); +error_3: av_frame_free(&vb->rendering_frame); -error_1: +error_2: av_frame_free(&vb->decoding_frame); +error_1: + av_frame_free(&vb->hw_frame); error_0: return false; } @@ -57,8 +73,10 @@ video_buffer_destroy(struct video_buffer *vb) { SDL_DestroyCond(vb->rendering_frame_consumed_cond); } SDL_DestroyMutex(vb->mutex); + av_free(vb->out_buffer); av_frame_free(&vb->rendering_frame); av_frame_free(&vb->decoding_frame); + av_frame_free(&vb->hw_frame); } static void diff --git a/app/src/video_buffer.h b/app/src/video_buffer.h index 303b3fc2..2121be36 100644 --- a/app/src/video_buffer.h +++ b/app/src/video_buffer.h @@ -5,14 +5,17 @@ #include #include "config.h" +#include "common.h" #include "fps_counter.h" // forward declarations typedef struct AVFrame AVFrame; struct video_buffer { + AVFrame *hw_frame; AVFrame *decoding_frame; AVFrame *rendering_frame; + void *out_buffer; SDL_mutex *mutex; bool render_expired_frames; bool interrupted; @@ -23,7 +26,7 @@ struct video_buffer { bool video_buffer_init(struct video_buffer *vb, struct fps_counter *fps_counter, - bool render_expired_frames); + bool render_expired_frames, struct size *size); void video_buffer_destroy(struct video_buffer *vb);