5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

RTPで受け取った動画をgolangで画像処理したい

Last updated at Posted at 2021-03-25

の続き。

概要

動画を受け取り、動画から受け取ったフレームをある間隔で画像処理するようなアプリケーションをgolangで書きたい。

画像処理ライブラリとしてopencvを利用する必要があり、ラッパーとしてgocvを利用した。
試行錯誤した結果 opencv.OpenVideoCaptureという関数のバックエンドとしてgstreamerが使用できるという記事があり、試してみた。

構造

実際にはRTPの前にはWebRTCがあり、今回はSkyway WebRTC Gatewayを用いてWebRTC->RTPに変換している。

image.png

WebRTC周りについては別の機会に。
RTPを受け取る方法にはいろいろあると思うが、今回はopencvのgstreamer backendを使用した。この機能はgocvでは最新(0.26)でしか動作しないため、バージョンは要確認。

module version
golang 1.15.x
opencv 4.5.1
gocv 0.26.0
gstreamer 1.0.0

実装

videocapture.go

package middleware

import (
	"context"
	"fmt"
	"time"

	"github.com/pkg/errors"
	"go.uber.org/zap"
	"gocv.io/x/gocv"
)

// Handler function of VideoCaptureServer
type VideoCaptureHandler func(ctx context.Context, img *gocv.Mat) error

// VideoCaptureServer is server to handle gocv.OpenVideoCapture image
type VideoCaptureServer struct {
	handler  VideoCaptureHandler
	pipeline string
	duration time.Duration
}

func NewVideoCaptureServer(handler VideoCaptureHandler, pipeline string, duration time.Duration) *VideoCaptureServer {
	return &VideoCaptureServer{
		handler:  handler,
		pipeline: pipeline,
		duration: duration,
	}
}

// Start start server
func (s *VideoCaptureServer) Start(ctx context.Context) error {
	cap, err := gocv.OpenVideoCapture(s.pipeline)
	if err != nil {
		return errors.Wrap(err, "OpenVideoCapture failed")
	}
	defer cap.Close()

	ticker := time.NewTicker(s.duration)
	defer ticker.Stop()

	img := gocv.NewMat()

	for {
		select {
		case <-ticker.C:
			if err := s.handler(ctx, &img); err != nil {
				logger.Error("exec handler failed", zap.Error(err))
			}

		case <-ctx.Done():
			return nil
		default:
			if ok := cap.Read(&img); !ok {
				logger.Error("cap.Read failed")
			}
		}

	}
}

処理の内容はVideoCaptureHandlerとして関数インターフェースのみ定義し、動画を受け取って処理に引き渡す周りのコードをVideoCaptureServerとして定義。

videocapture_test.go

package middleware

import (
	"context"
	"fmt"
	"testing"
	"time"

	"gocv.io/x/gocv"
)

func TestVideoCaptureServer(t *testing.T) {
	ctx := context.Background()
	window := gocv.NewWindow("test")
	handler := func(ctx context.Context, img *gocv.Mat) error {
		window.IMShow(*img) // Xサーバーにテスト画像を表示
		window.WaitKey(1)
		return nil
	}
	pipeline := `udpsrc port=8888 caps="application/x-rtp,media=video,clock-rate=90000,encoding-name=H264" ! rtph264depay ! avdec_h264 ! videoconvert ! appsink`

	//pipelineを使ってOpenVideCaptureを実行し、1秒おきにhandler関数を実行
	sv := NewVideoCaptureServer(handler, pipeline, time.Second) 
	sv.Start(ctx)
}

こちらがテスト。

  • Xサーバーにテスト用画像を表示するので、動作させるためには事前にX Serverを起動しておく必要がある。
  • gst pipelineでudp port=8888で動画を受け取るように書いているが、mp4ファイルを使ったテストをしたければpipeline文字列を入れ替えるだけで良い。

評価

環境構築に難しい面はあるものの、gocv.OpenVideoCapture関数さえ使いこなせれば簡単にRTPのようなプロトコルを受け取る事が可能で、そこからの処理はほかのサーバーとそう変わらず、難しくないと感じた。

何らかの動画をgolangで受け取るような要件がある場合は、候補になると思う。

なお、pipelineの動作については、事前にgst-launchを使って確認しておくこと。appsinkを使った時のデバッグメッセージは分かりにくいと感じました・・・

https://qiita.com/kishibashi3/items/983ae9df29b6d9f8c9dd
続き

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?