3232
3333using namespace openshot ;
3434
35+ int hw_de_on = 1 ; // Is set in UI
36+ int hw_de_supported = 0 ; // Is set by FFmpegReader
37+
3538FFmpegReader::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+
106148void 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)
703784int 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