-
Notifications
You must be signed in to change notification settings - Fork 11
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
encoder: Add VP9 support and refactor common encoder code #64
Conversation
f5113c5
to
b42c1c0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is looking very good overall. A few inline comments in the code, and it would really make my life easier if you could split the big refactoring CL into its logical parts.
But overall the end result looks really good, so I'm looking forward to merging this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you soo much for the review 😄
I will continue working on the remaining comments tomorrow. Commits before a8ce22b most likely won't change.
ac8ab58
to
9ca7e7e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hopefully all comments were answered 😅 Thanks again for the review!
I have started merging up to |
src/encoder/stateless/h264.rs
Outdated
@@ -191,7 +192,30 @@ where | |||
|
|||
pub struct H264; | |||
|
|||
impl StatelessCodec for H264 {} | |||
pub struct H264Specific<Backend> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically (and unless I am missing the purpose of this) you don't need a generic parameter to this struct. This allows you to remove _phantom
and keep this as an empty type.
After doing so, there is also no need to have a H264Specific
type since H264
can just implement StatelessCodecSpecific
and refer to itself as its own Specific
type.
I.e. this works just fine (patch is on top of the whole series):
diff --git a/src/encoder/stateless/h264.rs b/src/encoder/stateless/h264.rs
index bc790fb69..93d56ec8c 100644
--- a/src/encoder/stateless/h264.rs
+++ b/src/encoder/stateless/h264.rs
@@ -158,14 +158,7 @@ where
pub struct H264;
-pub struct H264Specific<Backend>
-where
- Backend: StatelessVideoEncoderBackend<H264>,
-{
- _phantom: std::marker::PhantomData<Backend>,
-}
-
-impl<Backend> StatelessCodecSpecific<H264, Backend> for H264Specific<Backend>
+impl<Backend> StatelessCodecSpecific<H264, Backend> for H264
where
Backend: StatelessVideoEncoderBackend<H264>,
{
@@ -179,7 +172,7 @@ where
}
impl StatelessCodec for H264 {
- type Specific<Backend> = H264Specific<Backend>
+ type Specific<Backend> = Self
where Backend : StatelessVideoEncoderBackend<Self>;
}
diff --git a/src/encoder/stateless/vp9.rs b/src/encoder/stateless/vp9.rs
index c17bd35a2..a261ffddb 100644
--- a/src/encoder/stateless/vp9.rs
+++ b/src/encoder/stateless/vp9.rs
@@ -83,14 +83,7 @@ pub struct BackendRequest<P, R> {
pub struct VP9;
-pub struct VP9Specific<Backend>
-where
- Backend: StatelessVideoEncoderBackend<VP9>,
-{
- _phantom: std::marker::PhantomData<Backend>,
-}
-
-impl<Backend> StatelessCodecSpecific<VP9, Backend> for VP9Specific<Backend>
+impl<Backend> StatelessCodecSpecific<VP9, Backend> for VP9
where
Backend: StatelessVideoEncoderBackend<VP9>,
{
@@ -104,7 +97,7 @@ where
}
impl StatelessCodec for VP9 {
- type Specific<Backend> = VP9Specific<Backend>
+ type Specific<Backend> = VP9
where Backend : StatelessVideoEncoderBackend<Self>;
}
Once you do that, StatelessCodec
is just used as a way to get an associated type to itself - so you can simplify things further:
diff --git a/src/encoder/stateless.rs b/src/encoder/stateless.rs
index b2c4dacaa..9d9adcd65 100644
--- a/src/encoder/stateless.rs
+++ b/src/encoder/stateless.rs
@@ -208,10 +208,10 @@ pub trait StatelessEncoderBackendImport<Handle, Picture> {
}
/// Trait helping contain all codec specific and backend specific types
-pub trait StatelessCodecSpecific<Codec, Backend>
+pub trait StatelessCodecSpecific<Backend>
where
- Codec: StatelessCodec,
- Backend: StatelessVideoEncoderBackend<Codec>,
+ Self: StatelessCodec,
+ Backend: StatelessVideoEncoderBackend<Self>,
{
/// Codec specific representation of frame reference wrapping a backend reference type
/// containing a codec specific frame metadata
@@ -229,12 +229,7 @@ where
type ReferencePromise: BackendPromise<Output = Self::Reference>;
}
-pub trait StatelessCodec: Sized {
- /// Codec and backend specific types container
- type Specific<Backend>: StatelessCodecSpecific<Self, Backend>
- where
- Backend: StatelessVideoEncoderBackend<Self>;
-}
+pub trait StatelessCodec: Sized {}
/// Stateless video encoder interface.
pub trait StatelessVideoEncoder<Handle> {
@@ -293,23 +288,20 @@ where
/// Helper aliases for codec and backend specific types
type Picture<C, B> = <B as StatelessVideoEncoderBackend<C>>::Picture;
-type CodecSpecific<C, B> = <C as StatelessCodec>::Specific<B>;
-
-type Reference<C, B> = <CodecSpecific<C, B> as StatelessCodecSpecific<C, B>>::Reference;
+type Reference<C, B> = <C as StatelessCodecSpecific<B>>::Reference;
-type Request<C, B> = <CodecSpecific<C, B> as StatelessCodecSpecific<C, B>>::Request;
+type Request<C, B> = <C as StatelessCodecSpecific<B>>::Request;
-type CodedPromise<C, B> = <CodecSpecific<C, B> as StatelessCodecSpecific<C, B>>::CodedPromise;
+type CodedPromise<C, B> = <C as StatelessCodecSpecific<B>>::CodedPromise;
-type ReferencePromise<C, B> =
- <CodecSpecific<C, B> as StatelessCodecSpecific<C, B>>::ReferencePromise;
+type ReferencePromise<C, B> = <C as StatelessCodecSpecific<B>>::ReferencePromise;
type BoxPredictor<C, B> = Box<dyn Predictor<Picture<C, B>, Reference<C, B>, Request<C, B>>>;
pub struct StatelessEncoder<Codec, Handle, Backend>
where
Backend: StatelessVideoEncoderBackend<Codec>,
- Codec: StatelessCodec,
+ Codec: StatelessCodec + StatelessCodecSpecific<Backend>,
{
/// Pending frame output promise queue
output_queue: OutputQueue<CodedPromise<Codec, Backend>>,
@@ -340,14 +332,14 @@ where
pub trait StatelessEncoderExecute<Codec, Handle, Backend>
where
Backend: StatelessVideoEncoderBackend<Codec>,
- Codec: StatelessCodec,
+ Codec: StatelessCodec + StatelessCodecSpecific<Backend>,
{
fn execute(&mut self, request: Request<Codec, Backend>) -> EncodeResult<()>;
}
impl<Codec, Handle, Backend> StatelessEncoder<Codec, Handle, Backend>
where
- Codec: StatelessCodec,
+ Codec: StatelessCodec + StatelessCodecSpecific<Backend>,
Backend: StatelessVideoEncoderBackend<Codec>,
Self: StatelessEncoderExecute<Codec, Handle, Backend>,
{
@@ -392,7 +384,7 @@ where
impl<Codec, Handle, Backend> StatelessVideoEncoder<Handle>
for StatelessEncoder<Codec, Handle, Backend>
where
- Codec: StatelessCodec,
+ Codec: StatelessCodec + StatelessCodecSpecific<Backend>,
Backend: StatelessVideoEncoderBackend<Codec>,
Backend: StatelessEncoderBackendImport<Handle, Backend::Picture>,
Self: StatelessEncoderExecute<Codec, Handle, Backend>,
diff --git a/src/encoder/stateless/h264.rs b/src/encoder/stateless/h264.rs
index 93d56ec8c..3c03a6978 100644
--- a/src/encoder/stateless/h264.rs
+++ b/src/encoder/stateless/h264.rs
@@ -158,9 +158,9 @@ where
pub struct H264;
-impl<Backend> StatelessCodecSpecific<H264, Backend> for H264
+impl<Backend> StatelessCodecSpecific<Backend> for H264
where
- Backend: StatelessVideoEncoderBackend<H264>,
+ Backend: StatelessVideoEncoderBackend<Self>,
{
type Reference = DpbEntry<Backend::Reconstructed>;
@@ -171,10 +171,7 @@ where
type ReferencePromise = ReferencePromise<Backend::ReconPromise>;
}
-impl StatelessCodec for H264 {
- type Specific<Backend> = Self
- where Backend : StatelessVideoEncoderBackend<Self>;
-}
+impl StatelessCodec for H264 {}
/// Trait for stateless encoder backend for H.264
pub trait StatelessH264EncoderBackend: StatelessVideoEncoderBackend<H264> {
diff --git a/src/encoder/stateless/vp9.rs b/src/encoder/stateless/vp9.rs
index a261ffddb..4b7ed4905 100644
--- a/src/encoder/stateless/vp9.rs
+++ b/src/encoder/stateless/vp9.rs
@@ -83,9 +83,9 @@ pub struct BackendRequest<P, R> {
pub struct VP9;
-impl<Backend> StatelessCodecSpecific<VP9, Backend> for VP9
+impl<Backend> StatelessCodecSpecific<Backend> for VP9
where
- Backend: StatelessVideoEncoderBackend<VP9>,
+ Backend: StatelessVideoEncoderBackend<Self>,
{
type Reference = Backend::Reconstructed;
@@ -96,10 +96,7 @@ where
type ReferencePromise = Backend::ReconPromise;
}
-impl StatelessCodec for VP9 {
- type Specific<Backend> = VP9
- where Backend : StatelessVideoEncoderBackend<Self>;
-}
+impl StatelessCodec for VP9 {}
pub trait StatelessVP9EncoderBackend<H>: StatelessVideoEncoderBackend<VP9> {
fn encode_frame(
I don't know how much this makes sense, so please take it as a suggestion. With that StatelessCodec
is not really useful anymore, hinting that maybe we can simplify some more. I'll let you see what should be done as I am not completely sure of your intent for the future here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, you are right here 😄
My intent for the potential future use would be to take advantage of already existing decoder's (or similar) structures for keeping the decoder state definitely consistent instead relying on predictor for this. Second benefit of that would be that the predictor would be a single function trait which would accept a queue of pending frames and decoder state in order to make the decision of the next actions. But first of all currently I don't consider it as a deal breaking benefit now. And I think it could bring downsides of overcomplication, potentially engaging parser for no reason and breaking possible parallelism for GoP structure (WIP).
Back to the review. You are right regarding simplification of those traits. I even went ahead and merged StatelessCodec
and StatelessCodecSpecific
into one. And despite those simplifications I still believe the above idea continues to be possible to implement.
Also now that I think about it, shouldn't we also merge crate::decoder::stateless::h264::H264
and crate::encoder::stateless::h264::H264
(and the same for VP9)?
This is necessary for the next commits
06a50d0
to
8ea0e86
Compare
This change adds VP9 encoding support and vaapi backend for it. The StatelessEncoder implementation is a copy from H264 implementation. Both will be merge in the following change.
This commit adds VP9 encoding option to ccenc
/// Accepts [`Request`] and is responsible for adding resutling [`BackendPromise`] to | ||
/// [`StatelessEncoder`] internal queues and decrementing the internal predictor frame counter if | ||
/// the backend moved the frame outside predictor ownership. | ||
pub trait StatelessEncoderExecute<Codec, Handle, Backend> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this has the same constraints has StatelessEncoder
, and StatelessEncoder
depends on this trait, can't we just make execute
a method of StatelessEncoder
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But in such case I'd have to add constraints for Handle
and Backend
as well
|
||
Ok(()) | ||
} | ||
fn new_h264(backend: Backend, config: EncoderConfig, mode: BlockingMode) -> EncodeResult<Self> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we could replace these codec-specific methods with a single one if we add a few more associated types (EncoderConfig
and Predictor
) to StatelessCodec
, but let's do this as a follow-up.
..Default::default() | ||
}; | ||
|
||
let ref_frame = self.references.pop_front().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a hard guarantee that the unwrap
won't crash us - it would be nice to remove it at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I should add a // SAFETY:
comment. It is in the next_request()
. If references
is empty, this function would not be called.
All merged! (not from the GUI since I had to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!!
All merged! (not from the GUI since I had to cargo format a few commits).
I am surprised to see that, I remember running the cargo fmt
before upload. Maybe we have a discrepancy in rustfmt.toml
. Could you please add yours to the repo?
..Default::default() | ||
}; | ||
|
||
let ref_frame = self.references.pop_front().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I should add a // SAFETY:
comment. It is in the next_request()
. If references
is empty, this function would not be called.
/// Accepts [`Request`] and is responsible for adding resutling [`BackendPromise`] to | ||
/// [`StatelessEncoder`] internal queues and decrementing the internal predictor frame counter if | ||
/// the backend moved the frame outside predictor ownership. | ||
pub trait StatelessEncoderExecute<Codec, Handle, Backend> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But in such case I'd have to add constraints for Handle
and Backend
as well
This PR adds VP9 encoding support and refactors the common encoder code to remove code duplication between codecs.