{"id":957,"date":"2023-07-28T17:30:41","date_gmt":"2023-07-28T09:30:41","guid":{"rendered":"https:\/\/swordofmorning.com\/?p=957"},"modified":"2025-10-09T13:55:19","modified_gmt":"2025-10-09T05:55:19","slug":"linux-like-on-android","status":"publish","type":"post","link":"https:\/\/swordofmorning.com\/index.php\/2023\/07\/28\/linux-like-on-android\/","title":{"rendered":"\u5728\u5b89\u5353\u4e0a\u4ee5Linux\u7684\u65b9\u5f0f\u8fd0\u884cC\/C++\u7a0b\u5e8f"},"content":{"rendered":"<p>&emsp;&emsp;\u73b0\u5728\u6211\u4eec\u9700\u8981\u5b9e\u73b0\u4e00\u4e2a\u7a0b\u5e8f\uff0c\u8be5\u7a0b\u5e8f\u8c03\u7528\u6444\u50cf\u5934\uff0c\u5e76\u5c06\u56fe\u50cf\u663e\u793a\u5230\u5c4f\u5e55\uff08\u5b89\u5353APP\uff09\u3002<\/p>\n<pre><code class=\"language-sh\"># \u4e00\u822c\u5f00\u53d1\u5de5\u4f5c\nprogram: camera -&gt; process -&gt; display\n\n# \u6211\u4eec\u7684\u601d\u8def\nprogram_1(linux_system): camera(v4l2) -&gt; process -&gt; rtsp_send\nprogram_2(user_android): rtsp_receive -&gt; display<\/code><\/pre>\n<p>&emsp;&emsp;\u5b89\u5353\u8fd0\u884cC\/C++\u7a0b\u5e8f\u4e00\u822c\u6709\u5982\u4e0b\u51e0\u79cd\u65b9\u6cd5\uff1aJNI\u3001QT\u3001\u4ee5\u53ca\u76f4\u63a5\u7f16\u8bd1C\/C++\u7a0b\u5e8f\u3002\u6211\u9009\u62e9\u7b2c\u4e09\u79cd\u65b9\u5f0f\u6765\u5b9e\u73b0\u8fd9\u4e2a\u7a0b\u5e8f\u3002\u4e3a\u6b64\uff0c\u6211\u4eec\u9700\u8981\u8fdb\u884c\u5982\u4e0b\u5de5\u4f5c\uff1a<\/p>\n<ol>\n<li>\u4e0b\u8f7dMediamtx\u63a8\u6d41\u3002<\/li>\n<li>\u914d\u7f6e\u4ea4\u53c9\u7f16\u8bd1\u5de5\u5177\u94fe\u3002<\/li>\n<li>\u79fb\u690dFFmpeg\u3002<\/li>\n<li>\u7f16\u5199C\/C++\u5e94\u7528\u7a0b\u5e8f\u3002<\/li>\n<\/ol>\n<p><strong>\u5173\u952e\u8bcd\uff1a<\/strong> Android\u3001Linux like\u3001C\/C++\u3001V4L2\u3001FFmpeg<\/p>\n<p><strong>\u6ce8\u610f\uff1a<\/strong> \u672c\u7ae0\u8282\u7684FFmpeg\u5e93\u4f7f\u7528\u9759\u6001\u94fe\u63a5\u3002<\/p>\n<hr>\n<p><div class=\"has-toc have-toc\"><\/div><\/p>\n<h2>\u4e00\u3001\u4ea4\u53c9\u7f16\u8bd1\u5de5\u5177\u94fe<\/h2>\n<p>&emsp;&emsp;\u6839\u636e\u81ea\u5df1\u7684\u67b6\u6784\u4e0b\u8f7d\u5bf9\u5e94\u7684\u5de5\u5177\u94fe\uff1a<a href=\"https:\/\/developer.arm.com\/downloads\/-\/gnu-a\" target=\"_blank\"  rel=\"nofollow\" >developer.arm.com<\/a>\u3002\u6211\u76ee\u6807\u673a\u5668\u7684CPU\u67b6\u6784\u662fARM64-V8\uff0cHost\u7f16\u8bd1\u73af\u5883\u662fX86_64\uff0c\u6240\u4ee5\u6211\u4f7f\u7528\u7684\u7f16\u8bd1\u5668\u662f<code>gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu<\/code>\u3002<\/p>\n<p>&emsp;&emsp;\u6211\u4eec\u5c06\u5176\u653e\u5230Host\u4e0a\u89e3\u538b\u5373\u53ef\uff0c\u662f\u5426\u9700\u8981\u914d\u7f6e\u73af\u5883\u53d8\u91cf\u770b\u4e2a\u4eba\u4e60\u60ef\u5373\u53ef\u3002<\/p>\n<h2>\u4e8c\u3001\u7f16\u8bd1FFmpeg<\/h2>\n<p>&emsp;&emsp;\u81ea\u884c\u5728<code>FFmpeg.org<\/code>\u6216\u8005<code>git<\/code>\u4e0a\u4e0b\u8f7dFFmpeg\uff0c\u6211\u4f7f\u7528\u7684\u7248\u672c\u662f4.3.1\u3002<\/p>\n<p>&emsp;&emsp;\u8fdb\u5165FFmpeg\u76ee\u5f55\uff0c\u521b\u5efa\u4e00\u4e2a\u811a\u672c\u7528\u6765\u63a7\u5236\u914d\u7f6e<code>configure<\/code>\u6587\u4ef6\u3002<\/p>\n<pre><code class=\"language-sh\">#!\/bin\/bash\n\nexport TOOLS=\/home\/xjt\/Gogs\/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu\nexport SYSROOT=${TOOLS}\/aarch64-none-linux-gnu\/libc\n# export PREFIX=.\/linux\/arm64\nexport LD=${TOOLS}\/bin\/aarch64-none-linux-gnu-ld\nexport AR=${TOOLS}\/bin\/aarch64-none-linux-gnu-ar\nexport RANLIB=${TOOLS}\/bin\/aarch64-none-linux-gnu-ranlib\n\nfunction build_lib\n{\n  .\/configure \\\n  --disable-shared \\\n  --enable-static \\\n  --prefix=$PREFIX \\\n  --cross-prefix=${TOOLS}\/bin\/aarch64-linux-gnu- \\\n  --cc=${TOOLS}\/bin\/aarch64-none-linux-gnu-gcc \\\n  --nm=${TOOLS}\/bin\/aarch64-none-linux-gnu-g++ \\\n  --ld=${LD} \\\n  --ar=${AR} \\\n  --ranlib=${RANLIB} \\\n  --target-os=linux \\\n  --arch=arm64 \\\n  --sysroot=$SYSROOT \\\n  --enable-runtime-cpudetect \\\n  --enable-cross-compile \\\n  --enable-pic \\\n  --enable-gpl \\\n  --enable-nonfree \\\n  --enable-yasm \\\n  --enable-muxer=mpeg4 \\\n  --enable-muxer=rtsp \\\n  --enable-encoder=mpeg4 \\\n  --enable-decoder=mpeg4 \\\n  --enable-network \\\n  --enable-protocol=tcp \\\n  --enable-pthreads \\\n  --disable-ffmpeg \\\n  --disable-ffplay \\\n  --disable-ffprobe \\\n  --disable-avdevice \\\n  --disable-doc \\\n  --extra-ldflags=&quot;-L${SYSROOT}\/libc\/lib -L\/home\/xjt\/Gogs\/x264\/install\/lib -lc&quot; \\\n  --extra-cflags=&quot;-I${SYSROOT}\/libc\/usr\/include -I\/home\/xjt\/Gogs\/x264\/install\/include -Wfatal-errors -Wno-deprecated&quot;\n  # --enable-libx264 \\\n  # --extra-libs=-ldl\n}\nbuild_lib<\/code><\/pre>\n<p><strong>\u6ce8\u610f\uff1a<\/strong><\/p>\n<ol>\n<li>\u8fd9\u91cc\u5982\u679c\u9700\u8981\u9759\u6001\u94fe\u63a5<code>x264<\/code>\uff0c\u5219\u9700\u8981\u5728\u540e\u9762\u52a0\u4e0a<code>--extra-libs=-ldl<\/code>\u3002<\/li>\n<li>RTSP\u9700\u8981<code>--enable-network<\/code>\u3001<code>--enable-protocol=tcp<\/code>\u3001<code>--enable-muxer=rtsp<\/code>\u3002<\/li>\n<\/ol>\n<p>&emsp;&emsp;\u63a5\u4e0b\u6765\u914d\u7f6e<code>configure<\/code>\u3001<code>make<\/code>\u3001<code>make install<\/code>\u5373\u53ef\u3002<\/p>\n<pre><code class=\"language-sh\">.\/build.sh\nmake -j8\nmake install<\/code><\/pre>\n<p>\u4e4b\u540e\u5728<code>PREFIX<\/code>\u76ee\u5f55\u4e0b\u627e\u5230\u8f93\u51fa\uff0c\u4e00\u822c\u6709<code>include<\/code>\u3001<code>lib<\/code>\u548c<code>share<\/code>\u3002<\/p>\n<h2>\u4e09\u3001\u5e94\u7528\u7a0b\u5e8f<\/h2>\n<p>&emsp;&emsp;\u5e94\u7528\u7a0b\u5e8f\u76ee\u5f55\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-sh\">.\n\u251c\u2500\u2500 build\n\u2502   \u251c\u2500\u2500 bin\n\u2502   \u251c\u2500\u2500 CMakeCache.txt\n\u2502   \u251c\u2500\u2500 CMakeFiles\n\u2502   \u251c\u2500\u2500 cmake_install.cmake\n\u2502   \u2514\u2500\u2500 Makefile\n\u251c\u2500\u2500 CMakeLists.txt\n\u251c\u2500\u2500 lib\n\u2502   \u251c\u2500\u2500 ffmpeg\n\u2502   \u2514\u2500\u2500 x264\n\u2514\u2500\u2500 src\n    \u251c\u2500\u2500 CMakeLists.txt\n    \u251c\u2500\u2500 main.cpp\n    \u251c\u2500\u2500 v4l2_stream.c\n    \u2514\u2500\u2500 v4l2_stream.h<\/code><\/pre>\n<h3>3.1 .\/CMakeLists.txt<\/h3>\n<pre><code class=\"language-cmake\">PROJECT(RK3568_APP)\n\nCMAKE_MINIMUM_REQUIRED(VERSION 3.5)\n\nSET(COMPILER_PATH &quot;\/home\/xjt\/Gogs\/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu&quot;)\nSET(CMAKE_C_COMPILER ${COMPILER_PATH}\/bin\/aarch64-none-linux-gnu-gcc)\nSET(CMAKE_CXX_COMPILER ${COMPILER_PATH}\/bin\/aarch64-none-linux-gnu-g++)\n\nSET(CMAKE_C_FLAGS &quot;${CMAKE_C_FLAGS} -s -O3 -lc -ldl -lrt -lm&quot;)\nSET(CMAKE_CXX_FLAGS &quot;${CMAKE_CXX_FLAGS} -std=c++11 -s -O3 -lstdc++ -lc -ldl -lrt -lm&quot;)\n\n# INCLUDE_DIRECTORIES(.\/lib\/x264\/include)\n# LINK_DIRECTORIES(.\/lib\/x264\/lib)\nINCLUDE_DIRECTORIES(.\/lib\/ffmpeg\/include)\nLINK_DIRECTORIES(.\/lib\/ffmpeg\/lib)\n\nSET(CMAKE_EXE_LINKER_FLAGS &quot;-static&quot;)\nADD_SUBDIRECTORY(src bin)<\/code><\/pre>\n<h3>3.2 .\/src\/CMakeLists.txt<\/h3>\n<pre><code class=\"language-cmake\">FILE(\n    GLOB_RECURSE SRC_LIST \n    .\/*.c\n    .\/*.cpp\n)\n\n# Exe output path\nSET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR\/bin})\n\nADD_EXECUTABLE(demo ${SRC_LIST})\n\nTARGET_LINK_LIBRARIES(\n    demo\n\n    # x264;\n\n    avformat;\n    swscale;\n    avcodec;\n    avutil;\n    swresample;\n\n    pthread;\n)<\/code><\/pre>\n<h3>3.3 .\/src\/main.cpp<\/h3>\n<pre><code class=\"language-cpp\">#include &lt;chrono&gt;\n#include &lt;thread&gt;\n#include &lt;iostream&gt;\n#include &lt;cstdio&gt;\n\n#ifdef __cplusplus\nextern &quot;C&quot; {\n#endif\n\n#include &lt;stdio.h&gt;\n#include &lt;libavcodec\/avcodec.h&gt;\n#include &lt;libavformat\/avformat.h&gt;\n#include &lt;libavutil\/opt.h&gt;\n#include &lt;libavutil\/time.h&gt;\n#include &lt;libswscale\/swscale.h&gt;\n#include &lt;libavutil\/avutil.h&gt;\n#include &lt;libavutil\/imgutils.h&gt;\n#include &lt;libswresample\/swresample.h&gt;\n#include &lt;pthread.h&gt;\n#include &quot;v4l2_stream.h&quot;\n\n#ifdef __cplusplus \n}\n#endif\n\n\/\/ using namespace std;\n\nstatic int video_is_eof;\n\n#define STREAM_FRAME_RATE 60\n#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P  \/* default pix_fmt *\/\n#define VIDEO_CODEC_ID AV_CODEC_ID_MPEG4\n\n\/* video output *\/\nstatic AVFrame *frame;\nstatic AVPicture src_picture, dst_picture;\n\nuint8_t input[640 * 480 * 2];\nuint8_t output[640 * 480 * 2];\nuint8_t output_128[640 * 480];\n\n\/* Add an output stream. *\/\nstatic AVStream *add_stream(AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id)\n{\n    AVCodecContext *c;\n    AVStream *st;\n\n    \/* find the encoder *\/\n    *codec = avcodec_find_encoder(codec_id);\n    if (!(*codec)) {\n        av_log(NULL, AV_LOG_ERROR, &quot;Could not find encoder for &#039;%s&#039;.\\n&quot;, avcodec_get_name(codec_id));\n    }\n    else {\n        st = avformat_new_stream(oc, *codec);\n        if (!st) {\n            av_log(NULL, AV_LOG_ERROR, &quot;Could not allocate stream.\\n&quot;);\n        }\n        else {\n            st-&gt;id = oc-&gt;nb_streams - 1;\n            st-&gt;time_base.den = STREAM_FRAME_RATE;\n            st-&gt;time_base.num = 1;\n\n            c = st-&gt;codec;\n            c-&gt;codec_id = codec_id;\n            c-&gt;bit_rate = 16000000;\n            c-&gt;width = 640;\n            c-&gt;height = 480;\n            c-&gt;time_base.den = STREAM_FRAME_RATE;\n            c-&gt;time_base.num = 1;\n            c-&gt;gop_size = 15;\n            c-&gt;pix_fmt = STREAM_PIX_FMT;\n        }\n    }\n\n    return st;\n}\n\nstatic int open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st)\n{\n    int ret;\n    AVCodecContext *c = st-&gt;codec;\n    \/\/ AVCodecContext *c = avcodec_alloc_context3(codec);\n\n    \/* open the codec *\/\n    ret = avcodec_open2(c, codec, NULL);\n    if (ret &lt; 0) {\n        av_log(NULL, AV_LOG_ERROR, &quot;Could not open video codec.\\n&quot;, avcodec_get_name(c-&gt;codec_id));\n    }\n    else {\n\n        \/* allocate and init a re-usable frame *\/\n        frame = av_frame_alloc();\n        if (!frame) {\n            av_log(NULL, AV_LOG_ERROR, &quot;Could not allocate video frame.\\n&quot;);\n            ret = -1;\n        }\n        else {\n            frame-&gt;format = c-&gt;pix_fmt;\n            frame-&gt;width = c-&gt;width;\n            frame-&gt;height = c-&gt;height;\n\n            \/* Allocate the encoded raw picture. *\/\n            ret = avpicture_alloc(&amp;dst_picture, c-&gt;pix_fmt, c-&gt;width, c-&gt;height);\n            if (ret &lt; 0) {\n                av_log(NULL, AV_LOG_ERROR, &quot;Could not allocate picture.\\n&quot;);\n            }\n            else {\n                \/* copy data and linesize picture pointers to frame *\/\n                *((AVPicture *)frame) = dst_picture;\n            }\n        }\n    }\n\n    return ret;\n}\n\nint VIDEOWRAP_YUYV422_to_YUV420P(int imgWidth, int imgHeight, uint8_t* inputData, uint8_t* outputData, int inputLength)\n{\n    struct SwsContext* ctx = sws_getContext(imgWidth, imgHeight, AV_PIX_FMT_YUYV422,\n                                            imgWidth, imgHeight, AV_PIX_FMT_YUV420P,\n                                            SWS_BICUBIC, NULL, NULL, NULL);\n    if (!ctx) {\n        fprintf(stderr, &quot;Failed to allocate SwsContext.\\n&quot;);\n        return -1; \/\/ Error handling\n    }\n\n    struct AVFrame* input_pFrame = av_frame_alloc();\n    struct AVFrame* output_pFrame = av_frame_alloc();\n    if (!input_pFrame || !output_pFrame) {\n        fprintf(stderr, &quot;Failed to allocate AVFrame.\\n&quot;);\n        if (input_pFrame) av_frame_free(&amp;input_pFrame);\n        if (output_pFrame) av_frame_free(&amp;output_pFrame);\n        sws_freeContext(ctx);\n        return -1; \/\/ Error handling\n    }\n\n    av_image_fill_arrays(input_pFrame-&gt;data, input_pFrame-&gt;linesize, inputData, AV_PIX_FMT_YUYV422, imgWidth, imgHeight, 1);\n    av_image_fill_arrays(output_pFrame-&gt;data, output_pFrame-&gt;linesize, outputData, AV_PIX_FMT_YUV420P, imgWidth, imgHeight, 1);\n\n    sws_scale(ctx,\n              (const uint8_t**)input_pFrame-&gt;data, input_pFrame-&gt;linesize, 0, imgHeight,\n              output_pFrame-&gt;data, output_pFrame-&gt;linesize);\n\n#if 0\n    FILE *fl;\n    fl = fopen(&quot;img.yuv&quot;, &quot;w&quot;);\n    if (NULL == fl)\n    {\n        fprintf(stderr, &quot;open write file failed.&quot;);\n    }\n    fwrite(outputData, inputLength * 1.5, 1, fl);\n    fclose(fl);\n#endif\n\n    \/\/ Release allocated resources\n    av_frame_free(&amp;input_pFrame);\n    av_frame_free(&amp;output_pFrame);\n    sws_freeContext(ctx);\n\n    return 0;\n}\n\n\/* Prepare a dummy image. *\/\nstatic void fill_yuv_image(AVPicture *pict, int frame_index, int width, int height)\n{\n    int x, y, i;\n\n    i = frame_index;\n\n    memcpy(input, v4l2_sp_globalBuffer[v4l2_sp_globalBufferIndex], v4l2_sp_globalBufferLength);\n\n    VIDEOWRAP_YUYV422_to_YUV420P(\n        640, 480, \n        input,\n        output, \n        v4l2_sp_globalBufferLength\n    );\n\n    memcpy(pict-&gt;data[0], output, 640 * 480);\n    memcpy(pict-&gt;data[1], output_128, 640 * 480 * 0.5);\n    memcpy(pict-&gt;data[2], output_128, 640 * 480 * 0.5);\n}\n\nstatic int write_video_frame(AVFormatContext *oc, AVStream *st, int64_t frameCount)\n{\n    int ret = 0;\n    AVCodecContext *c = st-&gt;codec;\n    \/\/ AVCodecContext *c = avcodec_alloc_context3(st-&gt;codecpar);\n\n    fill_yuv_image(&amp;dst_picture, frameCount, c-&gt;width, c-&gt;height);\n\n    AVPacket pkt = { 0 };\n    int got_packet;\n    av_init_packet(&amp;pkt);\n\n    \/* encode the image *\/\n    frame-&gt;pts = frameCount;\n    ret = avcodec_encode_video2(c, &amp;pkt, frame, &amp;got_packet);\n\n    if (ret &lt; 0) {\n        av_log(NULL, AV_LOG_ERROR, &quot;Error encoding video frame.\\n&quot;);\n    }\n    else {\n        if (got_packet) {\n            pkt.stream_index = st-&gt;index;\n            pkt.pts = av_rescale_q_rnd(pkt.pts, c-&gt;time_base, st-&gt;time_base, AVRounding(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));\n            ret = av_write_frame(oc, &amp;pkt);\n\n            if (ret &lt; 0) {\n                av_log(NULL, AV_LOG_ERROR, &quot;Error while writing video frame.\\n&quot;);\n            }\n        }\n    }\n\n    av_packet_unref(&amp;pkt);\n    return ret;\n}\n\nint main(int argc, char* argv[])\n{\n    printf(&quot;starting...\\n&quot;);\n\n    pthread_t v4l2_thread;\n    pthread_create(&amp;v4l2_thread, NULL, ThreadEntryPoint, NULL);\n    pthread_detach(v4l2_thread);\n\n    memset(output_128, 128, sizeof(output_128));\n\n    const char *url = &quot;rtsp:\/\/192.168.50.84:8554\/stream&quot;;\n\n    AVFormatContext *outContext;\n    AVStream *video_st;\n    AVCodec *video_codec;\n    int ret = 0;\n    int64_t frameCount = 0;\n\n    av_log_set_level(AV_LOG_DEBUG);\n\n    av_register_all();\n    avformat_network_init();\n\n    avformat_alloc_output_context2(&amp;outContext, NULL, &quot;rtsp&quot;, url);\n\n    if (!outContext) {\n        av_log(NULL, AV_LOG_FATAL, &quot;Could not allocate an output context for &#039;%s&#039;.\\n&quot;, url);\n        return 0;\n    }\n\n    if (!outContext-&gt;oformat) {\n        av_log(NULL, AV_LOG_FATAL, &quot;Could not create the output format for &#039;%s&#039;.\\n&quot;, url);\n        return 0;\n    }\n\n    video_st = add_stream(outContext, &amp;video_codec, VIDEO_CODEC_ID);\n\n    \/* Now that all the parameters are set, we can open the video codec and allocate the necessary encode buffers. *\/\n    if (video_st) {\n        av_log(NULL, AV_LOG_DEBUG, &quot;Video stream codec %s.\\n &quot;, avcodec_get_name(video_st-&gt;codec-&gt;codec_id));\n\n        ret = open_video(outContext, video_codec, video_st);\n        if (ret &lt; 0) {\n            av_log(NULL, AV_LOG_FATAL, &quot;Open video stream failed.\\n&quot;);\n            return 0;\n        }\n    }\n    else {\n        av_log(NULL, AV_LOG_FATAL, &quot;Add video stream for the codec &#039;%s&#039; failed.\\n&quot;, avcodec_get_name(VIDEO_CODEC_ID));\n        return 0;\n    }\n\n    av_dump_format(outContext, 0, url, 1);\n\n    ret = avformat_write_header(outContext, NULL);\n    if (ret != 0) {\n        av_log(NULL, AV_LOG_ERROR, &quot;Failed to connect to RTSP server for &#039;%s&#039;.\\n&quot;, url);\n        return 0;\n    }\n\n    while (video_st) {\n        frameCount++;\n\n        ret = write_video_frame(outContext, video_st, frameCount);\n\n        if (ret &lt; 0) {\n            av_log(NULL, AV_LOG_ERROR, &quot;Write video frame failed.\\n&quot;, url);\n            return 0;\n        }\n    }\n\n    avcodec_close(video_st-&gt;codec);\n    av_free(src_picture.data[0]);\n    av_free(dst_picture.data[0]);\n    av_frame_free(&amp;frame);\n\n    avformat_free_context(outContext);\n\n    printf(&quot;finished.\\n&quot;);\n\n    return 0;\n}<\/code><\/pre>\n<h3>3.4 .\/src\/v4l2_stream.h<\/h3>\n<pre><code class=\"language-c\">#include &lt;stdio.h&gt;  \n#include &lt;stdlib.h&gt;  \n#include &lt;string.h&gt;  \n#include &lt;assert.h&gt;  \n#include &lt;fcntl.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;sys\/stat.h&gt;\n#include &lt;sys\/types.h&gt;\n#include &lt;sys\/ioctl.h&gt;\n#include &lt;asm\/types.h&gt;\n#include &lt;sys\/mman.h&gt;\n#include &lt;linux\/videodev2.h&gt;\n#include &lt;errno.h&gt;\n#include &lt;stdint.h&gt;\n#include &lt;stdbool.h&gt;\n\n#ifdef __cplusplus\nextern &quot;C&quot; {\n#endif\n\n#define _Video_Device_SP_ &quot;\/dev\/video9&quot;\n#define _V4L2_SP_PIX_FMT_ V4L2_PIX_FMT_YUYV\n#define _V4L2_SP_BUF_TYPE_ V4L2_BUF_TYPE_VIDEO_CAPTURE\n#define _V4L2_SP_BUF_REQ_COUNT_ 60\n\nextern const int v4l2_sp_capture_width;\nextern const int v4l2_sp_capture_height;\n\n\/\/ device number for v4l2 single-planar\nextern int v4l2_sp_fd;\n\n\/\/ global memory for mmap v4l2 query buffer, 5 block\nextern uint8_t* v4l2_sp_globalBuffer[_V4L2_SP_BUF_REQ_COUNT_];\n\n\/\/ global memory buffer length\nextern int v4l2_sp_globalBufferLength;\n\n\/\/ to specify which memory is currently available\nextern int v4l2_sp_globalBufferIndex;\n\n\/\/ to specify globalBuffer length\nextern int v4l2_sp_globalBufferLength;\n\n\/\/ stop capture or not\nextern bool v4l2_sp_captureLoopFlag;\n\nint V4L2_SP_Streaming();\n\nvoid* ThreadEntryPoint(void* arg);\n\n#ifdef __cplusplus\n}\n#endif<\/code><\/pre>\n<h3>3.5 .\/src\/v4l2_stream.c<\/h3>\n<pre><code class=\"language-c\">#include &quot;v4l2_stream.h&quot;\n\nconst int v4l2_sp_capture_width = 640;\nconst int v4l2_sp_capture_height = 480;\n\nint v4l2_sp_fd;\nuint8_t* v4l2_sp_globalBuffer[_V4L2_SP_BUF_REQ_COUNT_];\nint v4l2_sp_globalBufferLength;\nint v4l2_sp_globalBufferIndex;\n\nbool v4l2_sp_captureLoopFlag = true;\n\nint V4L2_SP_Streaming()\n{\n    \/* Step 0 : Pre-Define *\/\n    int retval = 0;     \/\/ V4L2_SP_Streaming return value\n    int ret = 0;        \/\/ ioctl check\n\n#if Print_Debug_Info\n    printf(&quot;V4L2_SP_Streaming(): Step 1 Open Device\\n&quot;);\n#endif\n    \/* Step 1 : Open Device *\/\n    v4l2_sp_fd = open(_Video_Device_SP_, O_RDWR);\n    if (v4l2_sp_fd == -1)\n    {\n        perror(&quot;V4L2_SP_Streaming() open device&quot;);\n        retval = 1;\n        goto out_return;\n    }\n\n#if Print_Debug_Info\n    printf(&quot;V4L2_SP_Streaming(): Step 2 Setting Capture Format\\n&quot;);\n#endif\n    \/* Step 2: Setting Capture Format *\/\n    struct v4l2_format format;\n    format.type = _V4L2_SP_BUF_TYPE_;\n    format.fmt.pix.width = v4l2_sp_capture_width;\n    format.fmt.pix.height = v4l2_sp_capture_height;\n    format.fmt.pix.pixelformat = _V4L2_SP_PIX_FMT_;\n\n    ret = ioctl(v4l2_sp_fd, VIDIOC_S_FMT, &amp;format);             \/\/ set FMT\n    if (ret == -1)\n    {\n        perror(&quot;V4L2_SP_Streaming() setting format&quot;);\n        retval = 2;\n        goto error_close;\n    }\n\n#if Print_Debug_Info\n    printf(&quot;V4L2_SP_Streaming(): Step 3 Checking FMT Setting\\n&quot;);\n#endif\n    \/* Step 3: Checking FMT Setting *\/\n    ret = ioctl(v4l2_sp_fd, VIDIOC_G_FMT, &amp;format);             \/\/ get FMT\n    if (ret == -1)\n    {\n        perror(&quot;V4L2_SP_Streaming() checking format&quot;);\n        retval = 3;\n        goto error_close;\n    }\n\n    if (format.fmt.pix.pixelformat == _V4L2_SP_PIX_FMT_)        \/\/ set FMT == get FMT ?\n    {\n        printf(&quot;ioctl VIDIOC_S_FMT sucessful\\n&quot;);\n    }\n    else\n    {\n        printf(&quot;ioctl VIDIOC_S_FMT failed\\n&quot;);\n        retval = 4;\n        goto error_close;\n    }\n\n#if Print_Debug_Info\n    printf(&quot;V4L2_SP_Streaming(): Step 4 Request Buffer\\n&quot;);\n#endif\n    \/* Step 4: Request Buffer *\/\n    struct v4l2_requestbuffers reqbuf;\n    reqbuf.count = _V4L2_SP_BUF_REQ_COUNT_;\n    reqbuf.type = _V4L2_SP_BUF_TYPE_;\n    reqbuf.memory = V4L2_MEMORY_MMAP;\n    ret = ioctl(v4l2_sp_fd, VIDIOC_REQBUFS, &amp;reqbuf);\n    if (-1 == ret)\n    {\n        perror(&quot;V4L2_SP_Streaming() reqeuset buff&quot;);\n        retval = 1;\n        goto error_close;\n    }\n\n#if Print_Debug_Info\n    printf(&quot;V4L2_SP_Streaming(): Step 5 mmap Buffer\\n&quot;);\n#endif\n    \/* Step 5: mmap Buffer *\/\n    struct v4l2_buffer buff;\n    buff.type = _V4L2_SP_BUF_TYPE_;\n    buff.memory = V4L2_MEMORY_MMAP;\n    for (int i = 0; i &lt; _V4L2_SP_BUF_REQ_COUNT_; ++i)\n    {\n        \/\/ query buf\n        buff.index = i;\n        ret = ioctl(v4l2_sp_fd, VIDIOC_QUERYBUF, &amp;buff);\n        if (ret == -1)\n        {\n            perror(&quot;V4L2_SP_Streaming() query&quot;);\n            retval = 2;\n            goto error_close;\n        }\n\n        printf(&quot;buf[%d]: len = %d offset: %d\\n&quot;, i, buff.length, buff.m.offset);\n\n        \/\/ mmap\n        v4l2_sp_globalBuffer[i] = mmap(NULL, buff.length, PROT_READ, MAP_SHARED, v4l2_sp_fd, buff.m.offset);\n        v4l2_sp_globalBufferLength = buff.length;\n        if (MAP_FAILED == v4l2_sp_globalBuffer[i])\n        {\n            perror(&quot;V4L2_SP_Streaming() mmap&quot;);\n            retval = 3;\n            goto error_munmap;\n        }\n\n        \/\/ queue\n        ret = ioctl(v4l2_sp_fd, VIDIOC_QBUF, &amp;buff);\n        if (-1 == ret)\n        {\n            perror(&quot;V4L2_SP_Streaming() queue&quot;);\n            retval = 4;\n            goto error_munmap;\n        }\n    }\n\n#if Print_Debug_Info\n    printf(&quot;V4L2_SP_Streaming(): Step 6 Start Streaming\\n&quot;);\n#endif\n    \/* Step 6: Start Streaming *\/\n    int on = _V4L2_SP_BUF_TYPE_;\n    ret = ioctl(v4l2_sp_fd, VIDIOC_STREAMON, &amp;on);\n    if (-1 == ret)\n    {\n        perror(&quot;V4L2_SP_Streaming() VIDIOC_STREAMON&quot;);\n        retval = 5;\n        goto error_munmap;\n    }\n\n#if Print_Debug_Info\n    printf(&quot;V4L2_SP_Streaming(): Step 7 Capture\\n&quot;);\n#endif\n    \/* Step 7: Capture *\/\n    while (v4l2_sp_captureLoopFlag)\n    {\n        \/\/ deque\n        ret = ioctl(v4l2_sp_fd, VIDIOC_DQBUF, &amp;buff);\n        if (ret != -1)\n        {\n            \/\/ updata global var\n            v4l2_sp_globalBufferIndex = buff.index;\n            v4l2_sp_globalBufferLength = buff.bytesused;\n\n            \/\/ queue\n            ret = ioctl(v4l2_sp_fd, VIDIOC_QBUF, &amp;buff);\n        }\n    }\n\n#if Print_Debug_Info\n    printf(&quot;V4L2_SP_Streaming(): Exit\\n&quot;);\n#endif\nerror_munmap:\n    for (int i = 0; i &lt; _V4L2_SP_BUF_REQ_COUNT_; ++i)\n    {\n        if (v4l2_sp_globalBuffer[i] != NULL)\n            munmap(v4l2_sp_globalBuffer[i], v4l2_sp_globalBufferLength);\n    }\n\nerror_close:\n    close(v4l2_sp_fd);\n\nout_return:\n    return retval;\n}\n\nvoid* ThreadEntryPoint(void* arg) {\n    int result = V4L2_SP_Streaming();\n    \/\/ \u4f7f\u7528 pthread_exit() \u8fd4\u56de int \u7c7b\u578b\u7684\u7ed3\u679c\n    pthread_exit((void*)result);\n    return NULL;\n}<\/code><\/pre>\n<h3>3.6 \u6ce8\u610f\u4e8b\u9879<\/h3>\n<p>&emsp;&emsp;\u5176\u4e2d<code>fill_yuv_image()<\/code>\u548c<code>v4l2\u91c7\u96c6<\/code>\u5e76\u6ca1\u6709\u4e0a\u9501\uff0c\u9700\u8981\u6ce8\u610f\u662f\u5426\u5b58\u5728IO\u51b2\u7a81\u3002<\/p>\n<h2>\u56db\u3001\u9884\u89c8\u548c\u4f18\u5316<\/h2>\n<h3>4.1 \u9884\u89c8<\/h3>\n<pre><code class=\"language-sh\"># on win10\n.\\ffplay.exe rtsp:\/\/192.168.50.84:8554\/stream<\/code><\/pre>\n<p>&emsp;&emsp;\u5bf9\u4e8e\u53ef\u80fd\u5b58\u5728\u7684\u5ef6\u8fdf\u7d2f\u79ef\u6211\u4f7f\u7528<code>-flags low_delay -vf setpts=0<\/code>\u6709\u6548\uff0c\u5176\u4ed6\u60c5\u51b5\u53ef\u4ee5\u53c2\u8003\uff1a<a href=\"https:\/\/stackoverflow.com\/questions\/16658873\/how-to-minimize-the-delay-in-a-live-streaming-with-ffmpeg\" target=\"_blank\"  rel=\"nofollow\" >stackoverflow<\/a>\u3002<\/p>\n<h3>4.2 \u53d1\u9001\u7aef\u95ee\u9898<\/h3>\n<p>&emsp;&emsp;\u53d1\u9001\u7aef\u6709\u5982\u4e0b\u4e24\u4e2a\u95ee\u9898\uff1a<\/p>\n<ol>\n<li>\u4e0a\u8ff03.6\u8282\u4e2d\u63d0\u5230\u7684\u4e92\u65a5\u95ee\u9898\u3002<\/li>\n<li>CPU\u8c03\u5ea6\u53ef\u80fd\u5f15\u8d77v4l2\u91c7\u96c6\u961f\u5217\u7684\u5361\u987f\u95ee\u9898\u3002<\/li>\n<\/ol>\n<p>&emsp;&emsp;\u5bf9\u4e8e\u95ee\u98981\u53ef\u4ee5\u901a\u8fc7\u4fee\u6539\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801\u5b9e\u73b0\uff0c\u6211\u4f7f\u752860\u7247\u7f13\u5b58\u201c\u907f\u514d\u201d\u4e86\u8be5\u95ee\u9898\uff0c\u5982\u679c\u9700\u8981\u5b8c\u5168\u907f\u514d\uff0c\u5219\u9700\u8981\u4e92\u65a5\u91cf\u548c\u5e7f\u64ad\u3002<\/p>\n<p>&emsp;&emsp;\u5bf9\u4e8e\u95ee\u98982\u5219\u4f7f\u7528<code>taskset<\/code>\u6765\u6307\u5b9a\u8fdb\u7a0b\u7684<code>affinity mas<\/code>\u3002<\/p>\n<pre><code class=\"language-sh\">taskset -a E * \ntaskset -a 1 demo<\/code><\/pre>\n<p>\u4e0a\u8ff0\u547d\u4ee4\u7684<code>demo<\/code>\u6307\u5b9a\u5230CPU0\u4e0a\u8fd0\u884c\uff0c\u5176\u4f59\u8fdb\u7a0b\u53ea\u80fd\u5728CPU1~3\u4e0a\u8fd0\u884c\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>&emsp;&emsp;\u73b0\u5728\u6211\u4eec\u9700\u8981\u5b9e\u73b0\u4e00\u4e2a\u7a0b\u5e8f\uff0c\u8be5\u7a0b\u5e8f\u8c03\u7528\u6444\u50cf\u5934\uff0c\u5e76\u5c06\u56fe\u50cf\u663e\u793a\u5230\u5c4f\u5e55\uff08\u5b89\u5353APP\uff09\u3002 # \u4e00\u822c\u5f00\u53d1\u5de5\u4f5c prog &#8230;<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[45],"tags":[241],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/posts\/957"}],"collection":[{"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/comments?post=957"}],"version-history":[{"count":1,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/posts\/957\/revisions"}],"predecessor-version":[{"id":958,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/posts\/957\/revisions\/958"}],"wp:attachment":[{"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/media?parent=957"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/categories?post=957"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/tags?post=957"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}