4、Android 手机端进行实时目标检测,并使用FFMPEG将检测的视频流推到服务器显示

基本思想:记录一下上次工业检测项目的Android推流代码,客户要求在终端检测结果推到服务器上,以最大化发挥终端的算力,而把服务器只当做中转站~

一、先将up的GitHub - nihui/ncnn-android-nanodet代码跑起来,导入nihui大佬的ncnn库和opencv-mobile库即可

二、在linux系统上构建Android的交叉编译环境1、Android 移植C++ 开发的第三方.so包和.a包_sxj731533730-CSDN博客_c++开发安卓

三、使用交叉编译环境去编译Android 版的ffmpeg静态包或者动态包 8、Linuix\Android进行视频获取和推流服务器(FFMPEG4.4&Android7.0)_sxj731533730-CSDN博客_android从服务器获取视频

(1)官网的x264代码貌似有问题,贴一下x264代码

git clone https://github.com/sxj731533730/x264.git

在linux系统进行X264进行交叉编译命令

#!/bin/bash
export NDK=/usr/local/android-ndk-r21e
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export API=21
 
function build_android
{
./configure \
    --prefix=$PREFIX \
    --disable-cli \
    --disable-asm \
    --enable-static \
    --enable-pic \
    --host=$my_host \
    --cross-prefix=$CROSS_PREFIX \
    --sysroot=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
 
make clean
make -j8
make install
}
 
 
#x86
PREFIX=./android/x86
my_host=i686-linux-android
export TARGET=i686-linux-android
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
CROSS_PREFIX=$TOOLCHAIN/bin/i686-linux-android-
build_android
 
#armeabi-v7a
PREFIX=./android/armeabi-v7a
my_host=armv7a-linux-android
export TARGET=armv7a-linux-androideabi
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
build_android
 
#arm64-v8a
PREFIX=./android/arm64-v8a
my_host=aarch64-linux-android
export TARGET=aarch64-linux-android
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
build_android
 
#x86_64
PREFIX=./android/x86_64
my_host=x86_64-linux-android
export TARGET=x86_64-linux-android
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android-
build_android
 

(2)ffmpeg代码

git clone https://github.com/FFmpeg/FFmpeg.git

需要修改一下 FFmpeg/libavutil/x86/asm.h

//#define HAVE_7REGS (ARCH_X86_64 || (HAVE_EBX_AVAILABLE && HAVE_EBP_AVAILABLE)) 注释掉
#define HAVE_7REGS (ARCH_X86_64 ) //更改为

编译命令

#!/bin/bash
export NDK=/usr/local/android-ndk-r21e 
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
 
function build_android
{
	 
	./configure \
		--prefix=$PREFIX \
		--enable-neon  \
		--enable-hwaccels  \
		--enable-gpl   \
		--disable-postproc \
		--disable-static \
		--enable-shared \
		--disable-debug \
		--enable-small \
		--enable-jni \
		--enable-mediacodec \
		--disable-doc \
		--enable-ffmpeg \
		--disable-ffplay \
		--disable-ffprobe \
		--disable-avdevice \
		--disable-doc \
		--disable-symver \
		--enable-libx264 \
		--enable-encoder=libx264 \
		--enable-nonfree \
		--enable-muxers \
		--enable-decoders \
		--enable-demuxers \
		--enable-parsers \
		--enable-protocols \
		--cross-prefix=$CROSS_PREFIX \
		--target-os=android \
		--arch=$ARCH \
		--cpu=$CPU \
		--cc=$CC \
		--cxx=$CXX \
		--enable-cross-compile \
		--sysroot=$SYSROOT \
		--extra-cflags="-I$X264_INCLUDE -Os -fpic $OPTIMIZE_CFLAGS" \
		--extra-ldflags="-lm -L$X264_LIB $ADDI_LDFLAGS" 
	 
	make clean
	make -j8
	make install
	echo "The Compilation of FFmpeg with x264 $CPU is completed"
}
 
 
 






#armv8-a
ARCH=arm64
CPU=armv8-a
API=21
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
 
BASE_PATH=/home/ubuntu
LIB_TARGET_ABI=arm64-v8a
 
 
X264_INCLUDE=$BASE_PATH/x264/android/$LIB_TARGET_ABI/include
X264_LIB=$BASE_PATH/x264/android/$LIB_TARGET_ABI/lib
 
build_android
 
cp $X264_LIB/libx264.a $PREFIX/lib
 
#armv7-a
ARCH=arm
CPU=armv7-a
API=21
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
 
BASE_PATH=/home/ubuntu
LIB_TARGET_ABI=armeabi-v7a
X264_INCLUDE=$BASE_PATH/x264/android/$LIB_TARGET_ABI/include
X264_LIB=$BASE_PATH/x264/android/$LIB_TARGET_ABI/lib
 
build_android
 
cp $X264_LIB/libx264.a $PREFIX/lib



#x86
ARCH=x86
CPU=x86
API=21
CC=$TOOLCHAIN/bin/i686-linux-android$API-clang
CXX=$TOOLCHAIN/bin/i686-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/i686-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
 
