Skip to content

Commit 340803e

Browse files
committed
Initial rudimentary support for hardware acceleration (encode and decode)
Only Linux vaapi for now
1 parent 6b5e2d4 commit 340803e

File tree

4 files changed

+301
-8
lines changed

4 files changed

+301
-8
lines changed

include/FFmpegReader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ namespace openshot
9797
AVFormatContext *pFormatCtx;
9898
int i, videoStream, audioStream;
9999
AVCodecContext *pCodecCtx, *aCodecCtx;
100+
#if (LIBAVFORMAT_VERSION_MAJOR >= 57)
101+
AVBufferRef *hw_device_ctx = NULL; //PM
102+
#endif
100103
AVStream *pStream, *aStream;
101104
AVPacket *packet;
102105
AVFrame *pFrame;

include/FFmpegUtilities.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
extern "C" {
4343
#include <libavcodec/avcodec.h>
4444
#include <libavformat/avformat.h>
45+
#if (LIBAVFORMAT_VERSION_MAJOR >= 57)
46+
#include <libavutil/hwcontext.h> //PM
47+
#endif
4548
#include <libswscale/swscale.h>
4649
// Change this to the first version swrescale works
4750
#if (LIBAVFORMAT_VERSION_MAJOR >= 57)

src/FFmpegReader.cpp

Lines changed: 128 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232

3333
using namespace openshot;
3434

35+
int hw_de_on = 1; // Is set in UI
36+
int hw_de_supported = 0; // Is set by FFmpegReader
37+
3538
FFmpegReader::FFmpegReader(string path)
3639
: last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0),
3740
audio_pts_offset(99999), video_pts_offset(99999), path(path), is_video_seek(true), check_interlace(false),
@@ -103,6 +106,45 @@ bool AudioLocation::is_near(AudioLocation location, int samples_per_frame, int64
103106
return false;
104107
}
105108

