• 正文
    • 準(zhǔn)備工作
    • 調(diào)試錄像
    • 寫個(gè)代碼錄像
    • 錄像文件播放
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

海納思(Hi3798MV300)機(jī)頂盒遇到海思攝像機(jī)

2小時(shí)前
192
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

周末折騰了一下這個(gè)海思機(jī)頂盒,家里還有個(gè)海思的攝像機(jī)模組開發(fā)板,結(jié)合機(jī)頂盒來做個(gè)錄像。

準(zhǔn)備工作

    1. 海納斯機(jī)頂盒2. 攝像機(jī)模組3. 兩根網(wǎng)線、兩個(gè)電源、路由器4. 一塊64G固態(tài)硬盤

攝像機(jī)模組和機(jī)頂盒都接入路由器的LAN口,確保網(wǎng)絡(luò)正常通信

道具

攝像機(jī)模組

調(diào)試錄像

攝像機(jī)模組

攝像機(jī)模組里的程序其實(shí)是基于海思的SDK里的demo稍作修改而成,沒有做太復(fù)雜的功能,只加入了RTSP,對(duì)外提供RTSP接口服務(wù)。
這里用的rtsp服務(wù)的庫(kù)代碼比較好用,源碼鏈接:https://gitee.com/fensnote/RtspServer

在電腦上用VLC測(cè)試?yán)鞑シ牛?/p>

VLC

海納斯盒子錄像

關(guān)于錄像,這里只是實(shí)現(xiàn)簡(jiǎn)單的文件存儲(chǔ)、循環(huán)覆蓋,并不是專業(yè)的錄像,專業(yè)錄像里會(huì)做的比較復(fù)雜。

    1. 直接用Ffmpeg命令行錄取數(shù)據(jù)到文件里,為了方便播放保存為MP4文件。2. 寫代碼實(shí)現(xiàn)rtsp拉流存儲(chǔ),可以自己定義傳參。

Ffmpeg錄像

這個(gè)比較簡(jiǎn)單,一條命令即可,不過直接采用命令錄像沒法指定實(shí)現(xiàn)循環(huán)覆蓋,要想實(shí)現(xiàn)可以再寫個(gè)腳本取定時(shí)檢測(cè)錄像文件的個(gè)數(shù)。
首先需要先下載安裝Ffmpeg:

sudo?apt install ffmpeg

安裝日志

我這里已經(jīng)安裝過了。

接下來就用可以執(zhí)行錄像了:

ffmpeg -rtsp_transport tcp -i rtsp://192.168.2.168:41667/live -c copy -f segment -segment_time 60 stream_piece_%d.mp4

這條命令里是指定了錄像時(shí)長(zhǎng)60秒,即一分鐘切換一個(gè)文件。

ffmpeg

如下截圖,錄取一分鐘后已切換文件,1分鐘錄像數(shù)據(jù)15M,數(shù)據(jù)量挺大了:

錄像上傳上來播放一下看看:

上傳

播放:

使用win11的系統(tǒng)播放器就可以播放

播放

寫個(gè)代碼錄像

這里選用了go語言來寫這個(gè)錄像代碼,是因?yàn)間o語言的音視頻、網(wǎng)絡(luò)相關(guān)的庫(kù)實(shí)在太多,比較好用,代碼量也不大,可以提需求讓AI去寫,AI寫的基本上稍作修改測(cè)試幾次就可以用了。
Go還有個(gè)好處就是靜態(tài)編譯,真正的跨平臺(tái),一次編譯,CPU架構(gòu)一樣都可以運(yùn)行,感覺缺點(diǎn)就是可執(zhí)行文件比較大。

這里采用的gortsplib,源碼地址:https://gitee.com/fensnote/gortsplib.git

可以基于gortsplib/examples下的client-play-format-h264-save-to-disk示例代碼做修改:

復(fù)制

我復(fù)制了命名為client-play-format-h264-save-to-disk-file,在這里修改,下面代碼是調(diào)試完成的代碼:

package?main

import?(
? ??"flag"
? ??"fmt"
? ??"log"
? ??"os"
? ??"os/signal"
? ??"syscall"
? ??"time"

? ??"github.com/bluenviron/gortsplib/v4"
? ??"github.com/bluenviron/gortsplib/v4/pkg/base"
? ??"github.com/bluenviron/gortsplib/v4/pkg/format"
? ??"github.com/bluenviron/gortsplib/v4/pkg/format/rtph264"
? ??"github.com/pion/rtp"
)

const?(
? ? filePrefix =?"rec"? ??// 文件名前綴
? ? fileSuffix =?".mp4"? ?// 文件名后綴
)