BASE_PATH=/home/ubuntu
LIB_TARGET_ABI=x86
 
 
X264_INCLUDE=$BASE_PATH/x264/android/$LIB_TARGET_ABI/include
X264_LIB=$BASE_PATH/x264/android/$LIB_TARGET_ABI/lib
 
build_android
 
cp $X264_LIB/libx264.a $PREFIX/lib
 
#x86_64
ARCH=x86_64
CPU=x86-64
API=21
CC=$TOOLCHAIN/bin/x86_64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/x86_64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU -mtune=intel -msse4.2 -mpopcnt -m64"
 
BASE_PATH=/home/ubuntu
LIB_TARGET_ABI=x86_64
X264_INCLUDE=$BASE_PATH/x264/android/$LIB_TARGET_ABI/include
X264_LIB=$BASE_PATH/x264/android/$LIB_TARGET_ABI/lib
 
build_android
 
cp $X264_LIB/libx264.a $PREFIX/lib


然后生成了库文件

ubuntu@ubuntu:~/FFmpeg/android$ tree -L 3
.
├── armv7-a
│   ├── bin
│   │   └── ffmpeg
│   ├── include
│   │   ├── libavcodec
│   │   ├── libavfilter
│   │   ├── libavformat
│   │   ├── libavutil
│   │   ├── libswresample
│   │   └── libswscale
│   ├── lib
│   │   ├── libavcodec.so
│   │   ├── libavfilter.so
│   │   ├── libavformat.so
│   │   ├── libavutil.so
│   │   ├── libswresample.so
│   │   ├── libswscale.so
│   │   ├── libx264.a
│   │   └── pkgconfig
│   └── share
│       └── ffmpeg
├── armv8-a
│   ├── bin
│   │   └── ffmpeg
│   ├── include
│   │   ├── libavcodec
│   │   ├── libavfilter
│   │   ├── libavformat
│   │   ├── libavutil
│   │   ├── libswresample
│   │   └── libswscale
│   ├── lib
│   │   ├── libavcodec.so
│   │   ├── libavfilter.so
│   │   ├── libavformat.so
│   │   ├── libavutil.so
│   │   ├── libswresample.so
│   │   ├── libswscale.so
│   │   ├── libx264.a
│   │   └── pkgconfig
│   └── share
│       └── ffmpeg
├── x86
│   ├── bin
│   │   └── ffmpeg
│   ├── include
│   │   ├── libavcodec
│   │   ├── libavfilter
│   │   ├── libavformat
│   │   ├── libavutil
│   │   ├── libswresample
│   │   └── libswscale
│   ├── lib
│   │   ├── libavcodec.so
│   │   ├── libavfilter.so
│   │   ├── libavformat.so
│   │   ├── libavutil.so
│   │   ├── libswresample.so
│   │   ├── libswscale.so
│   │   ├── libx264.a
│   │   └── pkgconfig
│   └── share
│       └── ffmpeg
└── x86-64
    ├── bin
    │   └── ffmpeg
    ├── include
    │   ├── libavcodec
    │   ├── libavfilter
    │   ├── libavformat
    │   ├── libavutil
    │   ├── libswresample
    │   └── libswscale
    ├── lib
    │   ├── libavcodec.so
    │   ├── libavfilter.so
    │   ├── libavformat.so
    │   ├── libavutil.so
    │   ├── libswresample.so
    │   ├── libswscale.so
    │   ├── libx264.a
    │   └── pkgconfig
    └── share
        └── ffmpeg

52 directories, 32 files

3、修改一下目录名称,ffmpeg的对应生成文件夹的名字

armv-8a  armv-7a  x86  x86-64

修改之后的名字

arm64-v8a  armeabi-v7a  x86  x86_64

我建立一个总的文件夹来放这些库 ffmpeg-mobile-20220107-android

4、然后在ncnn-android-nanodet 代码基础上,添加我们自己编译的ffmpeg库

需要在src目录下建立jniLibs/libs/目录,然后江对应的动态库提取出来,如下图所示

 在build.gradle添加

   sourceSets{
        main{
            jniLibs.srcDirs=["src/main/jniLibs/libs"]
        }
    }

 然后cmakelists.txt导入一下即可

project(nanodetncnn)

cmake_minimum_required(VERSION 3.10)

set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/opencv-mobile-4.5.4-android/sdk/native/jni)
find_package(OpenCV REQUIRED core imgproc)

set(ncnn_DIR ${CMAKE_SOURCE_DIR}/ncnn-20211208-android-vulkan/${ANDROID_ABI}/lib/cmake/ncnn)
find_package(ncnn REQUIRED)

include_directories(${CMAKE_SOURCE_DIR}/ffmpeg-mobile-20220107-android/${ANDROID_ABI}/include)


add_library(libavformat SHARED IMPORTED)
set_target_properties(libavformat PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/libs/${ANDROID_ABI}/libavformat.so)


add_library(libavcodec SHARED IMPORTED)
set_target_properties(libavcodec PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/libs/${ANDROID_ABI}/libavcodec.so)

add_library(libavfilter SHARED IMPORTED)
set_target_properties(libavfilter PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/libs/${ANDROID_ABI}/libavfilter.so)

add_library(libavutil SHARED IMPORTED)
set_target_properties(libavutil PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/libs/${ANDROID_ABI}/libavutil.so)

