{"id":1755,"date":"2022-02-15T10:23:38","date_gmt":"2022-02-15T02:23:38","guid":{"rendered":"https:\/\/ykyi.net\/?p=1755"},"modified":"2022-02-15T10:23:38","modified_gmt":"2022-02-15T02:23:38","slug":"%e7%94%a8ffmpeg5-0-sdl3-%e6%92%ad%e6%94%be%e8%a7%86%e9%a2%91","status":"publish","type":"post","link":"https:\/\/ykyi.net\/?p=1755","title":{"rendered":"\u7528FFmpeg5.0 + SDL3 \u64ad\u653e\u89c6\u9891"},"content":{"rendered":"<p>\u4e0a\u4e2a\u6708\u770b\u5b8c\u4e86\u8bb2H.264\u89c6\u9891\u7f16\u89e3\u7801\u7684\u7ecf\u5178\u4e66\uff1a\u201cThe H.264 Advanced Video Compression Standard\u201d\uff0c\u770b\u5b8c\u540e\u611f\u89c9\u4e5f\u4e0d\u662f\u90a3\u4e48\u96be\u3002\u8bb0\u5f97\u5927\u7ea6\u5341\u5e74\u524d\u7b2c\u4e00\u6b21\u770b\u5230\u9884\u6d4b(prediction), \u8fd0\u52a8\u8865\u507f(motion compensation)\u4e4b\u7c7b\u7684\u672f\u8bed\u548c\u793a\u610f\u56fe\uff0c\u611f\u89c9\u662f\u975e\u5e38\u6666\u6da9\u96be\u61c2\u7684\u77e5\u8bc6\u3002\u800c\u73b0\u5728\u8865\u4e86\u57fa\u7840\u4fe1\u53f7\u7406\u8bba\u7684\u77e5\u8bc6\uff0c\u4e5f\u626b\u9664\u4e86\u5f88\u591a\u754f\u96be\u5fc3\u7406\u3002<\/p>\n<p>\u540e\u6765\u4e86\u89e3\u5230\u6bd4\u8f83\u5e38\u7528\u7684\u5f00\u6e90H.264\u7f16\u7801\u5668\u662fX264\uff0c\u800cFFmpeg\u5219\u81ea\u5e26\u4e00\u4e2a\u5e7f\u6cdb\u4f7f\u7528\u7684\u5f00\u6e90H.264\u89e3\u7801\u5668\u3002\u4e8e\u662f\uff0c\u6211\u5c31\u60f3\u5bf9\u7167\u7740\u5f00\u6e90\u4ee3\u7801\uff0c\u518d\u8fc7\u4e00\u904d\u7406\u8bba\u77e5\u8bc6\u3002\u90a3\u5c31\u9009FFmpeg\u5427\uff5e\u6bd5\u7adf\u540d\u6c14\u592a\u5927\u3002<\/p>\n<p>\u9996\u5148\uff0c\u5f97\u7528FFmpeg\u6574\u4e00\u4e2a\u7b80\u5355\u7684\u64ad\u653e\u5668\u8dd1\u8d77\u6765\u624d\u80fd\u8c03\u8bd5\u3002\u4f46\u662f\uff0c\u7f51\u7edc\u4e0a\u867d\u7136\u6709\u5927\u91cfFFmpeg\u7f16\u5199\u7b80\u5355\u64ad\u653e\u5668\u7684\u4ee3\u7801\uff0c\u7edd\u5927\u90e8\u5206\u4ee3\u7801\u5374\u662f\u65e0\u6cd5\u7528\u6700\u65b0\u7684FFmpeg\u901a\u8fc7\u7f16\u8bd1\u7684\u3002\u6bd5\u7adfFFmpeg\u5728\u6301\u7eed\u5f00\u53d1\u4e2d\uff0c\u8fde\u4e0a\u5c42API\u4e5f\u4f1a\u6709\u53d8\u5316\u3002\u6211\u4e0b\u8f7d\u4e86\u76ee\u524d(2022\/Feb\/13)\u6700\u65b0\u7684\u4ee3\u7801\uff0c\u7248\u672c\u53f7\u662f5.0\u3002\u65e7FFmpeg\u64ad\u653e\u5668\u4ee3\u7801\u8dd1\u4e0d\u8d77\u6765\u3002<\/p>\n<p>\u597d\u5728\u6211\u5728youtube.com\u4e0a\u627e\u5230\u4e00\u4f4dFFmpeg\u6838\u5fc3\u7ef4\u62a4\u8005Matt Szatmary\u505a\u7684presentation\uff0c<a class=\"wp-editor-md-post-content-link\" href=\"https:\/\/www.youtube.com\/watch?v=1aEinrlyp8w&amp;list=PLxbPHSSMPBeicXAHVfyFvGfCywRCq39Mp\">An Introduction to Building tools with FFmpeg libraries and APIs &#8211; Matt Szatmary | August 2019<\/a>\uff0c\u7ed3\u5408\u5b83\u7684\u4ecb\u7ecd\uff0c\u628a\u7b80\u5355\u7684\u64ad\u653e\u529f\u80fd\u8dd1\u8d77\u6765\u4e86\u3002\u8fd9\u91cc\u6709\u4e2a\u5c0f\u9057\u61be\u6ca1\u80fd\u627e\u5230Matt Szatmary\u7684\u6e90\u4ee3\u7801\uff0c\u867d\u7136\u4ed6\u5728\u89c6\u9891\u4e2d\u8bf4\u662f\u5f00\u6e90\u7684\u3002\u672c\u6765\u8fd8\u60f3\u5b66\u4e60\u4e00\u4e0b\u5927\u4f6c\u7684\u5199\u6cd5\u3002<\/p>\n<p>\u4ee3\u7801\u8d34\u5728\u8fd9\u91cc\u3002\u7528\u8fd9\u4e2a\u5c0fDEMO\u53ef\u4ee5\u7528\u8c03\u8bd5\u5668\u8ddf\u5230FFmpeg\u7684\u4ee3\u7801\u91cc\u770bH264\u89e3\u7801\u5668\u7684\u5177\u4f53\u5b9e\u73b0\u4e86\u3002<\/p>\n<pre><code class=\"language-C \">#include &lt;stdio.h&gt;\n#define __STDC_CONSTANT_MACROS\n\/\/ Linux...\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n#include &lt;libavcodec\/avcodec.h&gt;\n#include &lt;libavformat\/avformat.h&gt;\n#include &lt;libswscale\/swscale.h&gt;\n#include &lt;libavutil\/imgutils.h&gt;\n#include &lt;SDL2\/SDL.h&gt;\n#ifdef __cplusplus\n};\n#endif\n\nint sfp_refresh_thread(void *opaque);\n\n\/\/ Refresh Event\n#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)\n#define SFM_BREAK_EVENT (SDL_USEREVENT + 2)\nint thread_exit = 0;\nint thread_pause = 0;\n\nint sfp_refresh_thread(void *opaque)\n{\n    SDL_Event event;\n\n    thread_exit = 0;\n    thread_pause = 0;\n    while (!thread_exit)\n    {\n        if (!thread_pause)\n        {\n            SDL_Event event;\n            event.type = SFM_REFRESH_EVENT;\n            SDL_PushEvent(&amp;event);\n        }\n        SDL_Delay(40);\n    }\n    thread_exit = 0;\n    thread_pause = 0;\n    \/\/ Break\n    event.type = SFM_BREAK_EVENT;\n    SDL_PushEvent(&amp;event);\n    return 0;\n}\n\nint main(int argc, char *argv[])\n{\n    AVFormatContext *pFormatCtx;\n    int i, videoindex;\n    AVCodecParameters *pCodecPar;\n    AVCodecContext *pCodecCtx;\n    const AVCodec *pCodec;\n    AVFrame *pFrame, *pFrameYUV;\n    unsigned char *out_buffer;\n    AVPacket *packet;\n    int ret, got_picture;\n    \/\/------------SDL----------------\n    int screen_w, screen_h;\n    SDL_Window *screen;\n    SDL_Renderer *sdlRenderer;\n    SDL_Texture *sdlTexture;\n    SDL_Rect sdlRect;\n    SDL_Thread *video_tid;\n    SDL_Event event;\n    struct SwsContext *img_convert_ctx;\n    char filepath[] = \"video\";\n\n    \/\/ avformat_network_init();\n    \/\/ pFormatCtx = avformat_alloc_context();\n    pFormatCtx = NULL;    \/\/ MYNOTE: if pFormatCtx is not allocated explicitly using avformat_alloc_context()\n    \/\/ pFormatCtx will be allocated implicitly in avformat_open_input.\n    if (avformat_open_input(&amp;pFormatCtx, filepath, NULL, NULL) != 0)\n    {\n        printf(\"Couldn't open input stream.\\n\");\n        return -1;\n    }\n    if (avformat_find_stream_info(pFormatCtx, NULL) &lt; 0)\n    {\n        printf(\"Couldn't find stream information.\\n\");\n        return -1;\n    }\n    videoindex = -1;\n    for (i = 0; i &lt; pFormatCtx-&gt;nb_streams; i++)\n        if (pFormatCtx-&gt;streams[i]-&gt;codecpar-&gt;codec_type == AVMEDIA_TYPE_VIDEO)\n        {\n            videoindex = i;\n            pCodecPar = pFormatCtx-&gt;streams[i]-&gt;codecpar;\n            break;\n        }\n    if (videoindex == -1)\n    {\n        printf(\"Didn't find a video stream.\\n\");\n        return -1;\n    }\n\n    pCodec = avcodec_find_decoder(pCodecPar-&gt;codec_id);\n    if (pCodec == NULL)\n    {\n        printf(\"Codec not found.\\n\");\n        return -1;\n    }\n    pCodecCtx = avcodec_alloc_context3(pCodec);\n    if (!pCodecCtx)\n    {\n        fprintf(stderr, \"Could not allocate video codec context\\n\");\n        exit(1);\n    }\n    \/\/ \u8fd9\u4e00\u6b65\u4e0d\u80fd\u5c11\uff0c\u5426\u5219\u4f1a\u5d29\u6e83\u3002\u628aCodecPar\u7684\u53c2\u6570\u518d\u590d\u5236\u5230codecCtx\u4e2d\n    ret = avcodec_parameters_to_context(pCodecCtx, pCodecPar);\n    if (ret &lt; 0)\n    {\n        printf(\"avcodec_parameters_to_context failed...\");\n        return -1;\n    }\n    \/\/ pCodecCtx = pFormatCtx-&gt;streams[videoindex]-&gt;codecpar;\n    if (avcodec_open2(pCodecCtx, pCodec, NULL) &lt; 0)\n    {\n        printf(\"Could not open codec.\\n\");\n        return -1;\n    }\n    pFrame = av_frame_alloc();\n    pFrameYUV = av_frame_alloc();\n    size_t sz = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx-&gt;width, pCodecCtx-&gt;height, 1);\n    out_buffer = (unsigned char *)av_malloc(sz);\n    av_image_fill_arrays(pFrameYUV-&gt;data, pFrameYUV-&gt;linesize, out_buffer,\n                         AV_PIX_FMT_YUV420P, pCodecCtx-&gt;width, pCodecCtx-&gt;height, 1);\n    \/\/ Output Info-----------------------------\n    printf(\"---------------- File Information ---------------\\n\");\n    av_dump_format(pFormatCtx, 0, filepath, 0);\n    printf(\"-------------------------------------------------\\n\");\n    img_convert_ctx = sws_getContext(pCodecCtx-&gt;width, pCodecCtx-&gt;height, pCodecCtx-&gt;pix_fmt,\n                                     pCodecCtx-&gt;width, pCodecCtx-&gt;height, AV_PIX_FMT_YUV420P,\n                                     SWS_BICUBIC, NULL, NULL, NULL);\n    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO |\n                 SDL_INIT_TIMER))\n    {\n        printf(\"Could not initialize SDL - %s\\n\", SDL_GetError());\n        return -1;\n    }\n    \/\/ SDL 2.0 Support for multiple windows\n    screen_w = pCodecCtx-&gt;width;\n    screen_h = pCodecCtx-&gt;height;\n    screen = SDL_CreateWindow(\"Simplest ffmpeg player's Window\",\n                              SDL_WINDOWPOS_UNDEFINED,\n                              SDL_WINDOWPOS_UNDEFINED,\n                              screen_w, screen_h, SDL_WINDOW_OPENGL);\n    if (!screen)\n    {\n        printf(\"SDL: could not create window - exiting:%s\\n\", SDL_GetError());\n        return -1;\n    }\n    sdlRenderer = SDL_CreateRenderer(screen, -1, 0);\n    \/\/ IYUV: Y + U + V (3 planes)\n    \/\/ YV12: Y + V + U (3 planes)\n    sdlTexture = SDL_CreateTexture(sdlRenderer,\n                                   SDL_PIXELFORMAT_IYUV,\n                                   SDL_TEXTUREACCESS_STREAMING, pCodecCtx-&gt;width, pCodecCtx-&gt;height);\n    sdlRect.x = 0;\n    sdlRect.y = 0;\n    sdlRect.w = screen_w;\n    sdlRect.h = screen_h;\n    packet = (AVPacket *)av_malloc(sizeof(AVPacket));\n    video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);\n    \/\/------------SDL End------------\n    \/\/ Event Loop\n    for (;;)\n    {\n        char buf[1024];\n        int ret;\n        \/\/ Wait\n        SDL_WaitEvent(&amp;event);\n        if (event.type == SFM_REFRESH_EVENT)\n        {\n            while (1)\n            {\n                if (av_read_frame(pFormatCtx, packet) &lt; 0)\n                    thread_exit = 1;\n                if (packet-&gt;stream_index == videoindex)\n                    break;\n            }\n\n            \/\/ ret = avcodec_decode_video2(pCodecCtx, pFrame, &amp;got_picture, packet);\n            \/\/ ret = my_decode();\n            ret = avcodec_send_packet(pCodecCtx, packet);\n            if (ret &lt; 0)\n            {\n                fprintf(stderr, \"Error sending a packet for decoding\\n\");\n                exit(1);\n            }\n\n            while (ret &gt;= 0)\n            {\n                ret = avcodec_receive_frame(pCodecCtx, pFrame);\n                if (ret == AVERROR(EAGAIN))\n                {\n                    fprintf(stderr, \"EAGAIN occurred...\\n\");\n                    break;\n                }\n                if (ret == AVERROR_EOF)\n                {\n                    printf(\"byebye...\\n\");\n                    exit(0);\n                }\n                if (ret &lt; 0)\n                {\n                    fprintf(stderr, \"Error during decoding\\n\");\n                    exit(1);\n                }\n\n                printf(\"Got frame %3d\\n\", pCodecCtx-&gt;frame_number);\n                fflush(stdout);\n\n                \/* the picture is allocated by the decoder. no need to\n                   free it *\/\n                \/\/ snprintf(buf, sizeof(buf), \"%s-%d\", \"player.c\", dec_ctx-&gt;frame_number);\n                \/\/ pgm_save(frame-&gt;data[0], frame-&gt;linesize[0], frame-&gt;width, frame-&gt;height, buf);\n\n                sws_scale(img_convert_ctx, (const unsigned char *const *)pFrame-&gt;data,\n                          pFrame-&gt;linesize, 0, pCodecCtx-&gt;height, pFrameYUV-&gt;data, pFrameYUV-&gt;linesize);\n                \/\/ SDL---------------------------\n                SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV-&gt;data[0],\n                                  pFrameYUV-&gt;linesize[0]);\n                SDL_RenderClear(sdlRenderer);\n                SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);\n                SDL_RenderPresent(sdlRenderer);\n                \/\/ SDL End-----------------------\n            }\n\n            \/\/ av_free_packet(packet);\n            av_packet_unref(packet);\n        }\n        else if (event.type == SDL_KEYDOWN)\n        {\n            \/\/ Pause\n            if (event.key.keysym.sym == SDLK_SPACE)\n                thread_pause = !thread_pause;\n        }\n        else if (event.type == SDL_QUIT)\n        {\n            thread_exit = 1;\n        }\n        else if (event.type == SFM_BREAK_EVENT)\n        {\n            break;\n        }\n    }\n    sws_freeContext(img_convert_ctx);\n    SDL_Quit();\n    av_frame_free(&amp;pFrameYUV);\n    av_frame_free(&amp;pFrame);\n    avcodec_free_context(&amp;pCodecCtx);\n    avcodec_close(pCodecCtx);\n    avformat_close_input(&amp;pFormatCtx);\n\n    return 0;\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u4e0a\u4e2a\u6708\u770b\u5b8c\u4e86\u8bb2H.264\u89c6\u9891\u7f16\u89e3\u7801\u7684\u7ecf\u5178\u4e66\uff1a\u201cThe H.264 Advanced Video Compression Standard\u201d\uff0c\u770b\u5b8c\u540e\u611f\u89c9\u4e5f\u4e0d\u662f\u90a3\u4e48\u96be\u3002\u8bb0\u5f97\u5927\u7ea6\u5341\u5e74\u524d\u7b2c\u4e00\u6b21\u770b\u5230\u9884\u6d4b(prediction), \u8fd0\u52a8\u8865\u507f(motion compensation)\u4e4b\u7c7b\u7684\u672f\u8bed\u548c\u793a\u610f\u56fe\uff0c\u611f\u89c9\u662f\u975e\u5e38\u6666\u6da9\u96be\u61c2\u7684\u77e5\u8bc6\u3002\u800c\u73b0\u5728\u8865\u4e86\u57fa\u7840\u4fe1\u53f7\u7406\u8bba\u7684\u77e5\u8bc6\uff0c\u4e5f\u626b\u9664\u4e86\u5f88\u591a\u754f\u96be\u5fc3\u7406\u3002 \u540e\u6765\u4e86\u89e3\u5230\u6bd4\u8f83\u5e38\u7528\u7684\u5f00\u6e90H.264\u7f16\u7801\u5668\u662fX264\uff0c\u800cFFmpeg\u5219\u81ea\u5e26\u4e00\u4e2a\u5e7f\u6cdb\u4f7f\u7528\u7684\u5f00\u6e90H.264\u89e3\u7801\u5668\u3002\u4e8e\u662f\uff0c\u6211\u5c31\u60f3\u5bf9\u7167\u7740\u5f00\u6e90\u4ee3\u7801\uff0c\u518d\u8fc7\u4e00\u904d\u7406\u8bba\u77e5\u8bc6\u3002\u90a3\u5c31\u9009FFmpeg\u5427\uff5e\u6bd5\u7adf\u540d\u6c14\u592a\u5927\u3002 \u9996\u5148\uff0c\u5f97\u7528FFmpeg\u6574\u4e00\u4e2a\u7b80\u5355\u7684\u64ad\u653e\u5668\u8dd1\u8d77\u6765\u624d\u80fd\u8c03\u8bd5\u3002\u4f46\u662f\uff0c\u7f51\u7edc\u4e0a\u867d\u7136\u6709\u5927\u91cfFFmpeg\u7f16\u5199\u7b80\u5355\u64ad\u653e\u5668\u7684\u4ee3\u7801\uff0c\u7edd\u5927\u90e8\u5206\u4ee3\u7801\u5374\u662f\u65e0\u6cd5\u7528\u6700\u65b0\u7684FFmpeg\u901a\u8fc7\u7f16\u8bd1\u7684\u3002\u6bd5\u7adfFFmpeg\u5728\u6301\u7eed\u5f00\u53d1\u4e2d\uff0c\u8fde\u4e0a\u5c42API\u4e5f\u4f1a\u6709\u53d8\u5316\u3002\u6211\u4e0b\u8f7d\u4e86\u76ee\u524d(2022\/Feb\/13)\u6700\u65b0\u7684\u4ee3\u7801\uff0c\u7248\u672c\u53f7\u662f5.0\u3002\u65e7FFmpeg\u64ad\u653e\u5668\u4ee3\u7801\u8dd1\u4e0d\u8d77\u6765\u3002 \u597d\u5728\u6211\u5728youtube.com\u4e0a\u627e\u5230\u4e00\u4f4dFFmpeg\u6838\u5fc3\u7ef4\u62a4\u8005Matt Szatmary\u505a\u7684presentation\uff0cAn Introduction to Building tools with FFmpeg libraries and APIs &#8211; Matt Szatmary | August 2019\uff0c\u7ed3\u5408\u5b83\u7684\u4ecb\u7ecd\uff0c\u628a\u7b80\u5355\u7684\u64ad\u653e\u529f\u80fd\u8dd1\u8d77\u6765\u4e86\u3002\u8fd9\u91cc\u6709\u4e2a\u5c0f\u9057\u61be\u6ca1\u80fd\u627e\u5230Matt Szatmary\u7684\u6e90\u4ee3\u7801\uff0c\u867d\u7136\u4ed6\u5728\u89c6\u9891\u4e2d\u8bf4\u662f\u5f00\u6e90\u7684\u3002\u672c\u6765\u8fd8\u60f3\u5b66\u4e60\u4e00\u4e0b\u5927\u4f6c\u7684\u5199\u6cd5\u3002 \u4ee3\u7801\u8d34\u5728\u8fd9\u91cc\u3002\u7528\u8fd9\u4e2a\u5c0fDEMO\u53ef\u4ee5\u7528\u8c03\u8bd5\u5668\u8ddf\u5230FFmpeg\u7684\u4ee3\u7801\u91cc\u770bH264\u89e3\u7801\u5668\u7684\u5177\u4f53\u5b9e\u73b0\u4e86\u3002 #include &lt;stdio.h&gt; #define __STDC_CONSTANT_MACROS \/\/ Linux&#8230; #ifdef __cplusplus extern &#8220;C&#8221; { #endif #include &lt;libavcodec\/avcodec.h&gt; #include &lt;libavformat\/avformat.h&gt; #include &lt;libswscale\/swscale.h&gt; #include &lt;libavutil\/imgutils.h&gt; #include &lt;SDL2\/SDL.h&gt; #ifdef __cplusplus }; #endif &hellip; <a href=\"https:\/\/ykyi.net\/?p=1755\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;\u7528FFmpeg5.0 + SDL3 \u64ad\u653e\u89c6\u9891&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"class_list":["post-1755","post","type-post","status-publish","format-standard","hentry","category-tech_articles"],"_links":{"self":[{"href":"https:\/\/ykyi.net\/index.php?rest_route=\/wp\/v2\/posts\/1755","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ykyi.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ykyi.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ykyi.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ykyi.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1755"}],"version-history":[{"count":0,"href":"https:\/\/ykyi.net\/index.php?rest_route=\/wp\/v2\/posts\/1755\/revisions"}],"wp:attachment":[{"href":"https:\/\/ykyi.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1755"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ykyi.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1755"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ykyi.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1755"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}