1 /***************************************************************************
2     \file       ADM_ffmpeg_videotoolbox.cpp
3  ***************************************************************************/
4 
5 /***************************************************************************
6  *                                                                         *
7  *   This program is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU General Public License as published by  *
9  *   the Free Software Foundation; either version 2 of the License, or     *
10  *   (at your option) any later version.                                   *
11  *                                                                         *
12  ***************************************************************************/
13 #include "ADM_cpp.h"
14 #include "ADM_default.h"
15 
16 #ifdef USE_VIDEOTOOLBOX
17 extern "C" {
18 #include "libavcodec/avcodec.h"
19 #include "libavcodec/videotoolbox.h"
20 #include "libavutil/pixdesc.h"
21 }
22 
23 #include "ADM_codec.h"
24 #include "ADM_ffmp43.h"
25 #include "ADM_hwAccel.h"
26 #include "ADM_image.h"
27 #include "ADM_coreVideoToolbox.h"
28 #include "prefs.h"
29 #include "../private_inc/ADM_ffmpeg_videotoolbox_internal.h"
30 
31 extern "C"
32 {
ADM_VT_getFormat(struct AVCodecContext * avctx,const enum AVPixelFormat * fmt)33 static enum AVPixelFormat ADM_VT_getFormat(struct AVCodecContext *avctx, const enum AVPixelFormat *fmt)
34 {
35     int i;
36     ADM_info("[VideoToolbox]: GetFormat\n");
37     AVCodecID id=AV_CODEC_ID_NONE;
38     AVPixelFormat c;
39     AVPixelFormat outPix;
40     for(i=0;fmt[i]!=AV_PIX_FMT_NONE;i++)
41     {
42         c=fmt[i];
43         char name[300]={0};
44         av_get_pix_fmt_string(name,sizeof(name),c);
45         ADM_info("[VideoToolbox]: Evaluating PIX_FMT %d,%s\n",c,name);
46         av_get_codec_tag_string(name,sizeof(name),avctx->codec_id);
47         ADM_info("\t  Evaluating codec %d,%s\n",avctx->codec_id,name);
48         if(c!=AV_PIX_FMT_VIDEOTOOLBOX) continue;
49 #define FMT_V_CHECK(x,y) case AV_CODEC_ID_##x: outPix=AV_PIX_FMT_VIDEOTOOLBOX; id=avctx->codec_id; break;
50 
51         switch(avctx->codec_id)
52         {
53             FMT_V_CHECK(H264,H264)
54             FMT_V_CHECK(H265,H265) // requires ffmpeg >= 3.4
55 #if 0
56             FMT_V_CHECK(MPEG1VIDEO,MPEG1)
57             FMT_V_CHECK(MPEG2VIDEO,MPEG2) // check succeeds, hw decoder init fails
58 #endif
59             FMT_V_CHECK(VC1,VC1)
60             default:
61                 ADM_info("No hw support for format %d\n",avctx->codec_id);
62                 continue;
63                 break;
64         }
65         break;
66     }
67     if(id==AV_CODEC_ID_NONE)
68     {
69         return AV_PIX_FMT_NONE;
70     }
71     // Finish intialization of VideoToolbox decoder
72 #if 0 // The lavc functions we rely on in ADM_acceleratedDecoderFF::parseHwAccel are no more
73     const AVHWAccel *accel=ADM_acceleratedDecoderFF::parseHwAccel(outPix,id,AV_PIX_FMT_VIDEOTOOLBOX);
74     if(accel)
75     {
76         ADM_info("Found matching hw accelerator : %s\n",accel->name);
77         ADM_info("Successfully setup hw accel\n");
78         return AV_PIX_FMT_VIDEOTOOLBOX;
79     }
80     return AV_PIX_FMT_NONE;
81 #endif
82     return AV_PIX_FMT_VIDEOTOOLBOX;
83 }
84 }
85 
86 /**
87     \fn ctor
88 */
decoderFFVT(struct AVCodecContext * avctx,decoderFF * parent)89 decoderFFVT::decoderFFVT(struct AVCodecContext *avctx, decoderFF *parent) : ADM_acceleratedDecoderFF(avctx,parent)
90 {
91     AVCodecID codecID;
92     const char *name="";
93     alive = false;
94 
95     switch(_context->codec_id)
96     {
97         case AV_CODEC_ID_HEVC:
98             name="h265";
99             break;
100         case AV_CODEC_ID_H264:
101             name="h264";
102             break;
103         case AV_CODEC_ID_MPEG1VIDEO:
104         case AV_CODEC_ID_MPEG2VIDEO:
105             name="mpegvideo";
106             break;
107         case AV_CODEC_ID_VC1:
108             name="vc1";
109             break;
110         default:
111             ADM_warning("codec not in the list\n");
112             break;
113     }
114     if(admCoreVideoToolbox::initVideoToolbox(avctx))
115     {
116         ADM_error("VideoToolbox init failed\n");
117         return;
118     }
119     alive = true;
120     ADM_info("Successfully setup hw accel\n");
121 }
122 /**
123     \fn dtor
124 */
~decoderFFVT()125 decoderFFVT::~decoderFFVT()
126 {
127     ADM_info("Destroying VideoToolbox decoder\n");
128 }
129 /**
130     \fn uncompress
131 */
uncompress(ADMCompressedImage * in,ADMImage * out)132 bool decoderFFVT::uncompress(ADMCompressedImage *in, ADMImage *out)
133 {
134     if(!_parent->getDrainingState() && !in->dataLength) // Null frame, silently skipped
135     {
136         out->_noPicture = 1;
137         out->Pts=ADM_COMPRESSED_NO_PTS;
138         ADM_info("[VideoToolbox] Nothing to decode -> no picture\n");
139         return true;
140     }
141 
142     out->Pts=in->demuxerPts;
143     _context->reordered_opaque=in->demuxerPts;
144 
145     AVFrame *frame=_parent->getFramePointer();
146     ADM_assert(frame);
147 
148     int ret;
149 
150     if(_parent->getDrainingState())
151     {
152         if(_parent->getDrainingInitiated()==false)
153         {
154             avcodec_send_packet(_context, NULL);
155             _parent->setDrainingInitiated(true);
156         }
157     }else if(!handover)
158     {
159         AVPacket pkt;
160         av_init_packet(&pkt);
161         pkt.data=in->data;
162         pkt.size=in->dataLength;
163         if(in->flags&AVI_KEY_FRAME)
164             pkt.flags=AV_PKT_FLAG_KEY;
165         else
166             pkt.flags=0;
167 
168         ret = avcodec_send_packet(_context, &pkt);
169 
170         /* libavcodec doesn't handle switching between field and frame encoded parts of H.264 streams
171         in the way VideoToolbox expects. Proceeding with avcodec_receive_frame as if nothing happened
172         triggers a segfault. As a workaround, retry once with the same data. We do lose one picture. */
173         if(ret == AVERROR_UNKNOWN)
174         {
175             ADM_warning("Unknown error from avcodec_send_packet, retrying...\n");
176             if(!alive)
177                 return false; // avoid endless loop
178             alive = false; // misuse, harmless
179             return _parent->uncompress(in,out); // retry
180         }
181     }else
182     {
183         handover=false;
184     }
185     alive = true;
186 
187     ret = avcodec_receive_frame(_context, frame);
188 
189     if(!_parent->decodeErrorHandler(ret))
190         return false;
191 
192     if(frame->pict_type==AV_PICTURE_TYPE_NONE)
193     {
194         out->_noPicture=true;
195         out->Pts = (uint64_t)(frame->reordered_opaque);
196         ADM_info("[VideoToolbox] No picture \n");
197         return false;
198     }
199 
200     int result=admCoreVideoToolbox::copyData(_context, frame, out);
201     if(result)
202     {
203         ADM_error("copying hw image failed, return value was %d\n",result);
204         return false;
205     }
206 
207     out->Pts = (uint64_t)(frame->reordered_opaque);
208     out->flags = admFrameTypeFromLav(frame);
209     out->refType=ADM_HW_NONE;
210 
211     return true;
212 }
213 
214 
215 class ADM_hwAccelEntryVideoToolbox : public ADM_hwAccelEntry
216 {
217 public:
218                         ADM_hwAccelEntryVideoToolbox();
219      virtual bool       canSupportThis( struct AVCodecContext *avctx, const enum AVPixelFormat *fmt, enum AVPixelFormat &outputFormat );
220      virtual            ADM_acceleratedDecoderFF *spawn( struct AVCodecContext *avctx, const enum AVPixelFormat *fmt );
~ADM_hwAccelEntryVideoToolbox()221      virtual            ~ADM_hwAccelEntryVideoToolbox() {};
222 };
223 /**
224  *
225  */
ADM_hwAccelEntryVideoToolbox()226 ADM_hwAccelEntryVideoToolbox::ADM_hwAccelEntryVideoToolbox()
227 {
228     name="VideoToolbox";
229 }
230 /**
231  *
232  * @param avctx
233  * @param fmt
234  * @param outputFormat
235  * @return
236  */
canSupportThis(struct AVCodecContext * avctx,const enum AVPixelFormat * fmt,enum AVPixelFormat & outputFormat)237 bool ADM_hwAccelEntryVideoToolbox::canSupportThis( struct AVCodecContext *avctx, const enum AVPixelFormat *fmt, enum AVPixelFormat &outputFormat )
238 {
239     bool enabled=true;
240     prefs->get(FEATURES_VIDEOTOOLBOX,&enabled);
241     if(!enabled)
242     {
243         ADM_info("VideoToolbox not enabled\n");
244         return false;
245     }
246 
247     enum AVPixelFormat ofmt=ADM_VT_getFormat(avctx,fmt);
248     if(ofmt==AV_PIX_FMT_NONE)
249         return false;
250     outputFormat=ofmt;
251     ADM_info("Assuming that this is supported by VideoToolbox\n");
252     return true;
253 }
254 
spawn(struct AVCodecContext * avctx,const enum AVPixelFormat * fmt)255 ADM_acceleratedDecoderFF *ADM_hwAccelEntryVideoToolbox::spawn( struct AVCodecContext *avctx, const enum AVPixelFormat *fmt )
256 {
257     decoderFF *ff=(decoderFF *)avctx->opaque;
258     decoderFFVT *dec=new decoderFFVT(avctx,ff);
259     if(!dec->alive)
260         return NULL;
261 
262     return (ADM_acceleratedDecoderFF *)dec;
263 }
264 
videotoolboxProbe(void)265 bool videotoolboxProbe(void)
266 {
267     return true; // FIXME
268 }
269 
admVideoToolbox_exitCleanup(void)270 bool admVideoToolbox_exitCleanup(void)
271 {
272     return true; // FIXME
273 }
274 
275 static ADM_hwAccelEntryVideoToolbox videoToolboxEntry;
276 
277 /**
278  *
279  */
initVideoToolboxDecoder(void)280 bool initVideoToolboxDecoder(void)
281 {
282     ADM_info("Registering VideoToolbox hw decoder\n");
283     ADM_hwAccelManager::registerDecoder(&videoToolboxEntry);
284     return true;
285 }
286 #endif
287 // EOF
288