add_library(libswresample SHARED IMPORTED)
set_target_properties(libswresample PROPERTIES IMPORTED_LOCATION  ${CMAKE_SOURCE_DIR}/../jniLibs/libs/${ANDROID_ABI}/libswresample.so)

add_library(libswscale SHARED IMPORTED)
set_target_properties(libswscale PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/libs/${ANDROID_ABI}/libswscale.so)

add_library(X264 STATIC IMPORTED)
set_target_properties(X264 PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/libs/${ANDROID_ABI}/libx264.a)


add_library(nanodetncnn SHARED nanodetncnn.cpp nanodet.cpp ndkcamera.cpp)
find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log )

target_link_libraries(nanodetncnn
        ncnn
        ${OpenCV_LIBS}
        camera2ndk
        mediandk
        libavformat
        libavcodec
        libavfilter
        libavutil
        libswresample
        libswscale
        X264
        ${log-lib}

        )

因为整个ffmpeg库我直接拷贝过来了,头文件就混在另一个文件中,请自行选择位置导入

,下面是我的目录结构 

先测试一下,是否影响原来的功能,如果不影响,开始下一步,写推流pc端的代码 

四、在window10上搭建一个Nginx服务器  参考文档 http://nginx-win.ecsds.eu/download/documentation-pdf/nginx%20for%20Windows%20-%20documentation%201.8.pdf

(1)、下载压缩包 http://nginx-win.ecsds.eu/download/nginx%201.7.11.3%20Gryphon.zip 修改文件夹的名字 nginx_1.7.11.3_Gryphon并且解压到D盘

(2)、然后进入解压目录

G:\ffmpeg_demo>d:
D:\>cd nginx_1.7.11.3_Gryphon
D:\nginx_1.7.11.3_Gryphon>git clone https://github.com/arut/nginx-rtmp-module
Cloning into 'nginx-rtmp-module'...
remote: Enumerating objects: 4324, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 4324 (delta 1), reused 3 (delta 0), pack-reused 4317
Receiving objects: 100% (4324/4324), 3.11 MiB | 2.38 MiB/s, done.
Resolving deltas: 100% (2689/2689), done.

(3)在目录 nginx-1.7.11.3_Gryphon\conf下创建 nginx-win-rtmp.conf配置文件

#user  nobody;
# multiple workers works !
worker_processes  2;
 
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
 
#pid        logs/nginx.pid;
 
 
events {
    worker_connections  8192;
    # max value 32768, nginx recycling connections+registry optimization = 
    #   this.value * 20 = max concurrent connections currently tested with one worker
    #   C1000K should be possible depending there is enough ram/cpu power
    # multi_accept on;
}
 
rtmp {
    server {
        listen 1935;#监听端口,若被占用,可以更改
        chunk_size 4000;#上传flv文件块儿的大小
        application live { #创建一个叫live的应用
             live on;#开启live的应用
             allow publish 127.0.0.1;#
             allow play all;
        }
    }
}
 
