-
Notifications
You must be signed in to change notification settings - Fork 75
Refactor receiveFrame and sendPacket logic to dispatch directly to interface #954
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
Changes from 5 commits
718c268
30ee0e8
73fe68b
11d7adf
44edb93
8b4acd3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -429,7 +429,6 @@ void SingleStreamDecoder::addStream( | |
| TORCH_CHECK( | ||
| deviceInterface_ != nullptr, | ||
| "Failed to create device interface. This should never happen, please report."); | ||
| deviceInterface_->initialize(streamInfo.stream, formatContext_); | ||
|
|
||
| // TODO_CODE_QUALITY it's pretty meh to have a video-specific logic within | ||
| // addStream() which is supposed to be generic | ||
|
|
@@ -441,7 +440,8 @@ void SingleStreamDecoder::addStream( | |
|
|
||
| AVCodecContext* codecContext = avcodec_alloc_context3(avCodec); | ||
| TORCH_CHECK(codecContext != nullptr); | ||
| streamInfo.codecContext.reset(codecContext); | ||
| streamInfo.codecContext = SharedAVCodecContext( | ||
| codecContext, [](AVCodecContext* ctx) { avcodec_free_context(&ctx); }); | ||
|
|
||
| int retVal = avcodec_parameters_to_context( | ||
| streamInfo.codecContext.get(), streamInfo.stream->codecpar); | ||
|
|
@@ -453,14 +453,19 @@ void SingleStreamDecoder::addStream( | |
| // Note that we must make sure to register the harware device context | ||
| // with the codec context before calling avcodec_open2(). Otherwise, decoding | ||
| // will happen on the CPU and not the hardware device. | ||
| deviceInterface_->registerHardwareDeviceWithCodec(codecContext); | ||
| deviceInterface_->registerHardwareDeviceWithCodec( | ||
| streamInfo.codecContext.get()); | ||
| retVal = avcodec_open2(streamInfo.codecContext.get(), avCodec, nullptr); | ||
| TORCH_CHECK(retVal >= AVSUCCESS, getFFMPEGErrorStringFromErrorCode(retVal)); | ||
|
|
||
| codecContext->time_base = streamInfo.stream->time_base; | ||
| streamInfo.codecContext->time_base = streamInfo.stream->time_base; | ||
|
|
||
| // Initialize the device interface with the codec context | ||
| deviceInterface_->initialize( | ||
| streamInfo.stream, formatContext_, streamInfo.codecContext); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just pointing for myself and for other reviewers that the call to We should just make sure there was no other call to the interface method that would depend on the interface being initialized. I think this is OK, the only call was
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think we're good here. My rationale with the device was to initialize it as soon as we had everything we needed to do it. |
||
|
|
||
| containerMetadata_.allStreamMetadata[activeStreamIndex_].codecName = | ||
| std::string(avcodec_get_name(codecContext->codec_id)); | ||
| std::string(avcodec_get_name(streamInfo.codecContext->codec_id)); | ||
|
|
||
| // We will only need packets from the active stream, so we tell FFmpeg to | ||
| // discard packets from the other streams. Note that av_read_frame() may still | ||
|
|
@@ -1149,8 +1154,6 @@ void SingleStreamDecoder::maybeSeekToBeforeDesiredPts() { | |
| getFFMPEGErrorStringFromErrorCode(status)); | ||
|
|
||
| decodeStats_.numFlushes++; | ||
| avcodec_flush_buffers(streamInfo.codecContext.get()); | ||
|
|
||
| deviceInterface_->flush(); | ||
| } | ||
|
|
||
|
|
@@ -1169,24 +1172,16 @@ UniqueAVFrame SingleStreamDecoder::decodeAVFrame( | |
| cursorWasJustSet_ = false; | ||
| } | ||
|
|
||
| StreamInfo& streamInfo = streamInfos_[activeStreamIndex_]; | ||
| UniqueAVFrame avFrame(av_frame_alloc()); | ||
| AutoAVPacket autoAVPacket; | ||
| int status = AVSUCCESS; | ||
| bool reachedEOF = false; | ||
|
|
||
| // TODONVDEC P2: Instead of calling canDecodePacketDirectly() and rely on | ||
| // if/else blocks to dispatch to the interface or to FFmpeg, consider *always* | ||
| // dispatching to the interface. The default implementation of the interface's | ||
| // receiveFrame and sendPacket could just be calling avcodec_receive_frame and | ||
| // avcodec_send_packet. This would make the decoding loop even more generic. | ||
| // The default implementation uses avcodec_receive_frame and | ||
| // avcodec_send_packet, while specialized interfaces can override for | ||
| // hardware-specific optimizations. | ||
NicolasHug marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| while (true) { | ||
| if (deviceInterface_->canDecodePacketDirectly()) { | ||
| status = deviceInterface_->receiveFrame(avFrame); | ||
| } else { | ||
| status = | ||
| avcodec_receive_frame(streamInfo.codecContext.get(), avFrame.get()); | ||
| } | ||
| status = deviceInterface_->receiveFrame(avFrame); | ||
|
|
||
| if (status != AVSUCCESS && status != AVERROR(EAGAIN)) { | ||
| // Non-retriable error | ||
|
|
@@ -1222,13 +1217,7 @@ UniqueAVFrame SingleStreamDecoder::decodeAVFrame( | |
|
|
||
| if (status == AVERROR_EOF) { | ||
| // End of file reached. We must drain the decoder | ||
| if (deviceInterface_->canDecodePacketDirectly()) { | ||
| status = deviceInterface_->sendEOFPacket(); | ||
| } else { | ||
| status = avcodec_send_packet( | ||
| streamInfo.codecContext.get(), | ||
| /*avpkt=*/nullptr); | ||
| } | ||
| status = deviceInterface_->sendEOFPacket(); | ||
| TORCH_CHECK( | ||
| status >= AVSUCCESS, | ||
| "Could not flush decoder: ", | ||
|
|
@@ -1253,11 +1242,7 @@ UniqueAVFrame SingleStreamDecoder::decodeAVFrame( | |
|
|
||
| // We got a valid packet. Send it to the decoder, and we'll receive it in | ||
| // the next iteration. | ||
| if (deviceInterface_->canDecodePacketDirectly()) { | ||
| status = deviceInterface_->sendPacket(packet); | ||
| } else { | ||
| status = avcodec_send_packet(streamInfo.codecContext.get(), packet.get()); | ||
| } | ||
| status = deviceInterface_->sendPacket(packet); | ||
| TORCH_CHECK( | ||
| status >= AVSUCCESS, | ||
| "Could not push packet to decoder: ", | ||
|
|
||
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.
It'd be nice if we could have this just be:
and have the definition of
SharedAVCodecContextspecify its own destructor. This is what we do for the other smart pointers inFFmpegCommon.h.(It may not be possible or too clumsy, I gave it a quick try to better illustrate what I mean, and I wasn't able to make it work!)
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 think the deleter is not a part of type
std::shared_ptrso there would need to be a helper functionmakeSharedAVCodecContextinFFMPEGCommon.hand the usage would bestreamInfo.codecContext = makeSharedAVCodecContext(codecContext);Would this be preferred?
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.
@mollyxu, take a look at
UniqueAVCodecContext, which is defined right above your newSharedAVCodecContext. That makes the deleter part of theusingstatement, which enables us to just sayUniqueAVCodecContext(ptr)in normal code.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 looked at this again because I wanted to figure out why I couldn't make it work yesterday. It seems we can't use the same template parameter syntax as we do for
unique_ptr, becauseshared_ptronly accepts the deleter in the constructor. So we can't definewhich is what I was hoping for. That's just not supported by the
shared_ptrtype.The closest I got is to define a construction helper
which is then used as
(When I say I, I mean Claude).
It's very close to what you already have, the main difference is that we're using our
Deleterptemplate instead of a lambda. I'd still suggest to usemakeSharedAVCodecContext, so that all our smart pointer logic is centralized within the sameFFmpegCommon.hfile.(If we move
makeSharedAVCodecContextto the .cpp file we can remove theinline, but I don't think it's worth it).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.
@NicolasHug, thanks for digging into this! I am resisting the temptation to dive into the C++ docs to figure out why these two seemingly complementary classes have different interfaces! Not a joke. :)
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 was also wondering and got this:
But this isn't really a great reason. This explain why
unique_ptrcannot be passed the deleter to its constructor. But it doesn't explain whyshared_ptrdoesn't support have the same syntax asunique_ptr. 🤔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 for the suggestions and for taking the time to look into this!