📜  将 yt 视频转换为背景覆盖 - C 编程语言(1)

📅  最后修改于: 2023-12-03 15:09:33.047000             🧑  作者: Mango

将 YT 视频转换为背景覆盖 - C 编程语言

当您的应用程序需要一个更具吸引力的用户界面时,视频背景可能会有所帮助。 我们可以编写 C 代码,将 YT 视频转换为背景覆盖。 在这篇文章中,我们将介绍如何使用 C 编程语言来实现这一目标。

环境和工具

在继续之前,请确认您已经安装了以下工具:

  • C 编译器 (如 GCC)
  • FFMpeg 库
  • SDL 库

FFMpeg 和 SDL 库是我们将使用的主要依赖项。

步骤
步骤 1 - 下载 YT 视频

首先,我们需要下载 YT 视频。 我们可以使用 youtube-dl 工具来完成这个任务。 以下是下载视频的命令:

youtube-dl -f bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4 <yt video url>

请确保将 <yt video url> 替换为您要下载的 YT 视频的 URL。

步骤 2 - 安装 FFMpeg 和 SDL 库

我们将使用 FFMpeg 库来处理视频文件,SDL 库来呈现视频。 要安装它们,请按照以下步骤操作:

安装 FFMpeg 库

sudo apt-get install ffmpeg

安装 SDL 库

sudo apt-get install libsdl2-dev
步骤 3 - 编写 C 代码

以下是源代码:

#include <stdio.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>

#define SDL_AUDIO_BUFFER_SIZE 1024
#define MAX_AUDIO_FRAME_SIZE 192000