http {
    #include      /nginx/conf/naxsi_core.rules;
    include       mime.types;
    default_type  application/octet-stream;
 
    #log_format  main  '$remote_addr:$remote_port - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
 
    #access_log  logs/access.log  main;
 
#     # loadbalancing PHP
#     upstream myLoadBalancer {
#         server 127.0.0.1:9001 weight=1 fail_timeout=5;
#         server 127.0.0.1:9002 weight=1 fail_timeout=5;
#         server 127.0.0.1:9003 weight=1 fail_timeout=5;
#         server 127.0.0.1:9004 weight=1 fail_timeout=5;
#         server 127.0.0.1:9005 weight=1 fail_timeout=5;
#         server 127.0.0.1:9006 weight=1 fail_timeout=5;
#         server 127.0.0.1:9007 weight=1 fail_timeout=5;
#         server 127.0.0.1:9008 weight=1 fail_timeout=5;
#         server 127.0.0.1:9009 weight=1 fail_timeout=5;
#         server 127.0.0.1:9010 weight=1 fail_timeout=5;
#         least_conn;
#     }
 
    sendfile        off;
    #tcp_nopush     on;
 
    server_names_hash_bucket_size 128;
 
## Start: Timeouts ##
    client_body_timeout   10;
    client_header_timeout 10;
    keepalive_timeout     30;
    send_timeout          10;
    keepalive_requests    10;
## End: Timeouts ##
 
    #gzip  on;
 
    server {
        listen       80;
        server_name  localhost;
 
        #charset koi8-r;
 
        #access_log  logs/host.access.log  main;
 
        ## Caching Static Files, put before first location
        #location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
        #    expires 14d;
        #    add_header Vary Accept-Encoding;
        #}
 
# For Naxsi remove the single # line for learn mode, or the ## lines for full WAF mode
        location / {
            #include    /nginx/conf/mysite.rules; # see also http block naxsi include line
            ##SecRulesEnabled;
        	  ##DeniedUrl "/RequestDenied";
	          ##CheckRule "$SQL >= 8" BLOCK;
	          ##CheckRule "$RFI >= 8" BLOCK;
	          ##CheckRule "$TRAVERSAL >= 4" BLOCK;
	          ##CheckRule "$XSS >= 8" BLOCK;
            root   html;
            index  index.html index.htm;
        }
 
# For Naxsi remove the ## lines for full WAF mode, redirect location block used by naxsi
        ##location /RequestDenied {
        ##    return 412;
        ##}
 
## Lua examples !
#         location /robots.txt {
#           rewrite_by_lua '
#             if ngx.var.http_host ~= "localhost" then
#               return ngx.exec("/robots_disallow.txt");
#             end
#           ';
#         }
 
        #error_page  404              /404.html;
 
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
 
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ .php$ {
        #    proxy_pass   http://127.0.0.1;
        #}
 
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ .php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000; # single backend process
        #    fastcgi_pass   myLoadBalancer; # or multiple, see example above
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        #    include        fastcgi_params;
        #}
 
        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /.ht {
        #    deny  all;
        #}
    }
 
 
    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;
 
    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
 
 
    # HTTPS server
    #
    #server {
    #    listen       443 ssl spdy;
    #    server_name  localhost;
 
    #    ssl                  on;
    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;
 
    #    ssl_session_timeout  5m;
 
    #    ssl_prefer_server_ciphers On;
    #    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    #    ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:ECDH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!eNULL:!MD5:!DSS:!EXP:!ADH:!LOW:!MEDIUM;
 
    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
 
}

(4)、然后执行,开启流媒体服务

D:\nginx_1.7.11.3_Gryphon>nginx.exe -c conf\nginx-win-rtmp.conf

(5)、使用ffmpeg推一个视频,使用smplayer播放器拉一下

G:\>ffmpeg -re -i cxk.mp4 -vcodec libx264 -acodec aac -f flv rtmp://localhost:1935/live/home
ffmpeg version 4.4-full_build-www.gyan.dev Copyright (c) 2000-2021 the FFmpeg developers
  built with gcc 10.2.0 (Rev6, Built by MSYS2 project)
  configuration: --enable-gpl --enable-version3 --enable-shared --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libdav1d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libglslang --enable-vulkan --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
  libavutil      56. 70.100 / 56. 70.100
  libavcodec     58.134.100 / 58.134.100
  libavformat    58. 76.100 / 58. 76.100
  libavdevice    58. 13.100 / 58. 13.100
  libavfilter     7.110.100 /  7.110.100
  libswscale      5.  9.100 /  5.  9.100
  libswresample   3.  9.100 /  3.  9.100
  libpostproc    55.  9.100 / 55.  9.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'cxk.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf56.40.101
    description     : Tencent APD MTS
  Duration: 00:01:02.98, start: 0.000000, bitrate: 1015 kb/s
  Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 852x480, 817 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)
    Metadata:
      handler_name    : VideoHandler
      vendor_id       : [0][0][0][0]
  Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 192 kb/s (default)
    Metadata:
      handler_name    : SoundHandler
      vendor_id       : [0][0][0][0]
Stream mapping:
  Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
  Stream #0:1 -> #0:1 (aac (native) -> aac (native))
Press [q] to stop, [?] for help
[libx264 @ 0000026d74fc25c0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0000026d74fc25c0] profile High, level 3.1, 4:2:0, 8-bit
[libx264 @ 0000026d74fc25c0] 264 - core 161 r3048 b86ae3c - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=12 lookahead_threads=2 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, flv, to 'rtmp://localhost:1935/live/home':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    description     : Tencent APD MTS
    encoder         : Lavf58.76.100
  Stream #0:0(und): Video: h264 ([7][0][0][0] / 0x0007), yuv420p(progressive), 852x480, q=2-31, 30 fps, 1k tbn (default)
    Metadata:
      handler_name    : VideoHandler
      vendor_id       : [0][0][0][0]
      encoder         : Lavc58.134.100 libx264
    Side data:
      cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
  Stream #0:1(und): Audio: aac (LC) ([10][0][0][0] / 0x000A), 48000 Hz, stereo, fltp, 128 kb/s (default)
    Metadata:
      handler_name    : SoundHandler
      vendor_id       : [0][0][0][0]
      encoder         : Lavc58.134.100 aac
[flv @ 0000026d74f7bc40] Failed to update header with correct duration.9.4kbits/s speed=0.98x
[flv @ 0000026d74f7bc40] Failed to update header with correct filesize.
frame= 1319 fps= 30 q=-1.0 Lsize=    5876kB time=00:00:44.18 bitrate=1089.6kbits/s speed=0.997x
video:5122kB audio:694kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.043856%
[libx264 @ 0000026d74fc25c0] frame I:7     Avg QP:21.82  size: 34098
[libx264 @ 0000026d74fc25c0] frame P:409   Avg QP:23.84  size:  8567
[libx264 @ 0000026d74fc25c0] frame B:903   Avg QP:27.87  size:  1663
[libx264 @ 0000026d74fc25c0] consecutive B-frames:  1.4% 17.7% 13.0% 67.9%
[libx264 @ 0000026d74fc25c0] mb I  I16..4:  5.2% 80.1% 14.7%
[libx264 @ 0000026d74fc25c0] mb P  I16..4:  1.3%  6.6%  1.4%  P16..4: 42.6% 17.8%  8.1%  0.0%  0.0%    skip:22.3%
[libx264 @ 0000026d74fc25c0] mb B  I16..4:  0.1%  0.4%  0.1%  B16..8: 31.3%  4.1%  0.9%  direct: 0.9%  skip:62.1%  L0:45.1% L1:48.3% BI: 6.6%
[libx264 @ 0000026d74fc25c0] 8x8 transform intra:71.7% inter:74.9%
[libx264 @ 0000026d74fc25c0] coded y,uvDC,uvAC intra: 67.6% 58.7% 16.4% inter: 10.6% 8.3% 0.2%
[libx264 @ 0000026d74fc25c0] i16 v,h,dc,p: 41% 22%  5% 32%
[libx264 @ 0000026d74fc25c0] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 32% 17% 10%  4%  6% 11%  6%  9%  6%
[libx264 @ 0000026d74fc25c0] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 27% 28%  9%  4%  6% 10%  5%  7%  4%
[libx264 @ 0000026d74fc25c0] i8c dc,h,v,p: 51% 17% 25%  6%
[libx264 @ 0000026d74fc25c0] Weighted P-Frames: Y:0.2% UV:0.0%
[libx264 @ 0000026d74fc25c0] ref P L0: 63.1% 20.4% 12.9%  3.6%  0.0%
[libx264 @ 0000026d74fc25c0] ref B L0: 93.7%  5.5%  0.8%
[libx264 @ 0000026d74fc25c0] ref B L1: 98.9%  1.1%
[libx264 @ 0000026d74fc25c0] kb/s:954.19
[aac @ 0000026d76e992c0] Qavg: 376.109

测试结果(注意电脑的防火墙一定要处于关闭状态)

测试结果

使用命令拉流

五、先使用代码在vs上测试一下环境可用性

main.cpp


#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
#include "rtmpframe.h"
using namespace std;
using namespace cv;
int main()
{

	RTMPFrame* rtmpItem = new RTMPFrame();
	//读取视频或摄像头
	VideoCapture capture("F:\\xck.mp4");

	if (capture.isOpened()) {
		std::cout << "video_width = " << capture.get(cv::CAP_PROP_FRAME_WIDTH) << std::endl;
		std::cout << "video_height = " << capture.get(cv::CAP_PROP_FRAME_HEIGHT) << std::endl;
		std::cout << "video_fps = " << capture.get(cv::CAP_PROP_FPS) << std::endl;
		std::cout << "video_total_fps = " << capture.get(cv::CAP_PROP_FRAME_COUNT) << std::endl;
	}
	else {
		std::cout << "the video file is normal" << std::endl;
		return -1;
	}
	rtmpItem->rtmp_init(capture.get(cv::CAP_PROP_FRAME_WIDTH), capture.get(cv::CAP_PROP_FRAME_HEIGHT));

	while (true)
	{
		Mat frame;
		capture >> frame;
		if (frame.empty())
			break;
		rtmpItem->rtmp_sender_frame(frame.data, frame.cols, frame.rows, frame.elemSize());
		imshow("frame", frame);
		waitKey(1);
	}
	delete rtmpItem;
	return 0;
}

rtmpframe.h

#pragma once
#pragma once
//
// Created by PHILIPS on 1/7/2022.
//

#ifndef NCNN_ANDROID_NANODET_RTMPFRAME_H
#define NCNN_ANDROID_NANODET_RTMPFRAME_H
#include<iostream>

extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}


