Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segment and pause #531

Merged
merged 1 commit into from
Jul 11, 2023
Merged

Segment and pause #531

merged 1 commit into from
Jul 11, 2023

Conversation

bebon901
Copy link
Collaborator

@bebon901 bebon901 commented Jul 5, 2023

For review - DO NOT MERGE YET
@davidplowman @naushir @njhollinghurst

Have added the code to segment, pause, pause & split. Also updated codestyle

@bebon901 bebon901 closed this Jul 5, 2023
@bebon901 bebon901 reopened this Jul 5, 2023
@@ -13,6 +13,8 @@
#include "net_output.hpp"
#include "output.hpp"

bool enable_ = true;
Copy link
Collaborator

@njhollinghurst njhollinghurst Jul 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this relate to the member variable of the same name?
I'm not even sure which one will be in scope, and I expect it will confuse other readers as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Underscores on the end of a something normally imply a data member in a class)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. All these funny global variables need to go, unless there is some special justification for them.

@@ -321,21 +332,70 @@ LibAvEncoder::~LibAvEncoder()
LOG(2, "libav: codec closed");
}

int64_t previous_time = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a class member? (Is it different from previous_timestamp?)

av_packet_free(&pkt);
deinitOutput();
}


int64_t previous_timestamp = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another mysterious static variable

@@ -33,6 +33,8 @@ class Encoder
// describing a DMABUF, and by a mmapped userland pointer.
virtual void EncodeBuffer(int fd, size_t size, void *mem, StreamInfo const &info, int64_t timestamp_us) = 0;