func?main()?{
? ??// Define command line flags
? ? rtspURL := flag.String("r",?"",?"RTSP URL")
? ? maxFilesPtr := flag.Int("c",?0,?"文件數(shù)")
? ? startFileNumPtr := flag.Int("s",?0,?"起始文件編號(hào)")
? ? durationPtr := flag.Int("t",?60,?"單個(gè)文件錄像時(shí)長(zhǎng)")
? ? modePtr := flag.String("m",?"loop",?"錄像模式,單次模式:"once",循環(huán)模式:"loop", 注意要加雙引號(hào)")

? ??// Parse command line flags
? ? flag.Parse()

? ??// Check if the required arguments are provided
? ??if?*rtspURL ==?""?|| *maxFilesPtr ==?0?{?//|| *startFileNumPtr == 0 ? ??
? ? ? ? flag.PrintDefaults()?// Print usage information
? ? ? ? log.Fatal("Missing required command line arguments")
? ? }

? ??if?*startFileNumPtr <?0?|| *startFileNumPtr > *maxFilesPtr {
? ? ? ? log.Fatalf("起始文件編號(hào)必須是0~%d", *maxFilesPtr)
? ? }

? ??// Check if the RTSP URL is valid
? ? u, err := base.ParseURL(*rtspURL)
? ??if?err !=?nil?{
? ? ? ? log.Fatalf("無效的RTSP URL: %v", err)
? ? }

? ? c := gortsplib.Client{}

? ??// Connect to the server
? ? err = c.Start(u.Scheme, u.Host)
? ??if?err !=?nil?{
? ? ? ? log.Fatalf("連接 RTSP server 失敗: %v", err)
? ? }
// ? ?defer c.Close()

? ??// Find available medias
? ? desc, _, err := c.Describe(u)
? ??if?err !=?nil?{
? ? ? ? log.Fatalf("Failed to describe RTSP stream: %v", err)
? ? }

? ??// Find the H264 media and format
? ??var?forma *format.H264
? ? medi := desc.FindFormat(&forma)
? ??if?medi ==?nil?{
? ? ? ? log.Fatal("H264 media not found")
? ? }

? ??// Setup RTP -> H264 decoder
? ? rtpDec, err := forma.CreateDecoder()
? ??if?err !=?nil?{
? ? ? ? log.Fatalf("Failed to create H264 decoder: %v", err)
? ? }

? ??var?mpegtsMuxer *mpegtsMuxer
? ??var?fileCounter?int
? ??var?recordingStartTime time.Time
? ??// var bakPts int64;
? ??// var sub int

? ??// Create the first file immediately when the program starts
? ? fileCounter = *startFileNumPtr
? ? newFileName := fmt.Sprintf("%s%03d%s", filePrefix, fileCounter, fileSuffix)
? ? mpegtsMuxer = newMpegtsMuxer(newFileName, forma.SPS, forma.PPS)
? ? err = mpegtsMuxer.initialize()
? ??if?err !=?nil?{
? ? ? ? log.Fatalf("Failed to initialize MPEG-TS muxer: %v", err)
? ? }
? ? log.Printf("New file created: %s", newFileName)
? ? recordingStartTime = time.Now()

? ??// Setup a single media
? ? _, err = c.Setup(desc.BaseURL, medi,?0,?0)
? ??if?err !=?nil?{
? ? ? ? log.Fatalf("Failed to setup media: %v", err)
? ? }

? ??// Create a ticker to create a new file based on the specified duration
? ? duration := time.Duration(*durationPtr) * time.Second
? ? ticker := time.NewTicker(duration)
? ? duration = duration +?100000000// Add 200ms to the duration to ensure the ticker fires after the duration
? ??defer?ticker.Stop()

? ??// bakPts = 0
? ??// Called when a RTP packet arrives
? ? c.OnPacketRTP(medi, forma,?func(pkt *rtp.Packet)?{
? ? ? ??// Decode timestamp
? ? ? ? pts, ok := c.PacketPTS2(medi, pkt)
? ? ? ??if?!ok {
? ? ? ? ? ??//log.Printf("Waiting for timestamp")
? ? ? ? ? ? pts =?int64(pkt.Timestamp)
? ? ? ? ? ??//return
? ? ? ? }
? ? ? ??// if bakPts == 0 {
? ? ? ??// ? ? bakPts = pts
? ? ? ??// }

? ? ? ??// Extract access unit from RTP packets
? ? ? ? au, err := rtpDec.Decode(pkt)
? ? ? ??if?err !=?nil?{
? ? ? ? ? ??if?err != rtph264.ErrNonStartingPacketAndNoPrevious && err != rtph264.ErrMorePacketsNeeded {
? ? ? ? ? ? ? ? log.Printf("ERR: %v", err)
? ? ? ? ? ? }
? ? ? ? ? ??return
? ? ? ? }
? ? ? ??
? ? ? ??// sub = (int)(pts - bakPts)/100000
? ? ? ??// Encode the access unit into MPEG-TS
? ? ? ??if?mpegtsMuxer !=?nil?{
? ? ? ? ? ? err = mpegtsMuxer.writeH264(au, pts)
? ? ? ? ? ??if?err !=?nil?{
? ? ? ? ? ? ? ? log.Printf("ERR: %v", err)
? ? ? ? ? ? ? ??return
? ? ? ? ? ? }
? ? ? ? ? ??// log.Printf("Saved TS packet, pts: %d,sub:%d",pts,sub)
? ? ? ? }

? ? ? ??// Check if it's time to create a new file or exit
? ? ? ??// if sub >= *durationPtr {
? ? ? ??if?time.Since(recordingStartTime) >= duration {
? ? ? ? ? ? mpegtsMuxer.close()
? ? ? ? ? ??if?*modePtr ==?"once"?{
? ? ? ? ? ? ? ? log.Println("Recording duration reached, exiting...")
? ? ? ? ? ? ? ? c.Close()?// Close the RTSP client connection
? ? ? ? ? ? ? ? os.Exit(0)?// Exit the program immediately
? ? ? ? ? ? }?else?{
? ? ? ? ? ? fileCounter = (fileCounter +?1) % *maxFilesPtr
? ? ? ? ? ? newFileName := fmt.Sprintf("%s%03d%s", filePrefix, fileCounter, fileSuffix)
? ? ? ? ? ? mpegtsMuxer = newMpegtsMuxer(newFileName, forma.SPS, forma.PPS)
? ? ? ? ? ? err = mpegtsMuxer.initialize()
? ? ? ? ? ??if?err !=?nil?{
? ? ? ? ? ? ? ? log.Fatalf("ERR: %v", err)
? ? ? ? ? ? ? ? c.Close()?// Close the RTSP client connection
? ? ? ? ? ? ? ? os.Exit(-1)?// Exit the program immediately
? ? ? ? ? ? }
? ? ? ? ? ? log.Printf("New file created: %s", newFileName)
? ? ? ? ? ? recordingStartTime = time.Now()
? ? ? ? ? ??//bakPts = pts
? ? ? ? }
? ? ? ? }

? ? })

? ??// Start playing
? ? _, err = c.Play(nil)
? ??if?err !=?nil?{
? ? ? ? log.Fatalf("Failed to play RTSP stream: %v", err)
? ? }

? ??// Wait for interrupt signal or recording duration
? ? sigChan :=?make(chan?os.Signal,?1)
? ? signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
? ??gofunc()?{
? ? ? ??for?{
? ? ? ? ? ??select?{
? ? ? ? ? ??case?<-ticker.C:
? ? ? ? ? ? ? ??if?*modePtr ==?"once"?{
? ? ? ? ? ? ? ? ? ? log.Println("Recording duration reached, exiting...")
? ? ? ? ? ? ? ? ? ? c.Close()
? ? ? ? ? ? ? ? ? ? os.Exit(0)
? ? ? ? ? ? ? ? }
? ? ? ? ? ??case?<-sigChan:
? ? ? ? ? ? ? ? log.Println("Interrupt signal received, exiting...")
? ? ? ? ? ? ? ? c.Close()
? ? ? ? ? ? ? ? os.Exit(0)
? ? ? ? ? ? }
? ? ? ? }
? ? }()

? ??// Block main goroutine forever
? ??select?{}
}