class RTMPFrame {
public:
    RTMPFrame();
    ~RTMPFrame();
public:
    //1 、推流地址
    const char* out_url = "rtmp://192.168.10.151:1935/live/livestream";
private:
    //2 、初始化上下文
    SwsContext* sws_context;
    // 3、输出的数据结构
    AVFrame* yuv;
    // 4 初始化编码器
    AVCodec* codec;
    //5、初始化编码器上下文
    AVCodecContext* codec_context;
    //6、初始化编码器选项
    AVDictionary* codec_options;
    //初始化格式上下文
    AVFormatContext* format_context;
    //
    AVStream* vs;
    //
    AVPacket pack;
    int fps;
    int vpts;
    uint8_t* in_data[AV_NUM_DATA_POINTERS] = { 0 };
    int in_size[AV_NUM_DATA_POINTERS] = { 0 };
    char buf[1024] = { 0 };
public:
    int rtmp_sender_frame(unsigned char* frameData, int frameCols, int frameRows, int frameElemSize);
    int rtmp_init(int width, int height);

};

#endif //NCNN_ANDROID_NANODET_RTMPFRAME_H

rtmpframe.cpp

//
// Created by sxj731533730  on 1/7/2022.
//

#include "rtmpframe.h"

RTMPFrame::RTMPFrame() {
    //1、初始化推流地址

    //2 、初始化上下文
    sws_context = NULL;
    // 3、输出的数据结构
    yuv = NULL;
    // 4 初始化编码器
    codec = NULL;
    //5、初始化编码器上下文
    codec_context = NULL;
    //6、初始化编码器选项
    codec_options = NULL;
    //初始化格式上下文
    format_context = NULL;
    //
    vs = NULL;
    //
    fps = 25;
    vpts = 0;

}