virtual void Signal() {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a comment could be added above to explain what this method is for.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to be too nit-picky, but all the other comments appear above their respective function declarations.

I wonder if there's some way the wording could be a little more general/abstract, so that other Encoder sub-class writers will know what to do (or why they might or might not need to implement it)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, probably just move the comments before the declaration.


std::string filename = options_->output;
char subfilename[256];
snprintf(subfilename, sizeof(subfilename), options_->output.c_str(), segment_num);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this one of those evil occasions when you can wind up without the terminating null if the given string was too long?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

snprintf is safe wrt the NUL termination.

There is still a risk if output contained other unexpected conversion sequences (e.g. "%s%s%s"), which could cause it to read undefined values and crash. But I don't think this application is critical enough to bother checking this!

char subfilename[256];
snprintf(subfilename, sizeof(subfilename), options_->output.c_str(), segment_num);
filename = subfilename;
printf("Outputting with filename: %s\n", filename.c_str());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wanted to check whether printf is the right thing here, or whether it should be some kind of logging statement?

@@ -630,6 +704,8 @@ void LibAvEncoder::audioThread()
av_frame_unref(in_frame);
av_packet_unref(in_pkt);

std::this_thread::yield(); // Added to try to reduce thread load on proccessor

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a bit intrigued why this is necessary... do we know anything more?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@naushir requested this to be added
Seems to put the thread to sleep whilst we wait for a frame as oppose to constantly looping

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this for now - I'm not 100% sure if it's needed and we probably want to do a bit more experimenting.

@davidplowman
Copy link
Collaborator

Hi Ben, mostly this looks pretty good to me with the suggested tidy ups. @njhollinghurst Maybe ask Nick to show you how to squash some of those commits together that are patching up style problems, just to leave us with a trail that's easier to follow?

@naushir
Copy link
Collaborator

naushir commented Jul 6, 2023

Just a quick check @bebon901 - did you manage to do a test run with audio encode enabled?

@naushir
Copy link
Collaborator

naushir commented Jul 6, 2023

PS sorry I have not had a chance to review yet, I will do when we get back to the office tomorrow!

@bebon901
Copy link
Collaborator Author

bebon901 commented Jul 6, 2023

Just a quick check @bebon901 - did you manage to do a test run with audio encode enabled?
@naushir, Did manage, was all working. Will check once more before I rePush

@bebon901
Copy link
Collaborator Author

bebon901 commented Jul 6, 2023

I've changed the things you wanted, please can you review again?

Added segmentation, pause and split to LibAV encoding when running
libcamera-vid.
Tidied up and added comments on various files used.

Signed-off-by: Ben Benson <[email protected]>
@@ -17,7 +17,8 @@
#include <iostream>

#include "libav_encoder.hpp"

int64_t segment_start_ts = 0;
int segment_num = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these really want to be data members in the class?

@@ -323,12 +324,28 @@ LibAvEncoder::~LibAvEncoder()

void LibAvEncoder::EncodeBuffer(int fd, size_t size, void *mem, StreamInfo const &info, int64_t timestamp_us)
{

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can probably delete the extra blank line...

AVFrame *frame = av_frame_alloc();
if (!frame)
throw std::runtime_error("libav: could not allocate AVFrame");

if (!video_start_ts_)
if (!video_start_ts_) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In libcamera-apps we tend to add a line break before a curly brace.

video_start_ts_ = timestamp_us;
segment_start_ts = video_start_ts_;
}
printf("Frame added \n");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably turn these into LOG statements?


if (options_->segment){
char subfilename[256];
snprintf(subfilename, sizeof(subfilename), options_->output.c_str(), segment_num);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a bad feeling this might crash if options_->output.c_str() is too long?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah no, re-reading the snprintf documentation this does indeed seem to be oK!!

printf("Frame added \n");
if ((options_->segment && (timestamp_us - segment_start_ts)/ 1000 > options_->segment) && output_ready_)
{
printf("Segment Added \n");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above applies to all printfs (I shan't repeat it elsewhere!).

@@ -411,13 +434,15 @@ void LibAvEncoder::initOutput()

void LibAvEncoder::deinitOutput()
{

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the blank line? (It's not that the blank line matters, it's just that diffs are easier to look at the smaller they are!!)

@@ -444,6 +469,7 @@ void LibAvEncoder::encode(AVPacket *pkt, unsigned int stream_id)
output_ready_ = true;
}


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blank line?

@@ -487,9 +513,9 @@ void LibAvEncoder::videoThread()
using namespace std::chrono_literals;
// Must check the abort first, to allow items in the output
// queue to have a callback.
if (abort_video_ && frame_queue_.empty())
if (abort_video_ && frame_queue_.empty()){
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curly brace?

@@ -658,11 +688,11 @@ void LibAvEncoder::audioThread()

out_frame->pts = ts + (options_->av_sync > 0 ? options_->av_sync : 0);
audio_samples_ += codec_ctx_[AudioOut]->frame_size;

std::scoped_lock<std::mutex> lock(reset_mutex_); {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too, let's just check if the lock should be inside the curly brace.

{ "h264_v4l2m2m", encoderOptionsH264M2M },
{ "libx264", encoderOptionsLibx264 },
};

} // namespace

void LibAvEncoder::Signal()
{
feed_encoder_frames = !feed_encoder_frames;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if feed_encoder_frames should strictly be feed_encoder_frames_, just to maintain the convention about data member naming.

video_start_ts_ = timestamp_us;
segment_start_ts = video_start_ts_;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

segment_start_ts_?

int64_t previous_video_timestamp;
int64_t previous_audio_timestamp;
bool feed_encoder_frames;
bool previous_feed_value;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All fine, just want the trailing underscore at the end of each name.

namespace {

namespace
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra whitespace? (Sorry...)

@@ -54,14 +58,18 @@ void encoderOptionsLibx264(VideoOptions const *options, AVCodecContext *codec)
av_opt_set(codec->priv_data, "mixed_ref", "0", 0);
}

const std::map<std::string, std::function<void(VideoOptions const *, AVCodecContext *)>> optionsMap =
{
const std::map<std::string, std::function<void(VideoOptions const *, AVCodecContext *)>> optionsMap = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we revert this format change?

void LibAvEncoder::EncodeBuffer(int fd, size_t size, void *mem, StreamInfo const &info, int64_t timestamp_us)
{
AVFrame *frame = av_frame_alloc();

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra line can be removed.

int64_t previous_video_timestamp;
int64_t previous_audio_timestamp;
bool feed_encoder_frames;
bool previous_feed_value;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these want to have a _ suffix to denote they are class members.

std::condition_variable video_cv_;
std::thread video_thread_;
std::thread audio_thread_;

// The ordering in the enum below must not change!
enum Context { Video = 0, AudioOut = 1, AudioIn = 2 };
enum Context
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we revert this format change?

int64_t et = ts - previous_audio_timestamp; // Elapsed Time

if (feed_encoder_frames && options_->keypress)
{ // If feeding audio and keypress option is on, then count the frame timer for the audio frames
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on the next line.

}
else
{
out_frame->pts = ts - virtual_audio_ts + (options_->av_sync < 0 ? -options_->av_sync : 0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent, and remove brackets?

{ // If recording, or not.
ret = avcodec_send_frame(codec_ctx_[AudioOut], out_frame);
if (ret < 0)
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove brackets?

std::scoped_lock<std::mutex> lock(reset_mutex_);
{
if (feed_encoder_frames)
{ // If recording, or not.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on the next line.

}
previous_audio_timestamp = ts; // Previous TimeStamp
audio_samples_ += codec_ctx_[AudioOut]->frame_size;
std::scoped_lock<std::mutex> lock(reset_mutex_);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scoped lock inside the bracket below?

av_packet_free(&pkt);
deinitOutput();
}


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blank line?

}
previous_audio_timestamp = ts; // Previous TimeStamp
audio_samples_ += codec_ctx_[AudioOut]->frame_size;
std::scoped_lock<std::mutex> lock(reset_mutex_);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just check if this lock want to be inside or outside the braces?

@bebon901
Copy link
Collaborator Author

Have now resolved all previous issues.

@davidplowman
Copy link
Collaborator

Hi Ben, this is looking pretty good to me now. The only final thing I would suggest would be to squash both commits together. The 2nd is just style/cosmetic fixes, so by making it look as though you got this all perfect first time round then we actually end up with fewer changes for future generations to scratch their heads over...!

@bebon901 bebon901 force-pushed the SegmentandPause branch 3 times, most recently from 808581a to 4244a49 Compare July 11, 2023 12:30
int64_t previous_video_timestamp;
int64_t previous_audio_timestamp;
bool feed_encoder_frames;
bool previous_feed_value;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's looking to me a bit like we have versions with underscores and without. Have I read that right? If so, the underscoreless versions want to go!

previous_audio_timestamp_ = ts; // Previous TimeStamp
audio_samples_ += codec_ctx_[AudioOut]->frame_size;
{
std::scoped_lock<std::mutex> lock(reset_mutex_);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering about the placement of the lock. In the video thread, the lock covers the call to encode() but here it doesn't. Is one of them incorrect maybe?

Copy link
Collaborator

@naushir naushir Jul 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it ought to not cover encode() in both cases. But we ought to test this thoroughly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have removed mutexes. Seems to record very long videos with lots of segments without issue. Have confirmed audio also works.

nextSegment(timestamp_us, segment_start_ts_);
// Split Function
//want to catch the 'rising edge' of the feed_frames variable. when it rises, we want to save the file and start with a new one
if (options_->split && (previous_feed_value_ != feed_encoder_frames_) && feed_encoder_frames_)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We wouldn't normally bother with the brackets round the "not equals" comparison, but I'm happy to ignore.

@@ -388,6 +423,15 @@ void LibAvEncoder::initOutput()
if (!(out_fmt_ctx_->flags & AVFMT_NOFILE))
{
std::string filename = options_->output;
char subfilename[256];
int n;
n = snprintf(subfilename, sizeof(subfilename), options_->output.c_str(), segment_num_);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be conditional on setting the segment option?

@davidplowman
Copy link
Collaborator

Looks good to me. Happy to merge if Naush is.

At the risk of one final nit-pick, in the commit message, "Signed-off-by" should be "Signed-off-by:". Sorry!!

@naushir
Copy link
Collaborator

naushir commented Jul 11, 2023

Looks good to me. Happy to merge if Naush is.

Just some minor clarifications/fixes for the encode() call and filename handling, otherwise it looks good!

if (ret < 0)
throw std::runtime_error("libav: error encoding frame: " + std::to_string(ret));
}
}
encode(out_pkt, AudioOut);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here should encode be called only when feed_encoder_frames_ is true?

@bebon901
Copy link
Collaborator Author

All above issues have been resolved. Have tested with audio, without audio, segmented, not segmented and paused/not paused.

@bebon901 bebon901 force-pushed the SegmentandPause branch 2 times, most recently from 2276978 to 801a6fa Compare July 11, 2023 14:29
@bebon901 bebon901 marked this pull request as ready for review July 11, 2023 14:31
@naushir naushir merged commit 6e4b8da into main Jul 11, 2023
9 checks passed
@naushir naushir deleted the SegmentandPause branch October 19, 2023 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants