当前位置:   article > 正文

如何用海思HI3516DV300/3518EV200推流H.264_hi3518v100摄像头刷机

hi3518v100摄像头刷机

一开始尝试用hi3518EV200, 后来为了给自己加难度, 就买了个hi3516的板子, 看看差异多大, 结论是, 不大。。。
在这里插入图片描述

前期准备: 你得有一个3516或者3518的开发板(淘宝有卖各种开发板, 推荐一下易百纳, 但是整个板子没有支架, 摄像头都不能支楞起来, 怎么玩? 我就打印了一个非常潦草的支架, 如上图), 有对应的sdk, 有个安卓手机, 电脑上装了虚拟ubuntu14/16, 用于编译海思的应用, 安装Android Studio, 用于编译安卓APP.
然后有最起码的海思交叉编译的知识, 安卓APP的编译安装基础知识.

数据从海思板子上的GC2053摄像头, 从MIPI接口读出来>>>>>编码成H.264>>>>>>TCP发送到公网服务器>>>>>>服务器上的端口转发数据<<<<<<安卓端APP连接到公网<<<<<<<读到数据后解码播放.

不会画图, 将就看吧.

先说说目标, 简单来说, 就是为了我的4G小车的视频推流准备的,小车这边使用海思或者RK采集视像头信号,使用h.264压缩后,通过WiFi接4G的随身路由器推流到公网的固定ip的服务器, 然后再用手机连接我服务器的公网IP, 由公网上的服务器做一个中转, 手机上使用mediacodec+jni的方式解码。
一步步来, 首先, 海思上面跑一个截图摄像头信号, 参考那个著名的venc的sample程序, 并把数据通过TCP连接把数据帧发出去.
海思这部分技能三年前点花了4000块买了朱老师的海思教程才算入门。
海思部分的代码我放到了github, 把它复制到sdk的sample目录下面, 用make编译就好了

https://github.com/MontaukLaw/hi3516_venc_tcp.git

编译好的文件在smp目录下面, mipi_venc就是可执行文件
值得注意的就是

  1. 获取摄像头数据的部分请参考sample的vi部分
  2. 获取数据编码的部分参考venc的部分
  3. 拿到数据之后, 就是在venc的原本写入文件的部分, 改为将数据丢到一个链表里面进行缓存, 链表也非常简单, FIFO的小链表
  4. 然后用另一个进程取出链表中的头部数据, 一旦tcp连接建立, 就用write发送数据即可
  5. 理论上, APP跑起来的时候, 第一时间会去连接公网上的TCP服务器, 所以这部分出现延迟的可能性不大, 链表缓存中的数据最多是因为海思到公网这部分连接的网络延时, 不过值得观察一下缓存的大小.
  6. 分辨率应该就是调低延迟的一个主方向, 毕竟码流小了, 速度可能会更快点儿. 目前是1920*1080

接下来在公网的服务器上, 跑一个转流的程序, 因为海思跟手机都没有公网ip, 拜谁所赐呢?不敢细想。
海思的数据, 通过read海思连接的socketFd读取出来,再马上write到手机的socketFd上去,这部分还挺简单的, 不过也参考了b站上的黑马的linux网络编程的教程, 那位老师叫啥不知道, 但是真的讲得特别好。
重点是通过select管理多连接 。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <printf.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>

#define SERVER_TCP_PORT 54322  // tcp连接的端口
#define BUFF_SIZE 1024*10      // 缓存大小
// #define BUFF_SIZE 1400

int main(int argc, char *argv[]) {
    int i, j, n, nready;

    int nullFd = 0;   // 没有接收者连接的时候, 数据被丢弃
    int maxFd = 0;    // 
    int revFd = 0;    // 接收者的fd
    int listenFd, connFd;

    int senderFd = 0;
    int ret;
    struct timeval tv;
    long secNow = 0;
    long byteRate = 0;


    long totalSent = 0;
    nullFd = open("/dev/null", O_WRONLY);
    printf("null fd:%d\n", nullFd);

    char buf[BUFF_SIZE];
    struct sockaddr_in clientAddr, serverAddr;
    socklen_t clientAddrLen;
    listenFd = socket(AF_INET, SOCK_STREAM, 0);

    int opt = 1;
    setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    bzero(&serverAddr, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(SERVER_TCP_PORT);

    ret = bind(listenFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr));

    listen(listenFd, 128);

    fd_set rset, allset;
    maxFd = listenFd;

    FD_ZERO(&allset);
    FD_SET(listenFd, &allset);

    while (1) {
        rset = allset;
        nready = select(maxFd + 1, &rset, NULL, NULL, NULL);
        if (nready < 0) {
            printf("select error\n");
            continue;
        }

        if (FD_ISSET(listenFd, &rset)) {
            clientAddrLen = sizeof(clientAddr);
            connFd = accept(listenFd, (struct sockaddr *) &clientAddr, &clientAddrLen);
            if (connFd < 0) {
                printf("accept error\n");
                continue;
            }

            if (senderFd == 0) {
                senderFd = connFd;
                printf("sender connected first, fd is %d\n", senderFd);

            } else {
                revFd = connFd;
                printf("revFd connected, fd is %d\n", revFd);
            }

            FD_SET(connFd, &allset);
            if (maxFd < connFd) {
                maxFd = connFd;
                printf("maxFd:%d\n", maxFd);
            }

            if (0 == --nready) {
                continue;
            }
        }

        for (i = listenFd + 1; i <= maxFd; i++) {
            if (FD_ISSET(i, &rset)) {
                if ((n = read(i, buf, sizeof(buf))) == 0) {
                    if (i == revFd) {
                        revFd = 0;
                    } else if (i == senderFd) {
                        senderFd = 0;
                    }
                    printf("%d disconnected \n", i);
                    close(i);
                    FD_CLR(i, &allset);
                } else if (n > 0) {
                    // int writeFd = get_recv_fd(i, maxFd);
                    // printf("data from fd:%d write to %d\n", i, writeFd);
                    // write(writeFd, buf, n);
                    if (revFd != 0) {
                        totalSent += n;
                        // printf("sending :%ld to rev\n", totalSent);
                        byteRate += n;
                        gettimeofday(&tv, NULL);

                        if (tv.tv_sec != secNow) {

                            printf("total sent %ld br: %ld\n", totalSent, byteRate);

                            // printf("Seconds since Jan. 1, 1970: %ld\n", tv.tv_sec);
                            secNow = tv.tv_sec;
                            byteRate = 0;
                        }

                        write(revFd, buf, n);
                    } else {
                        printf("sending to null\n");
                        write(nullFd, buf, n);
                    }
                }
            }
        }
    }

    close(nullFd);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137

意思是如果没有接收端进行连接, 就写到/dev/null里面去, 就是扔掉了, 如果有连接, 就往连接的句柄当中写.
这里可以调试的就是接收发送的包大小, 调低延迟的一个观察方向.

之前是安卓端使用c语言的tcp接收,通过jni往上层传递, 其实是不是可以直接用java的socket?我干嘛要用jni呢???可能是花了5000块学的享学的安卓课程里面教我用ffmpeg解码,但是后来没用上。。。
但是用c连接TCP这个我可以啊, 就直接用jni了, 一开始用udp接收的时候, 一点问题都没有, 在内网延迟非常低.
后来改成Java的Socket来接收, 折腾了最少三天, 其实就是因为Java的那些BufferedReader读取的是char流,而不是byte流, 我又把char做了错误的强转, 导致无论如何出来的图像都不对, 解码器之前报错, 保存成文件, 雷神的SpecialVH264, 居然还能认出sps, pps帧, Elecard StreamEye Tools直接就死在当场, 后来在各端观察裸数据才发现,问题就在Java的转码上…
所以接数据的部分, 我直接就用了Java的InputStream, 它是可以直接read到byte数组的.
然后做分包, 确切的说是分帧, 因为mediacodec的input数据是一帧帧的, 话又说回来, 这部分我没仔细测试, 但是应该是这样, 如果可以直接丢数据包进去, 那可能还省去了很多功夫.
总之, 就是用Java找出数据中的sps, pps, sei, I帧跟P帧, 然后放入解码器, 再在output中对videoview进行渲染.
完整代码如下:
https://github.com/MontaukLaw/tcp_h254_decode_android.git

遗留的问题:

  1. 延迟高达300ms
  2. 分辨率太高
  3. 解码的部分可以直接丢数据包么?
  4. 安卓端APP会频繁的随机崩溃.
  5. 有人说webrtc公网150ms, 真的假的? 还有比我这样直来直去的方式更快的???
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/331742?site
推荐阅读
相关标签
  

闽ICP备14008679号