代碼編譯

export?GOOS=linux
export?GOARCH=arm
export?GOARM=5
#export CGO_ENABLED=1

go build -ldflags?'-s -w'

錄像測(cè)試

?vrec
? -c int
? ? ? ? 文件數(shù)
? -m string
? ? ? ? 錄像模式,單次模式:"once",循環(huán)模式:"loop", 注意要加雙引號(hào) (default?"loop")
? -r string
? ? ? ? RTSP URL
? -s int
? ? ? ? 起始文件編號(hào)
? -t int
? ? ? ? 單個(gè)文件錄像時(shí)長(zhǎng) (default 60)
2025/05/10 09:30:59 Missing required?command?line arguments
#錄像命令參數(shù):
vrec -c 1200 -m?"loop"?-s 0 -t 60 ?-r rtsp://192.168.2.168:41667/live

錄像文件播放

錄像文件查看,這是錄了一晚上的,文件比較多:

測(cè)試

通過電腦查看

在海納思的內(nèi)置web頁(yè)面查看錄像文件,首頁(yè)還是挺好看的:

首頁(yè)
文件管理器錄像文件
錄像文件可以直接點(diǎn)擊播放:

通過手機(jī)查看

手機(jī)
文件管理錄像列表
點(diǎn)擊播放播放

海思

海思

海思面向智能終端、顯示面板、家電、汽車電子等行業(yè)提供感知、聯(lián)接、計(jì)算、顯示等端到端的板級(jí)芯片和模組解決方案,覆蓋PLC、8K、NB-IoT、SoC和XR等技術(shù)領(lǐng)域,是全球領(lǐng)先的Fabless半導(dǎo)體芯片公司。

海思面向智能終端、顯示面板、家電、汽車電子等行業(yè)提供感知、聯(lián)接、計(jì)算、顯示等端到端的板級(jí)芯片和模組解決方案,覆蓋PLC、8K、NB-IoT、SoC和XR等技術(shù)領(lǐng)域,是全球領(lǐng)先的Fabless半導(dǎo)體芯片公司。收起

查看更多

相關(guān)推薦