RTMPFrame::~RTMPFrame() {
    av_dict_free(&codec_options);
    avcodec_free_context(&codec_context);
    av_frame_free(&yuv);
    avio_close(format_context->pb);
    avformat_free_context(format_context);
    sws_freeContext(sws_context);
}




int RTMPFrame::rtmp_init(int width, int height) {

    // 注册所有网络协议
    avformat_network_init();

    // 2.初始化格式转换上下文
    sws_context = sws_getCachedContext(sws_context,
        width, height, AV_PIX_FMT_BGR24,    // 源格式
        width, height, AV_PIX_FMT_YUV420P,  // 目标格式
        SWS_BICUBIC,    // 尺寸变化使用算法
        0, 0, 0);

    if (NULL == sws_context) {

        std::cout << "sws_getCachedContext error" << std::endl;
        return -1;
    }

    // 3.初始化输出的数据结构
    yuv = av_frame_alloc();
    yuv->format = AV_PIX_FMT_YUV420P;
    yuv->width = width;
    yuv->height = height;
    yuv->pts = 0;

    // 分配yuv空间
    int ret_code = av_frame_get_buffer(yuv, 32);
    if (0 != ret_code) {

        std::cout << "yuv init error" << std::endl;
        return -1;
    }

    // 4.初始化编码上下文
    // 4.1找到编码器
    const  AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (NULL == codec) {

        std::cout << "Can't find h264 encoder" << std::endl;
        return -1;
    }

    // 4.2创建编码器上下文
    codec_context = avcodec_alloc_context3(codec);
    if (NULL == codec_context) {

        std::cout << "avcodec_alloc_context3 failed" << std::endl;
        return -1;
    }

    // 4.3配置编码器参数
    // vc->flags           |= AV_CODEC_FLAG_GLOBAL_HEADER;
    codec_context->codec_id = codec->id;
    codec_context->thread_count = 8;

    // 压缩后每秒视频的bit流 50k
    codec_context->bit_rate = 50 * 1024 * 8;
    codec_context->width = width;
    codec_context->height = height;
    codec_context->time_base = { 1, fps };
    codec_context->framerate = { fps, 1 };

    // 画面组的大小,多少帧一个关键帧
    codec_context->gop_size = 50;
    codec_context->max_b_frames = 0;
    codec_context->pix_fmt = AV_PIX_FMT_YUV420P;
    codec_context->qmin = 10;
    codec_context->qmax = 51;


    //(baseline | high | high10 | high422 | high444 | main)
    av_dict_set(&codec_options, "profile", "baseline", 0);
    av_dict_set(&codec_options, "preset", "superfast", 0);
    av_dict_set(&codec_options, "tune", "zerolatency", 0);

    // 4.4打开编码器上下文
    ret_code = avcodec_open2(codec_context, codec, &codec_options);
    if (0 != ret_code) {
        av_strerror(ret_code, buf, sizeof(buf));

        std::cout << "avcodec_open2 format_context failed" << std::endl;
        return -1;
    }



    // 5.输出封装器和视频流配置
    // 5.1创建输出封装器上下文
    // rtmp flv封装器

    ret_code = avformat_alloc_output_context2(&format_context, 0, "flv", out_url);
    if (0 != ret_code) {
        av_strerror(ret_code, buf, sizeof(buf));

        std::cout << " avformat_alloc_output_context2 format_context error!" << std::endl;
        return -1;
    }



    // 5.2添加视频流
    vs = avformat_new_stream(format_context, NULL);
    if (NULL == vs) {

        std::cout << "avformat_new_stream avformat_new_stream failed" << std::endl;
        return -1;
    }

    vs->codecpar->codec_tag = 0;
    // 从编码器复制参数
    avcodec_parameters_from_context(vs->codecpar, codec_context);
    av_dump_format(format_context, 0, out_url, 1);

    // 打开rtmp 的网络输出IO
    ret_code = avio_open(&format_context->pb, out_url, AVIO_FLAG_WRITE);
    if (0 != ret_code) {
        av_strerror(ret_code, buf, sizeof(buf));

        std::cout << "avio_open format_context failed" << std::endl;
        return -1;
    }

    // 写入封装头
    ret_code = avformat_write_header(format_context, NULL);
    if (0 != ret_code) {
        av_strerror(ret_code, buf, sizeof(buf));

        std::cout << "avformat_write_header format_context fail" << std::endl;
        return -1;
    }


    memset(&pack, 0, sizeof(pack));

    std::cout << "init rtmp successfully" << std::endl;
    return 0;

}

