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