int main(int argc, char *argv[]) {

    AVFormatContext *pFormatCtx = NULL;
    AVCodecContext *pCodecCtx = NULL;
    AVCodec *pCodec = NULL;
    AVFrame *pFrame = NULL;
    AVPacket packet;
    int video_stream_index = -1;
    int frame_finished = 0;
    struct SwsContext *sws_ctx = NULL;
    SDL_Window *screen = NULL;
    SDL_Renderer *renderer = NULL;
    SDL_Texture *texture = NULL;
    SDL_Rect rect;
    SDL_Event event;
    SDL_AudioSpec wanted_spec, spec;
    uint8_t audio_buf[(MAX_AUDIO_FRAME_SIZE * 3) / 2];
    unsigned int audio_buf_size = 0;
    unsigned int audio_buf_index = 0;
    AVFormatContext *a_format_ctx = NULL;
    AVCodecContext *a_codec_ctx = NULL;
    AVCodec *a_codec = NULL;
    AVPacket a_packet;
    AVFrame *a_frame = NULL;
    unsigned int audio_stream_index = -1;
    int audio_frame_finished = 0;
    SwrContext *swr_ctx = NULL;

    if (argc < 2) {
        fprintf(stderr, "Usage: %s <input_file>\n", argv[0]);
        return -1;
    }

    if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0) {
        return -1;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        return -1;
    }

    av_dump_format(pFormatCtx, 0, argv[1], 0);

    int i;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && video_stream_index < 0) {
            video_stream_index = i;
        }
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_stream_index < 0) {
            audio_stream_index = i;
        }
    }

    if (video_stream_index == -1) {
        return -1;
    }

    pCodec = avcodec_find_decoder(pFormatCtx->streams[video_stream_index]->codecpar->codec_id);
    if (pCodec == NULL) {
        return -1;
    }

    pCodecCtx = avcodec_alloc_context3(pCodec);
    avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[video_stream_index]->codecpar);
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        return -1;
    }

    if (audio_stream_index != -1) {
        a_codec = avcodec_find_decoder(pFormatCtx->streams[audio_stream_index]->codecpar->codec_id);
        if (a_codec == NULL)
            return -1;
        a_codec_ctx = avcodec_alloc_context3(a_codec);
        avcodec_parameters_to_context(a_codec_ctx, pFormatCtx->streams[audio_stream_index]->codecpar);
        if (avcodec_open2(a_codec_ctx, a_codec, NULL) < 0)
            return -1;
        a_frame = av_frame_alloc();
        if (a_frame == NULL) {
            return -1;
        }
    }

    pFrame = av_frame_alloc();
    if (pFrame == NULL) {
        return -1;
    }

    screen = SDL_CreateWindow("Video Player", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, pCodecCtx->width,
                              pCodecCtx->height, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
    if (!screen) {
        return -1;
    }

    renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED);
    if (!renderer) {
        return -1;
    }

    texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width,
                                pCodecCtx->height);
    if (!texture) {
        return -1;
    }

    sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,
                             pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL);
    SDL_PollEvent(&event); /* Trigger refresh */
    while (event.type != SDL_QUIT) {
        if (av_read_frame(pFormatCtx, &packet) >= 0) {
            if (packet.stream_index == video_stream_index) {
                avcodec_decode_video2(pCodecCtx, pFrame, &frame_finished, &packet);
                if (frame_finished) {
                    sws_scale(sws_ctx, (uint8_t const *const *) pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                              pFrame->data, pFrame->linesize);
                    SDL_UpdateYUVTexture(texture, NULL, pFrame->data[0], pFrame->linesize[0], pFrame->data[1],
                                         pFrame->linesize[1], pFrame->data[2], pFrame->linesize[2]);
                    SDL_RenderClear(renderer);
                    SDL_RenderCopy(renderer, texture, NULL, NULL);
                    SDL_RenderPresent(renderer);
                }
            } else if (packet.stream_index == audio_stream_index) {
                avcodec_decode_audio4(a_codec_ctx, a_frame, &audio_frame_finished, &a_packet);
                if (audio_frame_finished) {
                    if (!swr_ctx) {
                        swr_ctx = swr_alloc_set_opts(NULL, a_frame->channel_layout, AV_SAMPLE_FMT_S16,
                                                     a_frame->sample_rate, pFrame->channel_layout,
                                                     pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL);
                        swr_init(swr_ctx);
                    }

                    int dst_nb_samples = av_rescale_rnd(
                            swr_get_delay(swr_ctx, a_frame->sample_rate) + a_frame->nb_samples,
                            pCodecCtx->sample_rate, pCodecCtx->sample_rate, AV_ROUND_UP);
                    int nb = swr_convert(swr_ctx, &audio_buf, dst_nb_samples, (const uint8_t **) a_frame->data,
                                         a_frame->nb_samples);
                    audio_buf_size = av_samples_get_buffer_size(NULL, pCodecCtx->channels, nb, pCodecCtx->sample_fmt,
                                                                 1);
                    audio_buf_index = 0;
                }
            }

            av_packet_unref(&packet);
        } else {
            /* Loop video */
            av_seek_frame(pFormatCtx, video_stream_index, 0, AVSEEK_FLAG_BACKWARD);
        }

        SDL_PollEvent(&event);
        switch (event.type) {
            case SDL_QUIT:
                SDL_DestroyRenderer(renderer);
                SDL_DestroyTexture(texture);
                SDL_DestroyWindow(screen);
                avformat_close_input(&a_format_ctx);
                avcodec_close(a_codec_ctx);
                av_frame_free(&a_frame);
                swr_free(&swr_ctx);
                avcodec_close(pCodecCtx);
                avformat_free_context(pFormatCtx);
                SDL_Quit();
                exit(0);
                break;
            default:
                break;
        }

        if (audio_stream_index != -1) {
            if (audio_buf_index >= audio_buf_size) {
                SDL_QueueAudio(1, audio_buf, audio_buf_size);
                audio_buf_index = 0;
            }
        }
    }

    return 0;
}
步骤 4 - 构建和运行

使用以下命令构建和运行程序:

gcc -o <output_file> <source_file> $(pkg-config --cflags --libs libavcodec libavformat libswscale sdl2)
./<output_file> <input_file>

<output_file> 是构建出的可执行文件的名称,<source_file> 是源代码文件的名称,<input_file> 是您下载的 YT 视频文件的名称。

结论

现在,您已经知道如何将 YT 视频转换为背景覆盖。 通过使用 FFMpeg 和 SDL 库,我们可以轻松地实现这一目标。