int RTMPFrame::rtmp_sender_frame(unsigned char* frameData, int frameCols, int frameRows, int frameElemSize) {

    if (frameData == NULL)
    {
        std::cout << "currentFrame is null" << std::endl;

        return -1;
    }

    // rgb to yuv
    in_data[0] = frameData;
    // 一行(宽)数据的字节数
    in_size[0] = frameCols * frameElemSize;
    int ret_code = sws_scale(sws_context, in_data, in_size, 0, frameRows,
        yuv->data, yuv->linesize);
    if (ret_code <= 0) {
        av_strerror(ret_code, buf, sizeof(buf));

        std::cout << "sws_context is fail " << std::endl;
        return -1;
    }

    // h264编码
    yuv->pts = vpts;
    vpts++;
    if (vpts < 0)
    {
        vpts = 0;
    }
    ret_code = avcodec_send_frame(codec_context, yuv);
    if (0 != ret_code) {
        av_strerror(ret_code, buf, sizeof(buf));
        std::cout << "codec_context is fail " << std::endl;

        return -1;
    }

    ret_code = avcodec_receive_packet(codec_context, &pack);

    if (0 != ret_code || pack.buf != nullptr) {//
        ;
    }
    else {
        av_strerror(ret_code, buf, sizeof(buf));

        std::cout << "avcodec_receive_packet is fail " << std::endl;
        return -1;
    }

    // 推流
    pack.pts = av_rescale_q(pack.pts, codec_context->time_base, vs->time_base);
    pack.dts = av_rescale_q(pack.dts, codec_context->time_base, vs->time_base);
    pack.duration = av_rescale_q(pack.duration,
        codec_context->time_base,
        vs->time_base);
    ret_code = av_interleaved_write_frame(format_context, &pack);
    if (0 != ret_code)
    {
        av_strerror(ret_code, buf, sizeof(buf));

        std::cout << "av_interleaved_write_frame format_context is fail " << std::endl;
        return -1;
    }
    av_packet_unref(&pack);
    std::cout << "rtmp frame successfully " << std::endl;

    return 0;


}



测试结果

六、然后开始集成Android的推流代码黏上就行 ,参考我这篇博客 代码改一下就行 8、Linuix\Android进行视频获取和推流服务器(FFMPEG4.4&Android7.0)_sxj731533730-CSDN博客_android从服务器获取视频

在AndroidManifest.xml中添加获取网络权限

​<uses-permission android:name="android.permission.INTERNET"> </uses-permission>

然后Android代码集成进去就行

贴个up的接口文件nanodetncnn.cpp

// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#include <android/asset_manager_jni.h>
#include <android/native_window_jni.h>
#include <android/native_window.h>

#include <android/log.h>

#include <jni.h>

#include <string>
#include <vector>

#include <platform.h>
#include <benchmark.h>

#include "nanodet.h"

#include "ndkcamera.h"

#include "rtmpframe.h"

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#if __ARM_NEON
#include <arm_neon.h>
#endif // __ARM_NEON

static int draw_unsupported(cv::Mat& rgb)
{
    const char text[] = "unsupported";

    int baseLine = 0;
    cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 1.0, 1, &baseLine);

    int y = (rgb.rows - label_size.height) / 2;
    int x = (rgb.cols - label_size.width) / 2;

    cv::rectangle(rgb, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
                    cv::Scalar(255, 255, 255), -1);

    cv::putText(rgb, text, cv::Point(x, y + label_size.height),
                cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 0));

    return 0;
}

static int draw_fps(cv::Mat& rgb)
{
    // resolve moving average
    float avg_fps = 0.f;
    {
        static double t0 = 0.f;
        static float fps_history[10] = {0.f};

        double t1 = ncnn::get_current_time();
        if (t0 == 0.f)
        {
            t0 = t1;
            return 0;
        }

        float fps = 1000.f / (t1 - t0);
        t0 = t1;

        for (int i = 9; i >= 1; i--)
        {
            fps_history[i] = fps_history[i - 1];
        }
        fps_history[0] = fps;

        if (fps_history[9] == 0.f)
        {
            return 0;
        }

        for (int i = 0; i < 10; i++)
        {
            avg_fps += fps_history[i];
        }
        avg_fps /= 10.f;
    }

    char text[32];
    sprintf(text, "FPS=%.2f", avg_fps);

    int baseLine = 0;
    cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);

    int y = 0;
    int x = rgb.cols - label_size.width;

    cv::rectangle(rgb, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
                    cv::Scalar(255, 255, 255), -1);

    cv::putText(rgb, text, cv::Point(x, y + label_size.height),
                cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));

    return 0;
}

static NanoDet* g_nanodet = 0;
static RTMPFrame *g_rtmpframe=0;
static ncnn::Mutex lock;

class MyNdkCamera : public NdkCameraWindow
{
public:
    virtual void on_image_render(cv::Mat& rgb) const;
};

void MyNdkCamera::on_image_render(cv::Mat& rgb) const
{
    // nanodet
    {
        ncnn::MutexLockGuard g(lock);

        if (g_nanodet)
        {
            std::vector<Object> objects;
            g_nanodet->detect(rgb, objects);

            g_nanodet->draw(rgb, objects);
            if(g_rtmpframe){
                cv::Mat rgbFrame;
                cv::resize(rgb,rgbFrame,cv::Size(1920,1080));
                g_rtmpframe->rtmp_sender_frame(rgbFrame.data,rgbFrame.cols,rgbFrame.rows,rgbFrame.elemSize());
            }

        }
        else
        {
            draw_unsupported(rgb);
        }
    }

    draw_fps(rgb);
}

static MyNdkCamera* g_camera = 0;

extern "C" {

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "JNI_OnLoad");

    g_camera = new MyNdkCamera;

    return JNI_VERSION_1_4;
}

JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
{
    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "JNI_OnUnload");

    {
        ncnn::MutexLockGuard g(lock);

        delete g_nanodet;
        g_nanodet = 0;
        delete g_rtmpframe;
        g_rtmpframe=0;
    }

    delete g_camera;
    g_camera = 0;
}

