HiveBrain v1.2.0
Get Started
← Back to all entries
patterngoMinor

Webcam frame splitter

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
webcamframesplitter

Problem

I wrote a camera streaming app for pizero.

Since the pizero is too weak to do any video encoding I am using it to just capture usb webcam frames (mjpeg format) and forward them over udp to a powerful pc that does the video encoding.

When the light conditions are low there is a lot of noise introduced to the frames and they grow in size, overflowing the udp write size limit.

To tackle the issue I wrote a func that splits the frame in parts and forwards the pieces to the encoder.

What I am looking for in this review is the following:

Performance - App runs on pizero where resources are limited. I am using buffered channels and I am not sure if I implemented them correctly.

Best practices

Hidden pitfalls - If any, point out if I shot myself in the foot.

Code contains the whole camera implementation plus the server side.

Please review both.

My programming level is beginner. This is my first project.

Camera:

```
package main

import (
"bytes"
"fmt"
"log"
"math"
"net"
"os/exec"
"sync"
"time"

"labix.org/v2/mgo/bson"
)

const (
FFMPEG_ENCODER = "192.168.178.200:8000"
CAM_ADDR = ":5000"
PACKET = 60000
)

type msg struct {
Fragment bool
FragmentID int
LastFragment bool
Data []byte
}

type reporting struct {
Fps int
Size int
Fragments float64
Encoding time.Duration
Writing time.Duration
}

func main() {
conn, err := udpDial()
if err != nil {
log.Fatal(err)
}

var stderr bytes.Buffer

//bash script with commands for interfacing with the camera.
//script outputs to stdout.
cmd := exec.Command("./v4l2")
pipe, _ := cmd.StdoutPipe()
defer pipe.Close()
cmd.Stderr = &stderr

if err := cmd.Start(); err != nil {
fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
}

//pizero - has issues serializing.
buffer := make(chan [][]byte, 500)
var wg sync.WaitGroup
var sta

Solution

Readability

To ease the readability (and testability) of your code, I would recommend you to split it in multiple functions communicating via multiple channels.

For instance, for the client:

func getOutput(command string) (io.ReadCloser, error)        // to launch the v4l2
func readFrames(in io.ReadCloser, out <-chan Frame)
func splitFrames(in chan<- Frame, out <-chan Fragment)
func sendFragments(in <-chan Fragment)


Your main would then consist of creating the different channels and launching those functions as goroutines (except for getOutput).

With this approach, you will need a fixed amount of goroutines (instead of starting a new one for each frame: go processFrame(frame, buffer, &statistic),
which might qualify as "I shot myself in the foot").

Statistics

Regarding the statistics, if see multiple possibilities:

  • pass the object as a variable (current usage)



  • or make it a global variable



  • or make it a channel of Report (the signification of Value depends on the Operation):



-

type Report struct {
    Operation string
    Value     int
    Duration  time.Duration
}


Performance

If you need to make your code faster, you will need to identify the bottlenecks. For this, I recommend you to profile your code : https://blog.golang.org/profiling-go-programs

  • Add the code var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")...



  • transfer the *.profile file to your computer



  • analyze the function calls

Code Snippets

func getOutput(command string) (io.ReadCloser, error)        // to launch the v4l2
func readFrames(in io.ReadCloser, out <-chan Frame)
func splitFrames(in chan<- Frame, out <-chan Fragment)
func sendFragments(in <-chan Fragment)
type Report struct {
    Operation string
    Value     int
    Duration  time.Duration
}

Context

StackExchange Code Review Q#159710, answer score: 3

Revisions (0)

No revisions yet.