From 4f6121b1c423cecf3b805d0c99dba04c272ddb26 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Tue, 25 Oct 2022 12:14:37 +0200 Subject: [PATCH] rpicamera: use exact frame timestamps --- internal/core/rpicamera_source.go | 8 ++------ internal/rpicamera/exe/encoder.c | 19 ++++++++++++++++--- internal/rpicamera/exe/encoder.h | 2 +- internal/rpicamera/exe/main.c | 11 ++++++----- internal/rpicamera/rpicamera.go | 14 +++++++++----- internal/rpicamera/rpicamera_disabled.go | 3 ++- 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/internal/core/rpicamera_source.go b/internal/core/rpicamera_source.go index acf8c7ba..6926089c 100644 --- a/internal/core/rpicamera_source.go +++ b/internal/core/rpicamera_source.go @@ -44,9 +44,8 @@ func (s *rpiCameraSource) run(ctx context.Context) error { enc := &rtph264.Encoder{PayloadType: 96} enc.Init() var stream *stream - var start time.Time - onData := func(nalus [][]byte) { + onData := func(dts time.Duration, nalus [][]byte) { if stream == nil { res := s.parent.sourceStaticImplSetReady(pathSourceStaticSetReadyReq{ tracks: tracks, @@ -58,15 +57,12 @@ func (s *rpiCameraSource) run(ctx context.Context) error { s.Log(logger.Info, "ready: %s", sourceTrackInfo(tracks)) stream = res.stream - start = time.Now() } - pts := time.Since(start) - stream.writeData(&data{ trackID: 0, ptsEqualsDTS: h264.IDRPresent(nalus), - pts: pts, + pts: dts, h264NALUs: nalus, }) } diff --git a/internal/rpicamera/exe/encoder.c b/internal/rpicamera/exe/encoder.c index 15eb72ca..cd96d251 100644 --- a/internal/rpicamera/exe/encoder.c +++ b/internal/rpicamera/exe/encoder.c @@ -17,7 +17,8 @@ #include "parameters.h" #include "encoder.h" -#define DEVICE "/dev/video11" +#define DEVICE "/dev/video11" +#define POLL_TIMEOUT_MS 200 char errbuf[256]; @@ -38,6 +39,8 @@ typedef struct { int cur_buffer; encoder_output_cb output_cb; pthread_t output_thread; + bool ts_initialized; + uint64_t start_ts; } encoder_priv_t; static void *output_thread(void *userdata) { @@ -45,7 +48,7 @@ static void *output_thread(void *userdata) { while (true) { struct pollfd p = { encp->fd, POLLIN, 0 }; - int res = poll(&p, 1, 200); + int res = poll(&p, 1, POLL_TIMEOUT_MS); if (res == -1) { fprintf(stderr, "output_thread(): poll() failed\n"); exit(1); @@ -72,9 +75,18 @@ static void *output_thread(void *userdata) { buf.m.planes = planes; res = ioctl(encp->fd, VIDIOC_DQBUF, &buf); if (res == 0) { + uint64_t ts = ((uint64_t)buf.timestamp.tv_sec * (uint64_t)1000000) + (uint64_t)buf.timestamp.tv_usec; + + if (!encp->ts_initialized) { + encp->ts_initialized = true; + encp->start_ts = ts; + } + + ts -= encp->start_ts; + const uint8_t *bufmem = (const uint8_t *)encp->capture_buffers[buf.index]; int bufsize = buf.m.planes[0].bytesused; - encp->output_cb(bufmem, bufsize); + encp->output_cb(ts, bufmem, bufsize); int index = buf.index; int length = buf.m.planes[0].length; @@ -284,6 +296,7 @@ bool encoder_create(parameters_t *params, int stride, int colorspace, encoder_ou encp->params = params; encp->cur_buffer = 0; encp->output_cb = output_cb; + encp->ts_initialized = false; pthread_create(&encp->output_thread, NULL, output_thread, encp); diff --git a/internal/rpicamera/exe/encoder.h b/internal/rpicamera/exe/encoder.h index d5a71010..a575ecd4 100644 --- a/internal/rpicamera/exe/encoder.h +++ b/internal/rpicamera/exe/encoder.h @@ -3,7 +3,7 @@ typedef void encoder_t; -typedef void (*encoder_output_cb)(const uint8_t *buf, uint64_t size); +typedef void (*encoder_output_cb)(uint64_t ts, const uint8_t *buf, uint64_t size); const char *encoder_get_error(); bool encoder_create(parameters_t *params, int stride, int colorspace, encoder_output_cb output_cb, encoder_t **enc); diff --git a/internal/rpicamera/exe/main.c b/internal/rpicamera/exe/main.c index 953f09ee..69519383 100644 --- a/internal/rpicamera/exe/main.c +++ b/internal/rpicamera/exe/main.c @@ -37,21 +37,22 @@ static void pipe_write_ready(int fd) { write(fd, buf, n); } -static void pipe_write_buf(int fd, const uint8_t *buf, int n) { +static void pipe_write_buf(int fd, uint64_t ts, const uint8_t *buf, int n) { char head[] = {'b'}; - n++; + n += 1 + sizeof(uint64_t); write(fd, &n, 4); write(fd, head, 1); - write(fd, buf, n-1); + write(fd, &ts, sizeof(uint64_t)); + write(fd, buf, n - 1 - sizeof(uint64_t)); } static void on_frame(int buffer_fd, uint64_t size, uint64_t timestamp) { encoder_encode(enc, buffer_fd, size, timestamp); } -static void on_encoder_output(const uint8_t *buf, uint64_t size) { +static void on_encoder_output(uint64_t ts, const uint8_t *buf, uint64_t size) { pthread_mutex_lock(&pipe_mutex); - pipe_write_buf(pipe_fd, buf, size); + pipe_write_buf(pipe_fd, ts, buf, size); pthread_mutex_unlock(&pipe_mutex); } diff --git a/internal/rpicamera/rpicamera.go b/internal/rpicamera/rpicamera.go index bd489b09..b765d530 100644 --- a/internal/rpicamera/rpicamera.go +++ b/internal/rpicamera/rpicamera.go @@ -7,6 +7,7 @@ import ( _ "embed" "fmt" "strconv" + "time" "github.com/aler9/gortsplib/pkg/h264" ) @@ -22,7 +23,7 @@ func bool2env(v bool) string { } type RPICamera struct { - onData func([][]byte) + onData func(time.Duration, [][]byte) exe *embeddedExe pipe *pipe @@ -33,7 +34,7 @@ type RPICamera struct { func New( params Params, - onData func([][]byte), + onData func(time.Duration, [][]byte), ) (*RPICamera, error) { pipe, err := newPipe() if err != nil { @@ -128,14 +129,17 @@ func New( if buf[0] != 'b' { return fmt.Errorf("unexpected output from pipe (%c)", buf[0]) } - buf = buf[1:] - nalus, err := h264.AnnexBUnmarshal(buf) + tmp := uint64(buf[8])<<56 | uint64(buf[7])<<48 | uint64(buf[6])<<40 | uint64(buf[5])<<32 | + uint64(buf[4])<<24 | uint64(buf[3])<<16 | uint64(buf[2])<<8 | uint64(buf[1]) + dts := time.Duration(tmp) * time.Microsecond + + nalus, err := h264.AnnexBUnmarshal(buf[9:]) if err != nil { return err } - onData(nalus) + onData(dts, nalus) } }() }() diff --git a/internal/rpicamera/rpicamera_disabled.go b/internal/rpicamera/rpicamera_disabled.go index 76616b9c..21dd2ebd 100644 --- a/internal/rpicamera/rpicamera_disabled.go +++ b/internal/rpicamera/rpicamera_disabled.go @@ -6,6 +6,7 @@ package rpicamera import ( "fmt" + "time" ) // RPICamera is a RPI Camera reader. @@ -14,7 +15,7 @@ type RPICamera struct{} // New allocates a RPICamera. func New( params Params, - onData func([][]byte), + onData func(time.Duration, [][]byte), ) (*RPICamera, error) { return nil, fmt.Errorf("server was compiled without support for the Raspberry Pi Camera") }