// public native boolean loadModel(AssetManager mgr, int modelid, int cpugpu);
JNIEXPORT jboolean JNICALL Java_com_tencent_nanodetncnn_NanoDetNcnn_loadModel(JNIEnv* env, jobject thiz, jobject assetManager, jint modelid, jint cpugpu)
{
    if (modelid < 0 || modelid > 6 || cpugpu < 0 || cpugpu > 1)
    {
        return JNI_FALSE;
    }

    AAssetManager* mgr = AAssetManager_fromJava(env, assetManager);

    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "loadModel %p", mgr);

    const char* modeltypes[] =
    {
        "m",
        "m-416",
        "g",
        "ELite0_320",
        "ELite1_416",
        "ELite2_512",
        "RepVGG-A0_416"
    };

    const int target_sizes[] =
    {
        320,
        416,
        416,
        320,
        416,
        512,
        416
    };

    const float mean_vals[][3] =
    {
        {103.53f, 116.28f, 123.675f},
        {103.53f, 116.28f, 123.675f},
        {103.53f, 116.28f, 123.675f},
        {127.f, 127.f, 127.f},
        {127.f, 127.f, 127.f},
        {127.f, 127.f, 127.f},
        {103.53f, 116.28f, 123.675f}
    };

    const float norm_vals[][3] =
    {
        {1.f / 57.375f, 1.f / 57.12f, 1.f / 58.395f},
        {1.f / 57.375f, 1.f / 57.12f, 1.f / 58.395f},
        {1.f / 57.375f, 1.f / 57.12f, 1.f / 58.395f},
        {1.f / 128.f, 1.f / 128.f, 1.f / 128.f},
        {1.f / 128.f, 1.f / 128.f, 1.f / 128.f},
        {1.f / 128.f, 1.f / 128.f, 1.f / 128.f},
        {1.f / 57.375f, 1.f / 57.12f, 1.f / 58.395f}
    };

    const char* modeltype = modeltypes[(int)modelid];
    int target_size = target_sizes[(int)modelid];
    bool use_gpu = (int)cpugpu == 1;

    // reload
    {
        ncnn::MutexLockGuard g(lock);

        if (use_gpu && ncnn::get_gpu_count() == 0)
        {
            // no gpu
            delete g_nanodet;
            g_nanodet = 0;
            delete g_rtmpframe;
            g_rtmpframe=0;
        }
        else
        {
            if (!g_nanodet)
                g_nanodet = new NanoDet;
            g_nanodet->load(mgr, modeltype, target_size, mean_vals[(int)modelid], norm_vals[(int)modelid], use_gpu);

            if (!g_rtmpframe)
                g_rtmpframe = new RTMPFrame;
            g_rtmpframe->rtmp_init( 1920,1080);

        }
    }

    return JNI_TRUE;
}

// public native boolean openCamera(int facing);
JNIEXPORT jboolean JNICALL Java_com_tencent_nanodetncnn_NanoDetNcnn_openCamera(JNIEnv* env, jobject thiz, jint facing)
{
    if (facing < 0 || facing > 1)
        return JNI_FALSE;

    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "openCamera %d", facing);

    g_camera->open((int)facing);

    return JNI_TRUE;
}

// public native boolean closeCamera();
JNIEXPORT jboolean JNICALL Java_com_tencent_nanodetncnn_NanoDetNcnn_closeCamera(JNIEnv* env, jobject thiz)
{
    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "closeCamera");

    g_camera->close();

    return JNI_TRUE;
}

// public native boolean setOutputWindow(Surface surface);
JNIEXPORT jboolean JNICALL Java_com_tencent_nanodetncnn_NanoDetNcnn_setOutputWindow(JNIEnv* env, jobject thiz, jobject surface)
{
    ANativeWindow* win = ANativeWindow_fromSurface(env, surface);

    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "setOutputWindow %p", win);

    g_camera->set_window(win);

    return JNI_TRUE;
}

}

另外两个文件填进去,cmakelists.txt引用一下就好

手机端的测试结果

 电脑端在开启的nginx过程中,拉取得流进行实时播放 测试结果 window10 拉流有点失真,原因在于手机显示画面是480*640  我拉成了1920*1080 失真了

 代码上传github,分为一个我编译的ffmpeg第三方库和一个推流的代码

ffmpeg4.4库: https://github.com/sxj731533730/ffmpeg4.4-mobile-20220107-android.git

推流实时检测代码:https://github.com/sxj731533730/ncnn-ffmpeg-android-nanodet.git

参考:

GitHub - nihui/ncnn-android-nanodet

Releases · Tencent/ncnn · GitHub

解决Android NDK编译FFmpeg 4.2.2的x86 cpu版时的问题 – K-Res的Blog
8、Linuix\Android进行视频获取和推流服务器(FFMPEG4.4&Android7.0)_sxj731533730-CSDN博客_android从服务器获取视频

GitHub - nihui/opencv-mobile: The minimal opencv for Android, iOS, ARM Linux, Windows, Linux, MacOS, WebAssembly

版权声明:本文为CSDN博主「sxj731533730」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sxj731533730/article/details/122270676

sxj731533730

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