109+
#if IS_FFMPEG_3_2
110+
#if defined(__linux__)
111+
#pragma message "You are compiling with experimental hardware decode"
112+
113+
static enum AVPixelFormat get_vaapi_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts)
114+
{
115+
const enum AVPixelFormat *p;
116+
117+
for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
118+
if (*p == AV_PIX_FMT_VAAPI)
119+
return *p;
120+
}
121+
ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (Unable to decode this file using VA-API.)", "", -1, "", -1, "", -1, "", -1, "", -1, "", -1);
122+
hw_de_supported = 0;
123+
return AV_PIX_FMT_NONE;
124+
}
125+
126+
int is_hardware_decode_supported(int codecid)
127+
{
128+
int ret;
129+
switch (codecid) {
130+
case AV_CODEC_ID_H264:
131+
case AV_CODEC_ID_MPEG2VIDEO:
132+
case AV_CODEC_ID_VC1:
133+
case AV_CODEC_ID_WMV1:
134+
case AV_CODEC_ID_WMV2:
135+
case AV_CODEC_ID_WMV3:
136+
ret = 1;
137+
break;
138+
default :
139+
ret = 0;
140+
break;
141+
}
142+
return ret;
143+
}
144+
145+
#endif
146+
#endif
147+
106148
void FFmpegReader::Open()
107149
{
108150
// Open reader if not already open
@@ -111,6 +153,14 @@ void FFmpegReader::Open()
111153
// Initialize format context
112154
pFormatCtx = NULL;
113155

156+
char * val = getenv( "OS2_DECODE_HW" );
157+
if (val == NULL) {
158+
hw_de_on = 0;
159+
}
160+
else{
161+
hw_de_on = (val[0] == '1')? 1 : 0;
162+
}
163+
114164
// Open video file
115165
if (avformat_open_input(&pFormatCtx, path.c_str(), NULL, NULL) != 0)
116166
throw InvalidFile("File could not be opened.", path);
@@ -151,7 +201,11 @@ void FFmpegReader::Open()
151201
// Get codec and codec context from stream
152202
AVCodec *pCodec = avcodec_find_decoder(codecId);
153203
pCodecCtx = AV_GET_CODEC_CONTEXT(pStream, pCodec);
154-
204+
#if IS_FFMPEG_3_2
205+
#if defined(__linux__)
206+
hw_de_supported = is_hardware_decode_supported(pCodecCtx->codec_id);
207+
#endif
208+
#endif
155209
// Set number of threads equal to number of processors (not to exceed 16)
156210
pCodecCtx->thread_count = min(OPEN_MP_NUM_PROCESSORS, 16);
157211

@@ -163,6 +217,23 @@ void FFmpegReader::Open()
163217
AVDictionary *opts = NULL;
164218
av_dict_set(&opts, "strict", "experimental", 0);
165219

220+
#if IS_FFMPEG_3_2
221+
#if defined(__linux__)
222+
if (hw_de_on & hw_de_supported) {
223+
// Open Hardware Acceleration
224+
hw_device_ctx = NULL;
225+
pCodecCtx->get_format = get_vaapi_format;
226+
if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, NULL, NULL, 0) >= 0) {
227+
if (!(pCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx))) {
228+
throw InvalidCodec("Hardware device reference create failed.", path);
229+
}
230+
}
231+
else {
232+
throw InvalidCodec("Hardware device create failed.", path);
233+
}
234+
}
235+
#endif
236+
#endif
166237
// Open video codec
167238
if (avcodec_open2(pCodecCtx, pCodec, &opts) < 0)
168239
throw InvalidCodec("A video codec was found, but could not be opened.", path);
@@ -252,6 +323,16 @@ void FFmpegReader::Close()
252323
{
253324
avcodec_flush_buffers(pCodecCtx);
254325
AV_FREE_CONTEXT(pCodecCtx);
326+
#if IS_FFMPEG_3_2
327+
#if defined(__linux__)
328+
if (hw_de_on) {
329+
if (hw_device_ctx) {
330+
av_buffer_unref(&hw_device_ctx);
331+
hw_device_ctx = NULL;
332+
}
333+
}
334+
#endif
335+
#endif
255336
}
256337
if (info.has_audio)
257338
{
@@ -703,9 +784,13 @@ std::shared_ptr<Frame> FFmpegReader::ReadStream(int64_t requested_frame)
703784
int FFmpegReader::GetNextPacket()
704785
{
705786
int found_packet = 0;
706-
AVPacket *next_packet = new AVPacket();
787+
AVPacket *next_packet;
788+
#pragma omp critical(getnextpacket)
789+
{
790+
next_packet = new AVPacket();
707791
found_packet = av_read_frame(pFormatCtx, next_packet);
708792

793+
709794
if (packet) {
710795
// Remove previous packet before getting next one
711796
RemoveAVPacket(packet);
@@ -717,7 +802,7 @@ int FFmpegReader::GetNextPacket()
717802
// Update current packet pointer
718803
packet = next_packet;
719804
}
720-
805+
}
721806
// Return if packet was found (or error number)
722807
return found_packet;
723808
}
@@ -734,17 +819,51 @@ bool FFmpegReader::GetAVFrame()
734819
{
735820
#if IS_FFMPEG_3_2
736821
frameFinished = 0;
822+
737823
ret = avcodec_send_packet(pCodecCtx, packet);
824+
738825
if (ret < 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
739826
ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (Packet not sent)", "", -1, "", -1, "", -1, "", -1, "", -1, "", -1);
740827
}
741828
else {
829+
AVFrame *next_frame2;
830+
#if defined(__linux__)
831+
if (hw_de_on && hw_de_supported) {
832+
next_frame2 = AV_ALLOCATE_FRAME();
833+
}
834+
else
835+
#endif
836+
{
837+
next_frame2 = next_frame;
838+
}
742839
pFrame = new AVFrame();
743840
while (ret >= 0) {
744-
ret = avcodec_receive_frame(pCodecCtx, next_frame);
841+
ret = avcodec_receive_frame(pCodecCtx, next_frame2);
745842
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
746843
break;
747844
}
845+
if (ret != 0) {
846+
ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (invalid return frame received)", "", -1, "", -1, "", -1, "", -1, "", -1, "", -1);
847+
}
848+
#if defined(__linux__)
849+
if (hw_de_on && hw_de_supported) {
850+
int err;
851+
if (next_frame2->format == AV_PIX_FMT_VAAPI) {
852+
next_frame->format = AV_PIX_FMT_YUV420P;
853+
if ((err = av_hwframe_transfer_data(next_frame,next_frame2,0)) < 0) {
854+
ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (Failed to transfer data to output frame)", "", -1, "", -1, "", -1, "", -1, "", -1, "", -1);
855+
}
856+
if ((err = av_frame_copy_props(next_frame,next_frame2)) < 0) {
857+
ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (Failed to copy props to output frame)", "", -1, "", -1, "", -1, "", -1, "", -1, "", -1);
858+
}
859+
}
860+
}
861+
else
862+
#endif
863+
{ // No hardware acceleration used -> no copy from GPU memory needed
864+
next_frame = next_frame2;
865+
}
866+
//}
748867
// TODO also handle possible further frames
749868
// Use only the first frame like avcodec_decode_video2
750869
if (frameFinished == 0 ) {
@@ -759,6 +878,11 @@ bool FFmpegReader::GetAVFrame()
759878
}
760879
}
761880
}
881+
#if defined(__linux__)
882+
if (hw_de_on && hw_de_supported) {
883+
AV_FREE_FRAME(&next_frame2);
884+
}
885+
#endif
762886
}
763887
#else
764888
avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet);

0 commit comments

Comments
 (0)