From 6718474f98be13c841e11bfa0874a5205f71f37a Mon Sep 17 00:00:00 2001 From: Vasily Shamporov Date: Fri, 20 Apr 2018 18:09:30 +0300 Subject: [PATCH 1/7] [vshampor] Added SAS --- vshampor/SAS.md | 33 +++++++++++++++++++++++++++++++++ vshampor/control_flow.png | Bin 0 -> 74520 bytes vshampor/perm_batch.png | Bin 0 -> 162724 bytes vshampor/perm_gen.png | Bin 0 -> 155880 bytes vshampor/perm_pair.png | Bin 0 -> 113824 bytes 5 files changed, 33 insertions(+) create mode 100644 vshampor/SAS.md create mode 100644 vshampor/control_flow.png create mode 100644 vshampor/perm_batch.png create mode 100644 vshampor/perm_gen.png create mode 100644 vshampor/perm_pair.png diff --git a/vshampor/SAS.md b/vshampor/SAS.md new file mode 100644 index 0000000..df694aa --- /dev/null +++ b/vshampor/SAS.md @@ -0,0 +1,33 @@ +# "deshuffler" +## Software Architecture Specification +##### Vasily Shamporov, Apr 2017 +# # + +###Overview +The program is written in C++ (with the support of C++14 standard). The basic program control flow is presented on the figure below: + +![alt text](control_flow.png) + +The input YUV file , which has every frame (except the first one) shuffled in random order on the basis of 64x64 blocks, is first opened for reading; next, for each frame the data which describes the correct position of each shuffled tile on the unshuffled frame ("permutation data") is calculated. Afterwards (optionally) the original unshuffled stream is completely reconstructed and output to the disk using the input shuffled stream and the permutation data calculated in the previous step. The calculation of permutation data is based on motion estimation between consecutive frames of the input YUV stream. More details on some of the steps of the algorithm follow. +###Details +#####Calculate permutation for the stream +![alt text](perm_gen.png) +This step incorporates frame-level parallelism to improve performance - the input stream is divided into M equal batches, with consecutive frame sequences in each batch, and each part is assigned a worker thread. Each worker thread then calculates permutation data between pairs of consecutive frames inside their batch, starting from the first one in display order. + +The batch containing the first, unshuffled frame and the corresponding worker thread (hereafter "primary" thread) are of special interest. Non-primary threads will calculate permutation data between pairs of shuffled frames, wherefore the primary thread is able to always calculate permutations between a shuffled frame and a reconstructed preceding frame, since its batch has the first, unshuffled frame. Hence, the permutation data produced by non-primary threads will only be relative to the first frames of their respective batches, while permutation data produced by the primary thread will be absolute. An additional post-processing step is therefore required to produce absolute permutation data for the whole stream. + +It is assumed that motion estimation between a shuffled frame and an unshuffled one will be more effective in producing correct permutation data than motion estimation between two shuffled, although consecutive frames and the calculation of permutation data for some of the frames in the non-primary thread batches may fail (see below for more details on the failure status assignment). To address this, the failed frames from each non-primary thread are aggregated, and then, after all threads have finished their calculations, the failed frames are processed in sequential order while using reconstructed preceding frames (which should be available by this moment of time, either as video data or absolute permutation data), and the correct permutation data is calculated for these frames. + +#####Calculate permutation for a sequential frame batch +![alt text](perm_batch.png) +As stated above, each worker thread processes its own batch of sequential frames starting with the first pair of consecutive frames in display order. Calculating permutation data between two frames is performed using FEI PREENC, which performs motion estimation on a 16x16 block basis, while shuffled tiles have a size of 64x64 pixels. Theoretically, it is sufficient to only perform motion estimation for a single 16x16 block inside the 64x64 tile to calculate the tile position on the preceding frame. This may be prone to errors, but brings obvious performance gain; therefore, as a first step, for each pair of consecutive frames (K_(i - 1), K_i) a pair of special frames (S_(i - 1), S_i) is constructed by taking a 16x16 block from the center of each 64x64 tile and putting them side-by-side in the same raster scan order as for the original frames. The permutation data is then calculated for frames (S_(i - 1), S_i). If this fails, the algorithm falls back to motion estimation on the full-res frames (K_(i - 1), K_i). If this fails as well (if, for example, it was not possible to reconstruct frame K_(i - 1)), then the whole frame K_i is assigned a failure status and the processing progresses to the next pair of frames in the batch. It is assumed that the primary thread should not fail at this point, otherwise deshuffling as a whole fails since no other means to improve the motion estimation accuracy are included in the algorithm. + + +#####Calculate permutation for a frame pair +![alt text](perm_pair.png) +When permutation data is calculated for two frames A and B, one of them serves as a reference for the other in terms of motion estimation. Let A be the reference frame - depending on the situation, it may already have absolute permutation data (calculated previously by the primary thread), relative permutation data (calculated previously by a non-primary thread), or no permutation data at all (if motion estimation by a non-primary thread failed, or frame A is the first one in a batch belonging to a non-primary thread). If frame A has absolute permutation data, then frame B will be assigned absolute permutation data after PREENC run as well, and it is marked as such. Otherwise, frame B is marked as having relative permutation data. + +Next, PREENC is run on frames A and B with A as reference. The output of PREENC is a map of (multiple) motion vectors per each 16x16 block of the frame and corresponding distortion values. Afterwards, if frames A and B were down-sized using the algorithm described in the previous section, a single best motion vector is selected for each 16x16 block (representing a 64x64 tile on the full-resolution frame); otherwise, if frames A and B had full resolution, a single best motion vector is selected for each 64x64 tile. Either way, at this point a per-tile map of motion vectors is produced for frame B relative to frame A. If this map specifies a valid permutation of tiles (i.e. no two MVs point to the same tile on frame A), then the calculation is deemed successful and actual permutation data is computed and assigned to frame B; a success status is returned. Otherwise, the calculation is deemed a failure - no permutation data is computed and a failure status is returned. + +#####Permutation data +The permutation data format for frame B relative to frame A is simple - it is a list of integers (one integer for each tile of frame B in raster scan order), each one representing a position of the corresponding tile on frame A in raster scan order. diff --git a/vshampor/control_flow.png b/vshampor/control_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..1ababd85f64709546ea9314a953db3a13007efa8 GIT binary patch literal 74520 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þ_XIDqi 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

Se`ap9*6XLrE* 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

Go)v)Hn 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%0AjQ}d3t|62>4E4-vVk0 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

7#FCXXd)S)4m{ty@`Q`ZXUT5qY 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 F{trAS6WRa( literal 0 HcmV?d00001 diff --git a/vshampor/perm_gen.png b/vshampor/perm_gen.png new file mode 100644 index 0000000000000000000000000000000000000000..248e9a06c21103a6fc613f3cf27eb25ca38dc9d5 GIT binary patch literal 155880 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 z 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 literal 0 HcmV?d00001 diff --git a/vshampor/perm_pair.png b/vshampor/perm_pair.png new file mode 100644 index 0000000000000000000000000000000000000000..7b21db2a0bf1283f8919a41543953d9f2b0c15a1 GIT binary patch literal 113824 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 R_`8snNlVG!$dxek{2$s75(oeQ literal 0 HcmV?d00001 From e115cbceb28cb2b2f884776f0b764e15508d5938 Mon Sep 17 00:00:00 2001 From: Vasily Shamporov Date: Tue, 15 May 2018 14:20:15 +0300 Subject: [PATCH 2/7] [vshampor] Added more details to SAS --- vshampor/SAS.md | 26 +- vshampor/preenc.png | Bin 0 -> 63411 bytes vshampor/preenc.svg | 3298 ++++++++++++++++++++++++++++++++++ vshampor/preenc_map.png | Bin 0 -> 72835 bytes vshampor/preenc_map.svg | 3495 ++++++++++++++++++++++++++++++++++++ vshampor/preenc_single.png | Bin 0 -> 38567 bytes vshampor/preenc_single.svg | 3105 ++++++++++++++++++++++++++++++++ 7 files changed, 9923 insertions(+), 1 deletion(-) create mode 100644 vshampor/preenc.png create mode 100644 vshampor/preenc.svg create mode 100644 vshampor/preenc_map.png create mode 100644 vshampor/preenc_map.svg create mode 100644 vshampor/preenc_single.png create mode 100644 vshampor/preenc_single.svg diff --git a/vshampor/SAS.md b/vshampor/SAS.md index df694aa..7c8e676 100644 --- a/vshampor/SAS.md +++ b/vshampor/SAS.md @@ -22,12 +22,36 @@ It is assumed that motion estimation between a shuffled frame and an unshuffled ![alt text](perm_batch.png) As stated above, each worker thread processes its own batch of sequential frames starting with the first pair of consecutive frames in display order. Calculating permutation data between two frames is performed using FEI PREENC, which performs motion estimation on a 16x16 block basis, while shuffled tiles have a size of 64x64 pixels. Theoretically, it is sufficient to only perform motion estimation for a single 16x16 block inside the 64x64 tile to calculate the tile position on the preceding frame. This may be prone to errors, but brings obvious performance gain; therefore, as a first step, for each pair of consecutive frames (K_(i - 1), K_i) a pair of special frames (S_(i - 1), S_i) is constructed by taking a 16x16 block from the center of each 64x64 tile and putting them side-by-side in the same raster scan order as for the original frames. The permutation data is then calculated for frames (S_(i - 1), S_i). If this fails, the algorithm falls back to motion estimation on the full-res frames (K_(i - 1), K_i). If this fails as well (if, for example, it was not possible to reconstruct frame K_(i - 1)), then the whole frame K_i is assigned a failure status and the processing progresses to the next pair of frames in the batch. It is assumed that the primary thread should not fail at this point, otherwise deshuffling as a whole fails since no other means to improve the motion estimation accuracy are included in the algorithm. - #####Calculate permutation for a frame pair ![alt text](perm_pair.png) When permutation data is calculated for two frames A and B, one of them serves as a reference for the other in terms of motion estimation. Let A be the reference frame - depending on the situation, it may already have absolute permutation data (calculated previously by the primary thread), relative permutation data (calculated previously by a non-primary thread), or no permutation data at all (if motion estimation by a non-primary thread failed, or frame A is the first one in a batch belonging to a non-primary thread). If frame A has absolute permutation data, then frame B will be assigned absolute permutation data after PREENC run as well, and it is marked as such. Otherwise, frame B is marked as having relative permutation data. Next, PREENC is run on frames A and B with A as reference. The output of PREENC is a map of (multiple) motion vectors per each 16x16 block of the frame and corresponding distortion values. Afterwards, if frames A and B were down-sized using the algorithm described in the previous section, a single best motion vector is selected for each 16x16 block (representing a 64x64 tile on the full-resolution frame); otherwise, if frames A and B had full resolution, a single best motion vector is selected for each 64x64 tile. Either way, at this point a per-tile map of motion vectors is produced for frame B relative to frame A. If this map specifies a valid permutation of tiles (i.e. no two MVs point to the same tile on frame A), then the calculation is deemed successful and actual permutation data is computed and assigned to frame B; a success status is returned. Otherwise, the calculation is deemed a failure - no permutation data is computed and a failure status is returned. +######PREENC call specifics +As stated above, PREENC works on a 16x16 block basis. However, the range of produced MVs is limited by the PREENC window size (roughly 128x96 pixels) - see picture below: + +![alt text](preenc_single.png) + + For our purposes the desired MVs (specifying the tile permutation) may be larger than the PREENC window size - as large as the frame width/height. In order to ensure that each 16x16 block is being searched for across the whole frame, PREENC will be called multiple times on the same pair of frames, but each time with a different "offset vector map" - a 2D-array of vectors (x;y), one for each 16x16 block, which specify offsets of the PREENC search window from the center of the 16x16 block. + + The number of PREENC calls is determined based on the frame size and the PREENC window size. The principle is to break the frame into an integer number of equal search areas, each having width and height equal to PREENC window size; the number of PREENC calls will be equal to the number of the search areas. By this time, the frame size is aligned by 16 pixels, but not aligned by the search area size, so the search areas will be overlapping, as illustrated in the following picture, which has 12 search areas (red dots correspond to the centers of the search areas): + + ![alt text](preenc.png) + +For each PREENC call corresponding to one search area the offset vector map is constructed in the next way - for each 16x16 block on the frame the offset vector is drawn from the center of the block to the center of the search area. This is illustrated on the picture below (only the offset vectors for the first 9 top-left blocks are shown): + +![alt text](preenc_map.png) + +The resulting motion vectors and distortion values from each call are aggregated per-16x16 block and passed higher up the architecture for purposes of finding the ultimate per-64x64 tile motion vector map. + +Since each PREENC call associated with a search area is independent from the others, these calls can be distributed among threads, achieving, roughly speaking, a "search-area parallellism". + +######Checking the per-tile MV map for consistency +Determining whether the per-tile MV map specifies a valid permutation of tiles is performed in the following way: first, a 2-D array of M x N boolean values `bool hitmap[M][N]` is allocated (where M and N are width and height of the frame in tile units respectively) and each boolean value is initialized to false. Next, per-tile motion vectors are processed in tile raster scan order; the coordinates N_x, N_y (in tile units) of the "target" tile , i.e. the tile where the motion vector points to when centered on the tile it belongs to ("source tile"), are calculated. If `hitmap[N_x][N_y]` is `false`, then it is set to `true` to mark that the corresponding "target" tile has been associated with one of the "source" tiles. If `hitmap[N_x][N_y]` is already `true`, the MV map is deemed as not specifying valid permutation data. Otherwise, if, after processing all per-tile MVs there has not been a situation where `hitmap[N_x][N_y]` is already ` true`, the MV map is deemed as specifying valid permutation data. The complexity of this algorithm is O(M * N) in computations and O(M * N) in memory. + #####Permutation data The permutation data format for frame B relative to frame A is simple - it is a list of integers (one integer for each tile of frame B in raster scan order), each one representing a position of the corresponding tile on frame A in raster scan order. + +#####Reconstructing the original stream +Since by the time the original stream reconstruction step is executed the absolute permutation data is known (i.e. each frame can be reconstructed using only its own pixel data and the permutattion data), this step is easily parallelizable on the pixel-level - basically, a single thread may be assigned to each tile to be replaced. diff --git a/vshampor/preenc.png b/vshampor/preenc.png new file mode 100644 index 0000000000000000000000000000000000000000..d11e32c5e651ac54a1fa6a5c4f24e1b5442c94c8 GIT binary patch literal 63411 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

CiJLwMOao4_=j2=7Q&`jyGGqr5@ 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 R)5jbRLQVHt@zpya{{=Vb5jX$< literal 0 HcmV?d00001 diff --git a/vshampor/preenc.svg b/vshampor/preenc.svg new file mode 100644 index 0000000..00d034d --- /dev/null +++ b/vshampor/preenc.svg @@ -0,0 +1,3298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + 128px + 96px + + 16px + + 16px + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vshampor/preenc_map.png b/vshampor/preenc_map.png new file mode 100644 index 0000000000000000000000000000000000000000..706bb7f307107547e94584160abcefd73afa93e6 GIT binary patch literal 72835 zcmbSzby!tf^zEUM29ZvYE&=IorKG#NyIW}xknWc5?vU#P5rg(rWvR71a~s#rfMjT)CMrns;fIQ*z&GlyRJ_`} zPBVp}50*62`FM7Btj4sv(*;R+@r@N!RGAdAu%T3;pBuvPLlFyhJudWBEHJ+3yA!?!jrR|ApU(J;%I?%7X0rzg82XWVp|47y z(9;J$_#aOETrX!-dI}Zt_j$^Eo}W}}OrzW>A>N?>z9#%W3ldH6mVNsMEgXcRG&wm5 z`r-X`uGT_j>*sgBOQNyPPDqQnvbY@a$TEj7*DD?$Th2$h1Fu@QM^d_0S9R*Fmaq~S zw1lj!%jR1>>epPw#lL;)8qZ| zU5m4`GY+?N2?3uc$HU{J(NH|S-PYi%&8@Aq`{QjTQlb?cn z`}^t}jck_ly6~r%qThkA!Jr28dYL(3@-^}Rf zQ5iAn-xWOu61s_8C$@Rr(-amK)_OlVbDt5pZK50fny);IQ_+rcf-z-cVtW1Vonk=| z0?76LuzrjG`F0Gi&+TR{D6S~C$)@c#@8PE(s(IJgjk&bm5?Q?q#B8YfH&v(Y4K#MhI ze)04gkf5{k^Gf3pyh@j2ZLoJ37#Mb^@=<%k@hMYSEtEGJy_e$VPL7Y3o9Bv0{oqj7 z_h*VhAPWl%$Aj6!_-oSFzb|GiSg<+8iQJ$;*1Pr&&d#vDc-)&`27WwT=Oc+0HfGhk zQ^A3#sj2ll{a-jaZ>90LO`dq~jHX%eY(HMFVuxYTV}Q_bajCmHI}Jrd{8g)s!@r*O zPBCiKA=$KE#^0O`F>W>3ZCK;o-<)pXFzNfttEs&JxjxR_Ib~@-K@AzZ9Y2AjJ_~8A+leMnE>!ms?jSVJXqwJ1Ft+(4L>*HC%wdo4^ zQ_B@n>~wU$;@4C?J$XF6yy9kxRaP0yVT_SLIbAm6>I{2?G2qe+SWgc~arU>-%rxVSiQ!jt~mpUVGO zDmd$L+)5cRPV9cR`Qc)JM)LMz>Z7Qd+JbbSHxOg%(}fBkUvXkD#k>3a)v9;^QRFl= zk$~Lt4+c&)3-09NBAzebn^YdRss`J&PU#)jjc|7BhV9kWrz`fT zloXFCJ9!DIPEwFC)rzxovdB<>6? zi2^1}dFxd&kNsva-m6#S&lmap1KO?bdb{IU%*@R4)=PELrA=3>0f^WYz)Dk^4*k(o zs|-UNU0mQY+u2rQKJpdD#>cPN{&;>|eGW-X#DM@2ayxeq#?c~2hW%M@0Vz{IS+)k3 z+u56^r|Wk7&I%wYH1BS1rrk0CxRA@s%PU%0SqY4Mg@v`A@%)f~a&jWe!^0DspzZzv zp7gEPonsP^jf+c5bH(ZPEAEos7Zc)>0|SM8?|%VEdHNql}ifj7A&Xoq`~P? zTjXbReYBj&W|h1)KRvDZutEHc=IgES6>vmRtyQMrpExr#2TDnSjFFL%>v{-fEQ`q~ zX#5!Gz6V=py4c~UJLF}yIrbuucAda=(pXf+0|y7!(AfBWaFMeBoeUpq4mKJZi4yf%3C$K)^o_nqc1>&>Ao%K$82^HdREQuE`b<$( z6~;&Y$C~$&l29O3b#?u%!8r5t?U93)j984dwYBxrK4NSb7#P>>Bojkz?R5A3Y3j@B zF}@fG2#7!fM$HCau!U~!?r3grZ^epR9d2`<<=5gPns{SQHB0iL)0gJ9}UKsyFTZ^|8;;ecNd-zCKXn zAVIwmgtYB<`?O{T22j}8*ew^6lFan<(j&veihh26$ZPXJZI~$6;Rn|sF%P7}rF!-j zovA?V87~0b&(?y7VL_PK*kqiXYM_n^y{^rN+jHFYKj{{=jkAe9A+4y?b3Vq;*!Fm( z0kO-TaI;@rzBylMV1sLVxLAuw>~#_K8^}BX3<)H(EIz-y6qSl{M7^0Hyw+ zq2_ZqS?Nnc(*p;s*3X|I?G6`NyV}D-LuG3X=>P6*7T8;qAV3yAfBxK^%xVFhd2Ffy zpb7!Mw`XfY{Ly0V5Spjc-ej)!wHsh58x9|>x$0)ZfPjF&Yb{O9eV!{I3~~6qTY#F{ zvbF)FaCZOTAhQ_fp9mIl2>>DqVw}-}kl8ReIyUwV=&@?j-yhm(xyVF<8(e1^QCGjmrJSp2O0Py^|9wwDIMk zA-BgBT~Yi7qyS*2V|RxTrDA2=XhC9PVh(QZIrm=xRw8qsqu{-R2SBV$v&pd#NL*yl zPXO-|-(}`BJm=$;$B0pO*Av~Y zdwe!aG$k!Nr&}Olkh3(ZjYr0-O~$zKng@Hruus3k5#x+F)&_-hJ`_Z z4EiGC1_lPq0Wd$lNgd-k|5CC@e+8He9ucwFLCsu72E}+J*$BQrPdbGHAUXR41^^Zn zsKefi19^(W=h z(kW~(AfT$~_JqD#*Ll86Xt3Wx0O@x4LcEufQlzJ+x51<3;nAd1uj#jyad2=z!eth5 zcX#icnE@*4tN!6(5l_z+Ag7=*+r?G^ra2ANl4ZxX?CjS!_xH5G_Cdb5EXMu7l>*71 zCKIIB6&Mm5iwOZLD=P#3Qnnb*`)^<)e*&}IJe$=v33Ky(t7Q{lMXX0W8od3@FWK1G z8k`Ror3JNT8dp8fM^cL+W=6dq2x zQ0@D_lCtw!D=C4UxxNmhz{TMbHo$X=8XMihcetGPF~LLv08$7L;LsqTieD`#7vw?F#P_?m)3te}YRxLCbcqi1Lck;QoUtIzF_j^SmR#Q`%=>lRAX zSOE6CJe{`t^QW)fM(=>n(|JZ!9eF3Q&qF0pD_+XT$W+wV_l3TqHAg-LJ(e{|#?wWC z3kk^g@&0CU;jy~nJu2w;uV*{CIi@gZw*&*VGb$q^QZj+zdv7l+;3S~0{F=t?QZ~m~ zDT^F1~B+1Z(_tSpIyq~rt;y$h`#EBf4t=qEJ$RQmsi$Y zw3RTEpz7}7VQ?^8igX%$gzkfM4$92Vh6IxMH8u4Mo0ii+*WIijGy;NQYu?{kmc7(L z5{Wl`d%hC|Kq>|XMuqhT@4G^1UHJ1N#piStrB^`HbF-J90SyB8n_qC4gaQKt^}B=7 zCnqK<^$;n4ui5+?&Suoa%&ZgedeUwvbClzvx7}n-{L`Au34mVEb-ys*)#{Jk+(|0QB9GMN+w##0flKUl1_5aa9vzE?C~Chc2L5L}bffwBMKpDKNX{HvcV zr>Kcy1xUvppv@5l@H$P>)PhC78@>^6iWxxOKGTF0WYMcu2((-+TC7LnFHPs&*^Q*I z_W@1TW+IaT8qj^GuvwY!+y5zJc;v8dhVnoMB5ZG8rP}Vz%VzzMxf<0G&Q9v4-@|#R~PuOZqvF)-!vlih$dP~%g`dVT>b=HeGm|E z+dy*}92WKx$f)=4-=ifk>cHAM)N6wQsoA;Ia`Ibci(C$@R$9~m`o%I#)+DYN;0oAqceR}# z$j5o7Kie{dMmj5)C|)V0^zUdsZleW|zLp^@*u27ghNjZ(+!z3m%7cjCKI!iVD9yiX z849x$k>vN=fd6D`0QLGWSc*8Jf(h2&>-QJ+xivM42N-`m8yrmZ9xTdS$4Bzry>9^; zJ9&7!sU6LrTwt=H@TcG?cA{>Ga0b1xwADP2mqk!f>>#@0C~=~u5?6@Lg0YkX z!E7bZX8XM=%ONbW!`p7iHlvqU{yC`x6Tgj7H@nCWry~qURp84iW3GiDF@X_R5kPoW z!vM2oD}s8lFkOR1XlYA``W-J{H=;Os_O)JMv90h+OINPFOQCK5yv~gOuJUU|<>b@t zH4WEy0`iJZvri%cdK*{>+-Gn&e!K-VDN!HjdP;rxBe){|#*GbdZf zzg2X{g&M0TK&CY!D(Xeja;Z=i)jhKpRRoOwfZ$Tsbn28QVGmF)S42U&y~O^_F$pak0jVEym&1%a%Re_XG<-4zV!8eLfDm6%Q~Ec2hq@LlIp?M3f=QN?+t4RzEWVa(Ymi&=UVXNFX49g+?|c6v66w9tQMkyUZ6&7XSOR zWUd`lPNr(&3(bg$Hmt~Y z0Zzv%yW!OePBe>XQa0~tdBVMND|$J6-*zwPtu0D|%}y1a4eh_aSLbsPJP zS;;8W{eKsl>|&RtC1cd1SW-EMqY zB78&Ix#5|oTNR{F>npR6jYBmVEYiZ6u_~ZB{O=E!UPOcGJeiIf;z|=You*^Y`0MSJ zXp+YDvKd5qR`jbq1RXc1#6Ki^cq>p-tyjUF9?0|g@tVE77?A${Trv7z#V_MyW1&GD z?iW-5y(+L{{Rfb!fF!5LNKQ@;CU_u{+-^X2n2-C1pVPiLn{ushC@I6Ls#X~v2=MWX z_5SBpBJiy~NJshx1_80jIxpk?BJA#`?H_T! zR_5k3;JwHx{d?~3z2rwnV^}WLmG*|y1D*Z9$o3!Yd7|{Bqkc{sguRr(go0W_FbzZRxCl>SvBURU6urGPvd zm5>mm;q=4qTK%8hr+}(d8yXVQXe6Lg+fqlm|5pZm?f#XiKSv$Bl{}-`8Gedf5rFjo z^z#eXRc@%H7%%bPhflOd0Ix3q6nB6!)><28QMG?01iF{*Zjl_PEi3MNWYkih_aXhE zXg#lbdbtjH4|c-%H(DQ$LnPD+ov8nA?i(rKr<~1ALpgPI_pZJ`B;3~!By@CAknDkO zPKVYW&8c*0C&A}r_THnO6Rc&$Ra8Dv;~&x{Mtvw5;&rAie?1f4X1JOhb7#8<*^uAc z*BLvZ{aIjC9kBQCC!FDMToy6F7pjopF1f;VA{)Q6rJ;c%$7$1z+nvnpeY6x#DoF~v zS;UD>XMjpeLe<@kxpY~NOWp<24}%|ZFZNz`-&UO?^F8hG@+TDEl||pqot>U+2gL=K zz{_PRzjG!0w-fc#aqv(a-YXy(s?M~&Q776Ika6oDi(3WB1y>j z{-IY1Ox8ObOz+$Z`l$xr-l;mtWs)=~RcLBgXgY=+b0Q`*`nvRoeqHTh$$g zhJk|r=MX}Q!LA(C;&b=mn0D<4r5l?ol}_itkcZ-13jF#eEM@u=>0=wivvI@PTqQ}K z>vM-}6(Pngz;*2XH%g03b>f{rp>el}$zSlgDu+lKHBPeOjpLuRI!8{4; z$J6uy5We&{OLKF#=p)P9hL!VK4)ZV>q%|wXZL+{O;w@4rdPm=f*e>nniEN96GS?1& zGl24KeA}lOS$HNe4sls{^>JVeW=J$4kwL;y+tH7q-pXc)D8%n2^0Hg&OAp#8&ZuM? z1fjp)&g&wHw3d~Zht$x}n668$V;keUFDYsA5#HS&e^q>67S9dkQlv%Lq;EVcwT8Zt zMMtl?Nb*UY_CQ$`t3rwbSz2C0y}o->E2by2)GxL~QjLPRSwhK0d$xeCHd2z9lf@!$ zw>~!`S|N2xm-TN<%XIC-A?W3wr{yY*3_)IjR%P})#@Lr>7I?h=FstWA{M zlbrqPmY1vRdDpMezi`)t3WOCt2PbD1-;kMw0l<*E&G*FxNJ^{{hhGfJ%e(k8&}U<{j5h=s zKTs-MCt)IKA|;XW2G)kKBz7^0PpV{cqK>Q%Wq}5~q^m8<8>=e=m%oFH2`^wYxaPS(|;z!EZH}qd|MREHN4&W{_ zLIc&UH56P{eCeTL{Zvrc`fvVvAB@SnZRBNj!ZkiMQm9fYk7(zoyWfXDKP@C0g) zTB6j<%Z*z%%cV^a9?VE&4(8MT)=9^5AZAd%2+Rp2F)<5=pG8KCZi(X5JEY>o^GF$D z86|t4#B^1dY!dwrkb`xGwfcpRNzt6Sg7J5d0+h`%@A0DF+-rY{Hm!qW3A>mZRmfc= z*?9RU?I)IjGJ=Zfpci3VZ|mlIq35I>>2{WY*dCmo;z6Uw-PIjsAuh#Lm#ZIANTgsi z+fU~tGK78>^W&qxiDegk{VTMS1`HI>1Q_;e?ZhNj1*CZ{fq~ZOCA6-8FVMn}x965D z$^a$?Iaabs$buViaSSuI0Rk9G08c=UxHZ`N4ui_=K&47qZ!oxN-a z4UeB)te%WdaK0_KG_O%-w*ORxkooZ%pI>|05-P$mIh47W%(jIGlMvMh;ou-$48NWN zE)oK}9F(SB?7O3n+0cq2&?78vk~0#*KK^y#DJiTYYRb3TsoLw+zq0^PgX%+oNXQe| zVOyM)w=czMMkL5A5QRT+o~*+Gncag#A>3I%-!>nR$Yw#^{a7~<6z=DP)NDj?JOFVl znrjSI3Oy1yJHc|*PS(XA7^%0|{RpdtZqKj%^!dKHF00 z-8I;Gbu*2jsHwX-G%HPC=Q7~MuKQGHVT)Z-t-;zWql;KjBu@Og*04O2qQKo31Oi0@ z$6_aUde?fH-;hp@AypGCG=|n*b~FwNXsG-o#kR6&bQOJW{*+VtWD+wge+?0jCGZio zl(z8sI2xIB15H+Wg+n`xme0|OMGbe7f!t6EJDB=u7`J1^%(pDWtdEru!jlZ5eU&Y1{`vLSCq7v{lSR8? zj!g*RCxn_s*wj6^H(tn^*3ciY&}TA^Xmn=<#&AVME}GA#d~X4VVLHcecVOGFrJYbsmV4&MQQX?jJvJ^=*!mTuuA2 z6W7>O0&CMrV2(U54>Aj7QHbD1A7mBiXF)5Xf&sqt4?Z0!N5?~UrIdJ& zFU$rh(Tbx<+B}i>Ghji!_=-vXTa?&gYJ(P#AMe+l>ebG+P;B0dCbA_w>fxVELQ;K1 z`Sft3WcswZbY@4De)#Fz^R&xoqD;`K?C^+h*HCSF?wF)(L~5bc6I;p7ws7}1!4g-v zeKdY63$>~juU{jqFX;CB@RlDwjBu-mdY3Oyv(2%S@_T>V*2%Bi$edS)fQvWhH*uE< zwBP5kz4JJ}0jd3YvKjwm!(-7W zZIK6}XMt$`zV@ot_4(CckN(e1Jm3irgu!X|B;I$JMH&`X7PoWu!IAAjuIFa`g$A{0 z?L$61L3@%K)T6JM?4JWClH23WSg|B%O-Kx)t{i$DUdI;acAP3L&SAWRlGxTG+3}H0 zdv~VvQG8Yxd)E(>WX^ub5*(*N!j}T;^h*FRPl78>48M!Y56w}K>j|D5pa!CU zNDM(}FmJCy6xv<2u8*fo4IHREYTiuVun_4^hi4-9h`!mLHd$Y7dsc>HYp*sbF2Z_h zGPiTG7uG*eZCsm%l^RHVooDzFBZZ-?Q>ZxlY_Q>If#aD1Ofzgii61^N|?-ePr_DleU6EB8%^n#SD%)I;8B8?o;yT#-yh@ zrfL1hr3WYb@irnrsQ`7Bp%1TSBi_v=3eiusV1dxa8B48@Y7?KYu7i7J!{TzEW^TvTrI z9OSMJe`1o^^E2eG4+{Ld2z5I8i+To`JI)ev4U5OpoYJc?H<_zD4U3`PQzuAJSTD1W z=tfezQoCfGY0-1rWb~m;*o^gqmOOq0TO0^P`fe5xXxT&BXv}SZV~5 za_^U4!`eG2SAw{gF#gmU*4Myb+FY)7$Wr@#I#-uNpcGFGRa2jfN~cReSm&frOgKPM z{5Z88Hdo|4W~%ktVoWbnsC<9^IxdTiJ^7UDIIi;?q{rCc^G(?UbqAvc+~Zi^i#mLMe|pf} zPdy}&1?h_mHCwHjpjui5kP;RoeEW&gcB;ZmZ5tTjIwaM+{^A;n6%{;itm= z7NYabtLmiymzH{rH0+V1KicW*Y}j6s$XyheCfLC*=(y&wKCvyb-Vmfq!Xmo%ne$#| z^#i>Z71tfxf+<^f5C=v{>VZG4zP~o~2^RjOd;zadjymT*8fB;t81qX(xD&p} zz4%axoZ3Fx94WXfjpFV=J_#3TRJV?1YMyry1p1@KH@YEoWmLTdv2x(e53uK+*k1@O zp1cFIBj#Jns&>O)3K`;0^bO@-Arx~YnS)s%IV7B{zkEM6_S#G zu6|GmIH4|;B2|nY_FTK{H56ciJHqEi9jYCsTz0JR$z?S8^{SZux}Hc8SgjP*IIC<^ z{u9ER5}`)*2s`e+khT!7IKmy5M8lRmEa^(1GXe)t(MJX1;bnG}i{Rs|H>n@qn&qd~ z=de#gUM{1|av6Eyk^@piv!yyC5Y+@9ATi*UcT`V6xVEVX?xue?=4OGHR~rmEJ>Wwi;u*L~wzle-&AdlNNqlTcGLZoJzAr2!fNDg@2veuAOxAz}ds{Y$#{Xh+LJJUz!Y7B0 z7>5$L+v#EuVnkNE+a$nk5OO|SqG3*OlhjZuh3C4LRMm%opjJ5LYb|;OCI{r_cdGkAEH8&O9H_BtJoLM9kjh^Y8{#}BizB`vYHHhx4(Pc|yQBo-%5rR5F+ zM~n80*4XfTHauQ`1XCw^CD1S=IX-~xhS-Z)0CcHmh65kIEbxilSf8<_Fp>ou@y;PmKwS(PTu4R%zG#;Od^IfQa`ZWQ@@;~44xv-KWMR;F~$QU z7`T-NYEW2NrB+;|13y+=)%FOzJJ)6Rs2=wWIfUe9Nc79yg!zwvUXnEnI1PAIy2B^v|n?!4^`e#JKIPSJ0C+qh~lPjS$l>>-*3?+Owsc)nv~` zD}ic|=g-6}eHUmM)xPfl8&FfO_%ioTgeZan7He|pvKJW+#Wpw`m9*dByse@4paugaE8+<5+R%8{mz&H837y9+QDBu$bQY?czhA zQ8sxKM$8ur6KfOri|xxqlX<=R=lG|=_)dB;z&VzhfGJv%!x$ z2S)-mto1$MM4p&hzz`=AeYpH6GS^TBsA)B_x$aY^p|TfqZ;<#h`DUJ|8(VZ6*Pbi$ zHjd-tl3s!N)?rs&@o4-g{1Pq&*sOFsZ~j!BCTu{4x{t^SAft20>vX?Fr+cCLdL)1| z&q(GaA)B)-7$yKsg93MM^al!hIyx9eelwV-^+$Lq6wKS!JE+onJSzfGDtHTgifgz- zm7U8(%IjChU0_`Y;1#cr&9&36oeFH>FqGGbrC%Dc3>hc3SX9BkM=D^2w=|o<04q8R1v1MF&$QsLW!9I8EnZ9@@r#%KKlD0pRw?q=_PNUN8FxagB}^ zz!iTPE`4a9I)r_csqZT%W*?~VLAn_Hbp?^tHSQznL%wGjJt0I5q@0MWSbxie+dh7} zP5&k39O4+qM{-Y<8~SpV9V;e42p(4atGo%Lx&FGV#ET>z_XPdlwNu@aAOl@1W2@a! zhbr67m;Ain{}qJSZ~JrL1ZHIF!F2CRZ)rx59(K86$_h}=0MFZ4N5YT6FBmN`kysI3 zX{`iheGu-k`IJ7~;>Cj$xG#fWY456VT*a)Rof^U@y2O zF$6{dU^s3LbIt)C4Jhb~E?uqoURrnh*Zs?ZM=eKo%W;79tNw|LL{xxS`Du2th4w{!@o_5d;Sn5x7I4e)NX z0=huY`_r=+%}477NyK*?IzqT0i1sx`$e{;YPlpx%1ewa0b z^7Dm`r?=B*4h8qT*?Tpb_(0#IMwzhzn@Dfkrd}td*AA~m8`Zt1PD@KQ4AeT98=d-< z_YBv*9d9pjsWSCk!reDsbO8m!yWLqkHQ{VyfOz4bn0gH;)gm*hH1XiP9wv$_EhDbb zcCI`A0z^>g75*Zv0(1U!Q}MyFt=YOh{DraW=-0v>1>{dFWns-K6=HAxT&71^eo`c^z2 z<@gT+5}$eq`QL!pK(En_iC(iWJSjbx^=0^ryCuT6T;PZrC0rqL+z znr#RL<{JfD9deI{a6_cYkEeZJP?7`Ft4}vCyBCHQXRA!gc|kvml0N1nw#!3ehvE07 zHmXA^?&FEm-{xP`AA7>+O^Gb2*FDwEZ#TaQZkbw~jrNiExj)G2J$*m~nJ} zobo&q(E?9>l!VW+W99a~*Rub>BO+E-SG&1g4+3e@S5MZVg~L-zSMX9UCjcMhi2x3z zQWJaMWTo*%G7hu889umK0eQ39b?B3TUTQ4NbOARC^us;o=?cJ4Up72kmADO+^}*6In4-( z3uam{=o5bV5ASprBo1hR6IiOP+yv2uIxcKS3}M$GV5DnPk8}o`Ks{0#v5_`#RD^Y@ zbbURRRe*7`Ym`_L&#Wm}bQd>YbT6wJO*xK4$kgvy^sBgqgkH~pbIJMeoEZA#`haSC*%Dfkd zT|@93?i~~#*&Z_9Ro&rqFp9p(vQOq9uRi(eieoT>U0|&*5ZLpU-v7Vnm9mLiw}wMP3Ta<>lRu zEGC5KP4-o7%ROssn97m&6Q1w|Z168Kw6(0S+Js+P=+CSqR*+A-*n;_%NuPOu035~C z#w7OKp`#nBI$gup1`fSw-(!a6!k}@g-E*Fgs>p_xLlU!pzP;Z=MQfxa$qN*0s-1Km z?ameKenevG#y+e3v^Z;jh0*OHNy=3NtxcT&IA^vbSNA@My&$pt#M4H@?x1h) zOAlh@_ka0)tHGtl{}#`zm1I2-MQ&a{Z;e4qsx{lxujXM=xFa#P2Ygtv+$6VKcVou> z*CNw$F>ZO0a&hUmv$WS4H+wHwckE?akM5Y$^FSq7WXO5~(CjOH=SJ0rCoa8-aE~YSm zI^WUnZd2NH?$uwb>{{IR`P62#2YuXYPk@!4X~Uw<9rY}Tr6-P&Kj8Wn+Y}!uX~tG! zH_g1s`k2b;Ds`{E@>Jdy?>1DM!4jroy#H2JBB9OVUAd@4p+cau&G^8@!4;bD8ix9PdbTSj?vt)N)>9S(Jg z1N875tc;zDbCvw5w)maxgPmjckj1$wdxHpIKMzCr)ZzM1Y!^#@m6*}}{z%-*uzM~| zmuCCFr9>k2=daA9UZ1I|S&v|#7Z+=KfEfWZJBNx_()=L zjMdx_M65gdSP$_-CE+Fi!H@R*=+^klvHhv<p_ z?zGpLi0fYj60kk zY7wK0x0*FaqadK88rKMv7YnPD95t&SA^j+HcG?a7O{~8JfydlmYaQje)woEj#J_eX*^V-o)A>S6++94=s%0zj8UoU4aie0!hE|dUY&vW}^pw!o z%ihK=`+GvHNeirGX67q=q(fAt03QxWA@qPTt%d=g$N=+vw$w#g3p8g+Ku#}a9Up47 zzh3qQnu_lcdfd}sPVLY`050Xe{vh%#O^MR$J^fV}JE)CKut5DX_XS1nM+`5Fdn(p| zq^mgVU_u<)IaF1~k$|TMurT;E^Uz$@3O<(cBa@?0;uTr6crc(M{YP0nz?Ai+eYJyd zacblz8-%yuCPX3C4`BVBE-~d`a}>nJiQyNwT6qX(UTfy>{5$s|F+cj&Zg()NnXBGc zKy~SD(#zN!v0L$h&lWH^Elw2}i3C(tj3#sL?cixiY9_(&Q3~+nHjO(PutL;1*@+jq zj9v2)tO|f9Aua2zu?q{;qJ?D_W#;A9hC@;I#|5qX=hkCGJ#JxOCBG`y7V*E%DjjH0 z3rJ(@I{xZz`;hH0RRt~ZfURBt!wzsir&-ouS(61m^8=i50IM0K;|D~mTHs?jm>L9a z#SR)Y@$fE)31E$ig~y~nWAypW1KTb2T61Sl(j*J(N*~a={5xC&Cg88cRp7yc6zKs= zh-&=uMw9>dyk?@4O&n1iEVa*ns+6a6!b#`R^gl+xt}y(ks>s7DC8N+pyx}-;98ZS)DfZ`64N_~18S9{r&t+Jll;ScQ%c8p z2-Wn~@=RkIeH!`#nLMB(7ev_bYcq^$(1A~W1(yV?CluFJSqFegIUkSKrPY2IaoN}_ zoSksUr5Sd@rG++@^<%m);3o|H`MCgay+fB!vqK2Akl^3}Byl<0(w}AfW!8$ta?Boz zb$2aG=k{_duRm1~HSE{SnC|@fl)`IfHlVG*1LhuYE4xG*U8c2vD{{WuP@9yE8No96 z+`mOZ;LOYyHf8aph(Lj{$H8iW9UG?Beg&{QHnm!t1zgC+ z93aY>bi{PSvF0$i4Lj$J@}a0sG38ch4lQsFhHt!iDV&_h&Kr6_IA+HG`-{niN!%D0 zX{^7)4hu~TT9WZ4W-0wG?;Gv(znbVl!frYvw@+84(vVfPm{H+l%Jm#$efYWjx=bG~ zpeM){lY}UFtaJz?7JQKz;~OC(B-@keBD^uGF<21!Cdah zsC<=KB5t8vF!w$9;r$zF>1@~)(DgePimG#HL9{=={C7g&H(zu*^ePTvrVy~n6}nLC zu$UviQpWD2Tn-9f;1&y|3E8i$l23VJ3$_c*B8Vh7GT9W@V6-+e{$MPxt4z}TaAIG- zqhve?{|@mRq66c}+xxd%b_dW7QqHlSES|)TearCS=-_*6aLscYJ$UWzKf`*G{-MnwJg!@R06YSoO~QE?t8jxma`m22Ji1uFQs!kN;-&GSH~4+ zMJlAy@=9s&qq!VoT`>NH_bO~l6QfzhWqgbu$=SW_grU( zp&={E1L?f+7Fjm;V^g!$8Lw6h+bOvT!B_)Ka=@JK+U5PD9^)68?0MM#V^M2#@N!4Z zXe?fj(|twQ!8~)Fb7h`>D`Kim#Uk@IKHiw|Q0_|8A_w-1JoJSplBcQUK)m*g-M!)i zvO%WMQMc3$K3Kk*MpqUV$#*g9#DOeSwPV>aiT%3b4};OK*!!nR=KRm{xnDW(enChlreNB9q4YPcA9G+B026 z)?*e^tFU&XcCqX6LcPho^K$r|KiTnelkIj*pZN)25&AC$O>ss#c;1cg)wJpIRoS-e*OOr0hoSm%^5S8BGj>4!F8l(ePs3c}^#E%~^m4RW=Dq;kpk{Y!blNwA z`iNRwb^#+08>eNqLbc3kC6@tuGWOuF>qW@{l^BoQAI@b@nuC}9RxxQVH@#~siRyn- z2=F|YDD;_sMtE`_9u6gr7EhO^h?v90JF(K8Et^ZGYJ2 zMc8F}DDN`V|4u36hcL4Ey!gH#^MY>~FXnd|pGiP6lWXJZ#O z^?^)W249cTM|0A$_hdE*hq>N`x#fCXHjpubkQ51nsrslwog#AY!>^fmt(y8rU;Y=# z>A2|qFTL5yX0-eQ>R`O=P7$}0_i4hH*_A`7B6Q~t%B}|=@CNZXobEBthfWO0UW!Zj z3_MDfAJ)z)NKg9@Ya|T=7{Ynv5?hyGWAoDT-Nd`^6hYF8+giO{zq)qU{lZRw+h&_f zbHjV^)wCS6BIk6=4I(*qI0n8KZDn;v2M%CCxCGs#U$a z)4OP%m@-O_>{gp=KBbZ3^~%vo!{yavzW#gPa47<6UQ{*rVk?y?ql_V*PSVe1b2@!P zecHxiFT^Gnf7Jd0puC5Jmz5WK33`gWT#{#zI{6KSp4*kZarFG;UY%gmd1AHWC{7v^ zIO&koU|sOn#Zto9Sc0V*f<*1MNZTWfs$Dx3~4B5d}-PFnyWk35r0hjs?#kn;4L2O*ylSe0- z$ZGCa0+LYl^dLIS)6lq73=mlFN#N<>Y!1em=2N)abJw_6s#eW?S}B|A1CA#%h(>o; z2=)laSP2}+7)j-S3;o_2`YaFsAe}I*m+mdkKQ;4!a+6$MhE&F;W;{}iNhMG!-<{g;d7i#i!#Q!IWjn za$FAQuf-W$hVh1LXFZGlu!Ln>dFGr)InCS#ejjlKMHe3jyZp#xu>7RwOBHAT4qe1$ zSG(5!xyf_4G8JAOH`m(**#)8A8Qz7)4i}^s$_39kZX$jcu0dzx%N2eaex=fptM0!c z^T}DZ-ZSdL>}*UuuXFmt{e1%|U+j?ZhzNS1uCzGYn;(+rgtq{B3CSwR>Kyt)LPMJ( z*)*Es6B5!x=$^}wl9GmkU%bX0_bhcyJ_^;PxvV-6WN+Lw5_ z$M2+P3!l@}QP)kIB#m9=-?ycD8MUgj0v)>Pxkz!Avu`nsF{c)hiT-J~Ky2-bkLetq z9X=nSQPHXyXNZy0UDwTX!oTB|IlWl2C^{>afR~$u9yk?fQrY|HM!`GMg|o1IGbT@j zVd7tQeGL^h-)p&P6pY#e+l&1+%3XTFw7Gj9M3%Lnz_NKf&P8wHPY{pa zv*G3$yh8YIeEBk_dnTz1YycqRpmQX$6!|1_a_(m0(`~E?U~iHB z>(?)ies7@B4g^L(u|f_ENZ>0o39yt`nwtK8cvPPpg2)7Hsl~)}&<+y0-57L!SkkI|makSM%8O-)!9nOuXXrU%8EJNlI-@;uPNU;l=@FYeSa!jA&4 z{ozu}ms$5)I?d?OY+eH1{&MURlRacLI)?O z2;kL}dY0(#e^)_4;SbQw17m?>MC+Xd(_~xN=!|}rx7L@j^vVG4hg?@W7h)Uk$U4(? z4RbC1ux199W0qwrnf#PrNNEtaRnSVNwx*q@DeWv<2i*D(Kcx4{b=4%aei-tcIJ;U* z2g~dppTI9|cNeS_GdO-q0|}CwSYte?uBo;dM{gbxqQ%}G`daTW!&+xc{i>oT0hqj{lI>Zy$=qS{WBfenb?*S)_A>i3^ z4*W6BfE|*Xsw!cF(%JOuR_EdYDq^&7F!@7F4Q!wUZ6m-5HzAPnAoOqueG?%n4 z>=yg>3J0{7D-Pg?b=Y<0;A8o{Zd{o+o!}y(V=hBoRQpH<7OeApVMqiirkq ziOQlPa{*fr*}~)Fd-(YHP+-8x;%wLZ206jq4D?keK+YWoj6tv=rv_#op0FFRFEs4z z`TRy0z{Uf(I#tsC9B zCv0dsal6!=1U|hLqqlPo*$nAk)LFC$>?79HD@yb{lu{e-nxcx+>4-DeKC0;e*^jKW zvc>RvMQwulvi6boyT)x;dTpkI2ZhJoUMv&S@1{cnc;nnz&Q}QHEHY}4iN6HY3d8?(MyGWX1@c5Ns0J zy|%SgS|=cHr)9QI=t{&?a@1wjjRNxwNoNFW1CsSj(}V>&zUL2MdBU~I#R8Nc2{!Q_ zV zh4o}pQ**P1tE*rwWY3bElXKS_IXyQ4tb*uubab-y8oWY*Wds2+aR-Q+R6^sxW-BFn zlj`OS!^+&;bxFM%*gF{k%K`tzAgWyrqM`opbw_XOS9R$HrZ*pO6D;-ylayVZr2(Q;Us=zQcvYeOdcif zC=l;7lu%!BxN`^!UQu6Vdvm8uk(;4^;Vw;!44vLVJ2I9sakLy`hSDXRIus~$2(@Z# zFcDj&*n9+evYfp&2HbEEiypG!o)P7p`pnGl272JXtJ`hG6a5q68L-ABTsBb z#>W?deZ?8DPniTRcWLZe6^_xj%+1Ze0lUTEuV20t5wgC8{uAX=eayu4?KTER>Cd0{ zfFBM&zvW04rQUG=57D@S)S>Ay9lL4XA8Si>SE|6mQ`2ny_*mDyf$x$%0$H{N-rjkB zA`=cnkZNzsS!lVmdd^-t$<>8+Fg#NFsjaM&mgrnVkxxr^xhrU@r^KM5sPbi{9POwH ztEu)w5T-)7L+wByTrY;u_G05bK843B4A(M#p_xSiUe7ew64GnOIT6AF08S z3ZXY32jw2)wN;UuuA$4TW8DFz%7@-8zHk>#Jn?mL>zV6F>v{&ujT7M`s;-|xJYkHr z^usTDL+*598ZcloD7!v!jXGs}qo({ER18pMrplj7;IVMq^mfUn)A)4Y-#8I%KW%-> zd(7WQ-c0W+euMHTub}!RS0A`Oe<5Gwv{`3fTnw2Ck(WGIlli_{v-K+mSH1>O-5gL# z$%<}*4C8ghnV;b`twa+{e856`KsTdqmvWx(P3K@4s;XWBKiRsvx_AlKJ)gNM`_JCMsqoR@9tdp8hdHQ2|o&5I0R;L zV%(B~22E(7g~GHW_3Rm%{EHX*S{2p?dfr!js4y}zvX+*Xdq!d#T6+PbF+;U#F-r@hIufI2TngO63XvL34j|nxL{t73_tNFW%=MD#Mpomo!&1(!S zBZ06=uh`d_EZG&|L+(TeP&u(#QWyhDedao9?c(4B|FmDkr30=sHXMkayi;{~knK3t z&Nzef{iurJr==!amFewx<@mHe@qf$^^XmVN9pcQV$F~wAfz4K!vWm(+u%6|C2|8~P zlarG_0(M};#Ke;F@&UGpx=!FUF)%3Z3?8-Zdf=xh*18H(jw9fe>3lX_lbA?}_;9EQ zoTy2KL`3947y|i4qu%ww!wQg*P@2^PuLob)V;E%6my?@2*x-GG+YVeTHyr_%@Zo$p zD)1XHfS_gQe@XsWUy@UxQr~rAglgR^N%X+N$`W00o}@q;M|dwIJr?0QndguDro9Ay zdfol>@F_`#CoH?4BNG`0{yEO`!#kxI~VbU8M!0BbKjS0P?}drrzGmI-x4gcH<1D@fo9BDUTw z6?!+{gtlDtqxbLbgnyyC+S+?-bUa`9KpH;~)nd5ZL>XL3WZ64bEpAs-#j(h!Uz>DA zde}mw*C_YLBa5VC0w#RpIVVJ##g{WIof8^b^L37-8_4htkQ;`7)nXp@R31t<4Kq(| ztWQ@qKTZk5wlwOaD7UBZ2&aV}HuAo4FA_4~WXdyl^Xe`pit^Dy@>9RBC;C=H~*$;cvlT$*!9`n42Eexw;t|eu6e4 z!Psx7LkGM5khgq-hq=ml*T<@oO5((#)-`>(VU_a~{EUJ(w_&`JyVb5mXS}XHyTGB_TJ95 z)-4Q$P#;Wh6vzHsd@}3~D-F23F8s--sJ(KH`&W_USsFMUo@9c@lmlmBe+20okI3{7 zYJJ|`e#ML$WQ(>laV;ogi6-NTuAq&>G>UR=cPnPMGAXx|7-cL8HB1fGZ_j53aO;*X6GURB)%X6vVB=oii*~F<1k4jRmk^}6`Unn3Tp!)0AR-@p~ zt}fsOj!)3uVazS}^5xD^!!X@HT!10en-`k`Mh7>+;yl#-exbEk$YSYTGr+>1B&WUbIjTe!YKQ-qi zZC<$WF#dqV(;q3Po-g<$n!!s?+)EdlJe7x$yP0dT)LS{#JIp55cuuov@3rTS&fC0n zLRM5yD#y|*5xF>?tt)dqAW`i9k!x){3vb$rDs+C^=Vc-NydW{^AHM8mz8qEd=9v41 z#blby#Cz|s$vJMK&_~4Mw(2}J^qp`TMOW*+IZc*6&Q@V46rmqs9B;e#>7jRXqGDs9 zF0x%Xv!Cv&EjCtPD`c;;)Memq9*#3#6Gw-l^*C)H%}jS?i!Rz$7ta;@Z`bPJD3IJ< z!&#d?UQ+W(D1^!2Vr)G>*+106AQ!~pm&AWZHhfyB@&P@%g!_+B@$PsD5=~u&=g%Se zrOwKLP*LqEN@Z~}N&-8Zt6ZNBTCFQ#5i!aH>G3waTK%oRLzH6l-NH|q-FxER3C)x4>F5@}J0NOZ3VY*0cyZlw?Q=VQ{5 z%{<#Y+aM$_Vjw`1Et;}uBgX#%-pSS#=-GXI%iTNPJK_aH? zkv_d@yVBcRyj(oP(&=A>Y8*mFCi1cEgpVk^XKs@xk zxzw#1I?#=+$lD!%`mDE$=bP_UY*yj;lV`onUQ%gG;Bhm#5Ml3EcaRVFO#-K@$unQCXP$6eTFbFx8s!KvDl2>U)a6}nft}+k%}CFw zgG;Xq9q1T#|-yHe%0$@+~>oKmQNGkJXi0IZe{Pcb8rBEa{nT7 z^|U*@Uavc-<&$aE{VUt$l+}fE&oQcB1P2(<3qxm#xl~hI8(%cXD>ySur($cphYs20x&r>;r7@R-X!w8#u5fRY=5S5*2A4^)rfD<_2xxUI(%F z=XW;=!yN?t*g>oQ$Yos~<~Y#@h91#2KJsaD>a%~uU}O$w<$q{r3bn4mr{=ABy^iW< z=fZ^g#Gu#JF5Dgpvg_nLYSR#78c+@wGq^iiTq$*eg5@Z0SaeJ%a>JG4lKV2+!M`-z zo+q_ePzpy1)O7L@6|*(H{fhs(=ij{G{9!59^SoJ|ULlnv#edgAXcqX+hBa{j|g?l>|2P{2e9!)PHU ze#QKG%l%eDtdl>I^nB=~WV^>XIDaW(r!{C!7_scojp`0{egQrXK#)6KcMT(DBX#bs zmE&UjPWrvpb)p0i-lI8eJ?G*T!?@yMKZQ)F#shUZizVyhwN>@ zmh3MC=8{}>m>U9rcrO4jm#nNr>g`F~Tsm-rF#%9<_*ZWU@(TyPYo0o`HvyRp*%0k! zA$~DyrZ)b$;Ip5;CFr22h|vg*Sqj~tg$^4Cmcn9sn{4n1epcpHTU-%Wu^%XnC;r?0 zkL3G7_Xp5$pr#^(>jfeKPR^;$cw{pv&w8 zXa)|Ht7pdWP6E)~fr~(Fum|tI>qromNF1`f?G00lFof{b$O+r@5k9UQ*^5!11m1@gmnkBpq zM}RmX^XlzAo^Jwc@7HZM*~BVohB09eSQZI2+(CU?T(eS>>8`k|! zI@z_KyrV7yP$aM6A39LOGc+^U7whECV_#ckCD*_p?6e2LHB>QVlNTc3yk2+Uqnr4g zGT7Ol0rLR_|3Hlts5MP7Fsw4%3=q0-5ymJa@!7yCeG*UZ>{|#~jq?zA<&_ z_50w{ntvg8?rxhNDu{T|deae7Vdq0)vQoQ15L>co|BMqt$KOdl9XkH-C@jA#Tw0NT z0lfL!Cr>dBEUm2ev&8-YyX41q2+<1T&IfHm7_dC`;!gk-J)T$t2F-VX^EL`JQ)hmC zommH@7PD13IV`Ro9z5r|?qC{JdyZ;uBC*>f=m^Nm%d6zc7Sz;`?!r$gwZO~*+CL}I zB#on^BjI?n&QSWTrOq%I42%c?ku0URT1-@Q$}v%=`aQjf`_WohB%`R&!O*l^QWs?u zB1hKp@gOe>%=+C~Cat_3fRi$)3OOM#xz58)Y1a^j{gy0HKTf}9$?1E8?Fl-H$Z@Zp+An<^E2nH8s*mR>b6R zzjV%OF5M?FqJj|I&$Z|+;z~d+{26~wI+BVJVLFqT?ZE_)lMCkKX6SqaBwALNBd;JW z2!Jw7<8)EozFnGKHm8n-GZn`Ow=hS>*PwoOP;?@kbo(co-{DMtDE!l9bBrCT;36X< zhw?N^qk)ZfSf{ZyL?i(vMB3SzYb$3jt6D)Cuzp_q{*fObd)Bix5yVzPM;#$_MMXtF ziP+Q|pGrwhf=0k37!?vzU0n@ktk9sB8TWYtSJgVF)bG!MWwhNyAPLXJPa3H-K$wu@ zjtNwZ7ko_$<_bY53d^kzfGUvw^XE_8aWGxzZG}lU5jqTHh!fQgjJr(Tdv@MPGB6wi z6E-k0@$EJi(Tni{9ghXluVzZB%BtZA-2|72=fW~4Sx3(54F>)&0}#l6kO>AsxU;jz zHWZVfE}+N6tDebzL7=~Er^<_KN<$y`bYhZP=MGZm> zDX_-aeAo~PFYrO&6xy}0Jmmmuv`QmrQEKJHzRSbI&a;(R69Ve}Urf7?|d8+V>s)U$(5SB&Q>_(hgYZ7cXC$f{_Y1@`{QkYK6M0 zmpwBx;8?wUvO0IwfHG8Ps4XnVQmsv3u-L2)+NJ z6fan;;s7!vRCCOyiDDi^pdSfc5KzLyV2|7Oyl+mukH9PyW;r7*5ioWE;l9Z(?Q<}9 zb1k5uuI}i{0h)H!bM-p+_3J*Ia}#lBXHBRmxr*Z`Q+$yuZRAg{T(#<>V`BZb-tLmW znV*xEGa+BtQTna0L_>PHeA%BHVHT2}2|CDtTqP{F$qP|u|7N;OZv``QVS*)eG+e<< z1S|s`jM@s!ODCtK>M;~Jh$l7Uu6WVKcUBpLe}kxIQ?g0<^&>N>)iYrk<;l<7Jul** z{`^F>$LdtPJYvhRc-`=#X3Oetie5KhDf>kF=v~KufTETh$hJ4UZ2T^`yrtFT6PG95 z%*>LO@tqi1N)Bs=G5-fPysjJgClF1V&?X{@q3UJ|jEF|;=Bq|#s$`{3k) zTUL`Ig*+&7|4o(7kHM4Si%a1T(A9mwxY`5$#a`zJCWwK7#@w8oGcYW$V{R@9B$zc~ zHvmE@24nqLn3?AV><~+CZfM#u)DFWKl@0)jM!dwnEj24AhnAY!2QcH-!1RK; z?CkI31khn0vx=*2##LE!HV{6c;tml-8O%4AYLor{YncJS4RrHIo02!yYtU%qM+u3C)avj04`4757F}mU_LJbhm#AHeXiTu_!viKu7a)b~ZEcnjd`4 zpk+5OI{Gy~pPh}Jorbe|8wVgyN;bVs0kXR89+qR-&gqpR&)0Z*?le}n^+h1MN{?9T zSQFi#_O5f>)xL~n&_5LPTcx*R>a-oxR$f@YpLk}1V2}AAeH!owoW#Es1ZX(O?wy`;#s9aBTA&>U*0T0E{WUU#O^PKwsv~Tx#jsqYW0%t_s z(TRUQVd7S#0y>@<>WawTX)RBT?5FP_znsY)ny&8|3fm2KQE(2-1k|12dCIcmg}0`) z?G=hb`H8WV|HGrg#sfUG#!K?@#41ztr> z;n}uI$C~`S{H>$&kw<+0@aVosq3IMh;5J}OK$(8{JNgur-@KqdY|`Jg0&*M2%6$%~ zR7E~T|)7cHovT>=Uof|w8OChcwzPe$07n3G#bQ16zvfYEU`7?0$E3> zov&S7V28!$%z03)Yx9Ybra5E%MK5e+!g5o>J9?52^1woePOVTzekPfh>bGfbIJ;fMp>|j^v3gh>O3r zVKH~`ep4t{pb`ldmEZNOJ+&BL)?q7o^gv6lqNrb9+-wme=y|dR^?BS`SWT#VVS#ti zls-fXf1kTtR{DVvi}Q{dgd3DsTWK_Qz%)(D{bkGpUpdzFR>w(E5qh>bhi~iaMR>J} z9*bF2FTd~bbuRcFkWs<_F*wE*^itU$RuQd9}Cn~bL*c3bo*c^YAI-v3~Pd}Cm zRTsGav6`!hZ5HfuRmGtm**#IXi)wVOi2WHR*pKFQY&mUp{RhW)!$Zl50`|v?WFVy< zwsv{ei*JEF_u+b^9|!g>7Nzb;GtTO!YTrSBG{|h&=@Q;Imazp2E6Hk#L+799a2C=(`t;bmBUsOj>UM?qMa#KbqRCbS z(q~e}aBZ&FA=m+hsDAQfO2R8@-d!~+W1q#$y2Vp}#P%_Pb4tT#m-jt%yqo>a_{GMk zICn=oKXJ7Bn*PU+9%AQGmPQtbeTaF_g!k@L`f2><=ga`;V9+f6H94f=@7z$|f z7C)m9UA|rKzX0jM1E3dH`a@bLN1USIeeTUqXRHJ>7r+J*Hdk zeeQj1EeuQFQN(~w0eTtiV&Sqt(t$M%vUE??3qmauxPPP;JEJ5{-V==Zlld~&B}4bs z9uaE*xt!RvR$6aol^?~fTN$U@d?lzP@X!6$Je@gb3hvy5_Eq(n^`)6{n!!jmGNUE@ zo;ALL?uG8r#&@-J@B~JFU?O)v;2c8xaE=Kxv~_ z@Y=>&o}2!hSxN*ZAlbc|-c8>ztWut`U8~D1j;x%sBkRbm>kvMysqK^yno2Fd2b}{E z41D6Qnu@vfZ9FQbTdzjG9@iCT*ii4jx5zBGvM+X7g`{9Hw5E&(CRu~-yah0C- zKdo;u#N5uG>V{rJGJpOe8N|dolHanC$+(0242l{-GlESryAYPtJvC|In<>W{ z`0Tq{mDM5Tv8NC_4RDMB<*%!-&u=R^5Lyw80x=GeaasD zrg<_yLZc-`9@_s<(=yT&LLh3|Xie_G>%reE_HoQsZ;7b#c}RB?-J*U>OS}|zTzr+e z8^x{C=25Xbn5~gn<>YMz5j8dZOp6(g4_@}u{QIzo>ijBud+!V51}LGm)+;zbq$oW? zRC*-6?7Rrf+fF9Q`VQ%S4lKVH2E~6HSTEgykA}Y z39*C3T3^PRqQP#kS5gLE%ykEW06IF5mBa!9SsCKIaD>;E4>Up;s_~pM0WrITOdo?# z_BK?h?<#mb-#i#A?(g$%tIxd*f?FMPTy?G}@xC=reYuoj1YtXDA6Los~glq58Pzt*?`P=gsTezAgnVm8As;bW6%x7d1XI63L2g zsWW#}By(%YemWuG7?Q(EHxG08V2`=IH&)SMYvuh24kM`)JHfrlcs-5u?eY8q_|jc! zF8!rF+@MXI&wk}Mi61eaXjIRlYPu7eTcY>ixbI)GT`p9<@{b2e zyvu_Ewl{2d{{naF{y?(IFB~96KnGVI9LrzwsTxP==0xE7KvWFCa#sGSaSyOdhf{A! zfT}JYUHCrigseHLqbK8@k_V@ljfo9}McQfADaxwK<~bGZRJJwHQ{R=8&(yN>v=e@R zc&F)Z(*$E{QjxdbnShplna`*E&m!dvHYVH7C~y)Bo+T79anFvU=ait9d0NeM?}}w* z@kfL2aK?fnU~t-Rozi(L?M}m$q>wmY3vxIh$)lR0Zo0{2cN$;>o~TZ$ZAl^VIsq>h zJWL(!jecWG1qMk8H-VE|#Rj{YaCz^FE1qmQr66i19uvTH4HEI@996_>+LD?pV#lv= zbiWZJjzOJU-S5NMiLV_0=;?6J+D`Dyy8Su!#fExe;^AxOM=$~Y%SXAhMkv3Ho+ORZ z^}o5g$0sY^zjQGja^*{n#6j`dR(PRF+L*87>TER`D9-h-m2gHH6i82>&YZfDn^uSk!T%G+`-7PB3DHuffoM*=_^1N(lZqXV z)Yfk%;77mYeyQ6y*c9e#7_$GaF)A^FbXEU!JYY%@hw zMJS*XdTcwbXtxU%JpG3Y0Px@MYM7ALdKuB(3aC~<``L}(t*dcG4Mp=1v-^5rUm*+) z(pkTI`8d}i2L&dG?b!G?A&yUl2P#YhKl4BsOLFS3&p=GX1Vy1#9`qEq6ziu4XzM_R znozF>l*Ucp=-Pc24Fo@26;1pX08R-8f=B8*Vho({A3$}OdsqNfd)z~hn;;(k`^xmc zTM^8+UQw)@bHcm)BA}-W@Q44>e(fNtb#*EO60x8*20)ciB^6b0pk@u!hJcXn@3D{F z8_k~y$^4@$U!Lv$tW<+XR_PsImhVVrm9fH@X!di1IZOCohsld#Do?VD=_h0>giK(RwpjQ(1F)zupMGZg=HuuW% z1+xSLz(;L2ZiggR6g6JofNC25e=z8Ns4o4qH_{<*$Lh)A1`1)g@!11<9a%#jygp<3 zU1IrYf=|e(H9cvthPJTo$#agJ;U>aYUc5)cBbC&J^`+czP~ zWl7gm7k`2+B)U;lwpw?L8yH$YdE1=NnE3quKpfIY($4Up<>l?a^bPJO7L-8LUrA=$ zEHWDhVTLiaW-u7~20GNhTI30KV6|ao>(}jr@`&y1sb=&X+!oLvfpAGEXMu_(i0OIo zexVPM1f<*cSnOEP!UaoF{%^B`1k&Z-OB8lnw*h&BPK?V}07irs7^4N^v?uvDWH1V;tS3eKb@0j_g%>SdMFy)y`o9tT=hT1E zWw-^p5~3=?EW(h1Lj$g0Q!x)!>hA5ugXZo84!8}RDhbrF?pMc|q3P|7)MPBFe__SQ zf3RXG8I>N!Y9j_D>-8+;Yw5#4C>2oni^5*WXgYzyjsk~Jvty2BU|n#ZD)b|U1EVA$ zmcE=7I__BHjo>3(E(=h6IP4NJ$2Bch0R_8SfGs}=1DfFEj!Suf4MXim=vX0toZbgQ zVd+F^|M$~l=~YU!{e9g)=HFY^bMFm)=gI`Rbo$LM=965^`X{tM|CI*}bpQ1z*E_VM z2e~v8q*or|tg_14{df=vc&Yk8J>Nu!7iG4tb5L_MQu=RZ2~>Cg&&)E@Jn{A4eOKke z^b*kECw$VtxXmb_Zu7WlyE$7=n|b8mQ;0p!XT=GLCkJw;hM6}>aFO7Ei<%3J>*^@a z`_lH(&H~1-!(V(#W=i&vL!4~IH`hHYp}n_0Nczvr^fb=H@0 zN!_V&o=$l?^;Ua#8*w1H{1HQ#lX6z(7jM+*@oBCGV2-hhye2~oUqh@R}XZ5A>MliIP1`l6c0ZXlD~1=t09T@-|SGitbcx zXS`w>-E!K7lTZ889Ik{Aot5tZ@m#~~Nx??lmiqk?foIw|4Mz$6oX|G`U#Gawo;H|k zEWNB8v4b^}`O+`(?Y`~4WB$5k?bvkha-4|G%~qbAT}#D%ymGnzp#f!)BSX1jxf3M@ zGc2=5OZ0dAdU4;sJz;zOO3SA{!*g1(Cf~ZiS^($N%l?RKJJqK8iXo+PaRYZgp+Ir1 z9H(;ah5npmS7LZR|Cwzv(def&)WL5Qe&Vp(a zd#|FenQuT~F3GPr1A!hAZa@DFg7P@u&7#RCeccI+90J<)tEcAjYlOap8=B73zFGt$ zM2=^~C?A<>nON?yzWT2F-7(_?yKS4Cv6-j2wYtWk<|B!XY;DEWF;ToGbq`KYagP;= z$!!zn#hTxZ6N1p5;ps?>xczJhAR@MS^g|}5+pYiR3Hc=VJzxP19h?*ossDaQ%3Cj& zxY}w&nbQi6E4C&}c4yo@%p!E(HTj2?b`o+O^7lTgV081* zTeYkurW#c4QWrV!$=O9T2{fzqb;QyX@afd8C3=Qh&3v*nb>Y8zgA9V zyqCAW^4ObpRc=?{3D1tbAT>zb6cv8(;h7LHcmsFu%Qgn`mmStJc)I1~K-`0YDsJ;5y-nF@KDors0c?dogdqAMvI=Y4N!uO!!{zYI3nZTqlB^BajT|?B?#XFDAO( zYp(`q_&_*Xc)##PsV2Cm#%ni?4(58YH=?>~qTPFMN6T&Z-|Z1c8@zIUY^Jce&>8@y z%SY|jBRSxp#qMJA@EJ_@4$9zd@x)<)L3_6CL7PHNHUHI=HbZUHst+inCFpA&uzP6Q zWH|QD=&uA(%{Sco)v51nzc+hYJ2%t(tZLh1#_hxK-JjD)pZ52SX?L>@z6kC!yxZ}H znK)ZAp#D5~mU=QAT;4Q&nkLxkxJ@4mM%r^GhT8~kjl+C}Z(psIpx^ho|-LAu) zAjjbN@?=Z#mhGf?hXv9ZzIL6ir?XIvbi9=iD7x-s9hH>l`{Qs84MwZ$8vHoO#E#?bNF7 z!*Q=)V4S=DrJ4CnPD+BBxM+%CNIT`k6}}s(h4H|rkDiLh_jN_+=X!A8Z57T5!u~9% zCaa>n)S-@K9hH=#8|c1UR5D=4R_@ez9OqMwIZ(McGv%G%S^@(@T3)a2F>Ch9(S$Si z6h3P_Sip|f!$IwkbeTJhLXkpkEC}Ww9Lut3>5Yu5O0T{xSvwvdkwVCZ%}(9wt=BV? z7yEizIiO}t_}-n2A+*^a?u|18}SIXgs{*4d-|M2qL6 z2I%s3wPO3~Z*$iRm$eQ3Z=iq!UJTQTMw2q=4J$t$%kwxYtd8}wd z?&-cJ5V_K(8Lq1JB+FfJDt1$V*xWnv3yd7OF7hDxh$XqE18L_*KQMCZXcIF!2K?z9 z&EyrLfnM^W>eJ~?yn<>%rdo7jvRf5!k%qbOSR8e)6i-^agFmA2e4bU`>%gKi!~6=0 zyb59dHEkE{wHcoI);6$3E^&Rd3`+f8i9kIIiY}$7LHFj{XlA*mr?a-@{jfr;uwbq<@G#>3}P9jNy$ z&e|$5s&%T~Jwf$Bc%JHv4vupbB%=H6(s;JZK8v+YdA1UkE#Id#@Om8sQj`<+rk8 z*ZTLsTsQkV)O2uNx=VaAv50cys(a@sfUeUQ!-^<*jl1u0 z$BsOmZ6dfIlA$r2H~G%s^8sQ}=FT7~hGm+joiz-v&sTWP|uO<*hNb$_1Yl&^($^7f) z7->-A+*?tQ@gTH>`eNP*r!y8h+NKLC)_=0vZ zx!`_4e_`49!noxTRd@Vp+2$#{5d7Z>-PGw^QeFN{5Z$b%k5GQjeQ&L%sXV7cT-#TacDhkpdpxdi z?yx#|jq4W1XWX3X0tbufWt%3OT(aGS_P_FZVx!alL)=?NRo!*{!jyoL(jfvOAT833 zbeDv*q;yD0V-S+k4bt7+N{5JaY(l!b`>a2g*L^?Fhj+Z=d^&p!1#xfo+AHS##awHB z|H9un5hequKpeRL>jlD#XF_s($0u%wPH(IpDTGys!$x507S^Y=FxuH_`HZG9M~t$` zvR2MXbqEkCOmpuyEta!awll_RMv*6!$YruVMZ!72`Mf<|UH>^bHH2K{>ebprrV)Lw zVBlD6F`!LC5erY|HZ?wvhr!(=hhs@WxLH>El%`!dY>l6% zV8q7pyu)yyqf*GOWy5xi9JKifc-tthDDPc|p1&VnksiX7UX^~%q=CLWXnAHb;UY4; zZaN{YV}07<#-^Ee(_7D0mw#>AU07;I>%rP#f4qJi4J>#IM$rqmF^NeH4$tiZPe=pjL?`o-uW~N@N&ah};ssYEXqWtX_ z+2h!q`dhn#c_O>_Y+oZnKbRu23b{1z$sI;+d`+Q1gq&4wBbZ1Ke z;t1Dk(v%-$UzYM!A)ADoY-`#2XJbY~IC-cwYvGsrV0Fw)NHb~P)AXe@jrEY~$3^lJ zzUH{j4Ghjfe`bEPx;ND5yegNNh=&=(kH4V*@=O^pCRMB(M1uScy@UE8fWSs|t=Iyr+?i}x7m=O5Tyv z9K{RsM7|r@FtnwwK;w3U5cu&^Jw!LK}`-~d<8L;to;Xu<%aEF znM9|m8mhM|#xoPJ6Ui9B&ef%luh7~wIbe(3qt*MqfgGm2FdrmUC z!1^TGY~L=b0fEHAZxP+XvdL*;ivn8nF4e)#s#zM7I+MjRyJsPba||gv5;LrCyC2bG zvZxn$sz-wP325_phsvmquO@RvE=8p<^(Ad2I3pKYqn_nWNy%&tixzxvHZwDh_nV|( zQWv#rM!F&;qm`Z_1JmChLGWDpeYjiSG1HVzwPvl2Q*@a^msk)I!8V*iF%pel)tw6P zdgFP#J%?!FzNRQ2?UrhjoEs$#g9OqR`d3;@1?LML+mc_KCPQe0DCr$(@Pi1Z}8GAL!oEn8U0c@*SRAz@G8&y{*o*&f7k!1NE^QW7~7z z9bJsatQY>}c@tqccOR0kw&}Q<0u;vpo~P!34^a>hD9Qfe=fVd*xA`(9))CgfmL46K z5BEi>q&xv{=J<)Fb1Hb$Q(#Ik0Scjy%^yP{bgN`~Zk|=3-hBqfC6$mbo-{-IJzZ+)8c*q@bao8zhQZ3d}NNY}$Dp(u5{V(d@V z=o`-gHXp}z*Ffwcg^N_DaQC;zDJ;5zvIOiSN;a(wZ1e7!(c|32^q-ISC*f+u1I3xr5rcQ;^FyJuGwQB}8M_j_Ag1let?61fPVE#w^!>h$^-a?!9& zTyB~}8EG36$K=@L^-5JR1RyesB&jG_UZP8rJz*Ks7t`HS+Qs@dwQ!Ro46 zOu5I;Ah`y_92of5bJE|%GKvonD{~31xZKw@Nwf@O`WT_G1ylicz?nhRX#c+S;hn@7 zy7H`2D39pM@AJJwi>d*O7ud+#I9{Xuqb_6)Vqp&_)*8Ywk;s(bJrT?xz0s_Xf``Q= zCHIBZ*vxD0uX|nifU+Wl77#$6v+&{CTSb`je}*x0`fVC9lvkB zU(>s*R0o!ar4Pso5$>4pKugV7uMdxn?CsF{?_v5$q4*@mq_1CNzXg?yZY}6`dDM>& zOYJMBLDvtR2`-vT-{8zYe^&q&oNA(%yU<@S=WF1*fxoJ;b~rT9@n_`6JG~u#*5=dq ze>2G7V1CA3FQoG6UwJOgnM;1h+^`wZ1-JHS_;eTaZ0^;?6UP~ptr|}8*_Uh>j35e4 z-s1iz?+FY7N=;-WRO((e8#>Kd9yz9MKL-jZETd=5HV2{A^`Py6)8*1Vpo&3B^p^fQ z7Im9k2EfAVL`Urx_SnzuX?SI_1%@|?GtYAFdf|_$Eu$B*-Fs2Kz4WOwn7}D=0+twv z&dG5eA|aasp)pG=51F-fj8&8)-aAYrMv=#q=-IvhAjohohb~V>AErw7Bm585lrzjW z!UIa$>YqYk)}GcL_8tfJ9OF*R*Ox;rzU~Qp8moU}N{SSOP|Coicw;a&gSR8(ezza! z6X>yHacQ?WVLj_8IcWXgi+q05wA#$cmT)(|ZrVQK)U~b;oY;D8kZ8rA&v!DVi^#Fl z1r8MNYm8Ag7t6Z{>eA0`eA%El3o`u@6l{#c!lALkrKF8AId^>X+wFa(YxlRAmxd1K zD-Aft&792>9-Xl#MaZ4{KAdTUt^|kgR=gyK1(iUr~+LC;D!u>e{(KKfub|8`MwH9P4QcxQ-g5!G2|Ue#Y& zB1P>`KhpYq)hTMYR2}}#`PUXEZfwnN@>>&Q-_;YLu2I)uft_AiB+e3R?*`Y!n=m8P z1y}WdYXP8W?B05ZRO_)Bs2GTnJns>}UJP;`pJ)wpTwSb{h~Ji6&T$-%f!hp0a)sCi z{D-eMlu-}AzJ$;ys{*U)c>YZ6FI)fAX$R}DI00i$e1F1%QNe;9u>=pkb>~0ks_|7q zDS&=CBX4Db$+SG#772p)j?7v-X`B&|aOo3NGWEz0c&}H7ygzTMYn&%8wmdAs@=AsX z*}#Ky^Y$9|Rcoi%Te?fE|190d+ILHrIp2C? zd$P2mKCi}|{v-ex9Iajzo(*{*Z>~p6MCF6NT?52&M(>XMByA+2+{^;r8orb)ra+(o z=kq^+0m`Z%wz9LbBmGhoa9pxq#(etvCXwaQEZmA5GP9$tH=_`#&{jfe&gKdi8dg%H*U+WJhl`3 zkeg#<9voLl^stss30NA_$NV6xoXAaQ)Dt_kReBEB{_C=wOe_HJE7-~2rFym)(h7?F zTzHS2eJ0^_qhZ^)wvyK5$H@gp=$|R!zOkpP79HtHBZz4D);zaO+pF5oy8dvfwz9sD zr}TjF6dAb*SuCvS^H%cu`fWj&;w?verISmG6bcS`>!ELfjz3I_XWU2WJGYjh=7|qx9R}4FA3z4>*L|WI> z_m%WDulxtl82R31M4~#+4QuU9YbRL_06?L94ss*8X>>|f8ntQN&-AO`SGEOv|8RIH z)3c^e;U%`f{qlgj+IUv#`bVBW`X=fKHcz{o6mROtv_vpVfLp6c)u=F0$kktZ zeu97kFfgjxz@b)%4!TZId$M;D>C*wN%3sO%-yeGZ*J8l>C$-02FazhY03enx0q_qY zst?&~>0234mKDcpaeR1lgGWE}RY7)+TJ6PlIRri%6Zj!lti)RHoqP|;%&Zp(^RdA1 z96}uG>1n-x=&*{`)gKBe4v;6}BOIlRx9q=C0C{t>xkv%%-}y8$+GR0WAcf+=8m_SH zL2STpfwTkH%hmm=+i~M(=#X{t(HX^Q!e;aBqCdzqQ}X{cd?prs?#3YQ5^8#djBVwy z8ALK6cu@7DWN;^N=E-}P(!JRorW)NznptP}L}oDyfEzhAz~4553iZ|njfj=^o7#T( ztGNmD^1S9Y!*!#Zqd5xBdw>b-JzZ9irpX}V`Zn)iQl0?w&%k;Wi^w^%-mJqW`2M9*8I}( z=~;jbmJ}@?TLTb3I!P@5{C?tLsGsY#a>WJ}SU?al-iw3m65%vYtVE2yR@3J`A8q%7 zcE^N(eGSRnTYhTbxz@0A)n$3~>8iQhG+o<2S^Df|)J=M{+`$5%M1vf&?0+Wv>)3XP zIlGGpm~C!1R_dybrq#;v=K=5xX3do37pi?-IE9vlgN##a51?hOu90o}! z5Q0Sj7!3XVjt-6KF=ff{YF26xfEe%O23XIAc4ddYBG1RqLuyn~i&L}i^a}C{1oa{B zO`;WbV8>k@qFVq-W1ZeJ_-j?){zD({y8tTEG4cQ`Dp5d30L_Xg)q;cDX^5RY2|?Ip z!G7=k;rY!T8S9}E85GC=YL7+FbSROniJxX*i3NFOUq7Rw_+&F@G~iTFfIfgu=wE<| z2#16OG3S#^tLUv%ImDmHe~fp}5pLO#^hX)-LlPu{s~?Bx;yNpX88;a>K>>U}xe1P! z7qGKMyFCMrUN=057&I;(w(IlXh=61n1x(XgYp2c98O~b$=gH_id0z7eXcq?yvA}8G zE`CY&&*x&zR=k^E#~h8s)Og!A)}aTJS+g*Y@P{3`xa$I9>{#uZqzD3+kklQAEr+b#ZOn8MWC2Bdie4$ zlKfhi#~ zzv8{_*;b4Kp3JxnEHlBetXgf=nye)n-IwwS8&yZ)mga)#W+8W=}ZRX8>_8F`skp%Cjk!@ONMLgy>&|j;XSjS@Zuc?Kr6E@@5dZ@L!{u z(IU}$0${Y2eHs2<;-~zvzON#X3V$GSFoHaXVN5Uo5NL(CigsRXztd{STw3yqTvzR; zJI?PbIIz36iU3SH_l}+`GlRZ$va-f-N-QDqy?Q9A1+BO|e ztx(TY*Y|WF{`xj_1(W2CSh*wT+EY7+!)K|`D(x5`AyterDnVuAl z6|bLtjB;hOV-r!cQ|(<0D<&VJJ~|LR9EAqtX$U0d`dJY_0Rz$=r)N~kVwXDgr{M$o zXLvoqtMd1^TbB#3QWe!;?6C3BW}0mDIFUoY{){7rb;GlII5z9V(E5@DaxTTrnVn2vA}eT;(tJjfJBbU#k&6>7z%*`uq+ z34XT-V*1-c!)-7TaS)Bh*G^D8tZ)OoR|I0 z?nozP@kZr{jYtnuB0~~^T|lArn+0Ali&%zv_k(ALvd`(!{LjNrDQqV*%81R(5Q6(H zDLhuw&6RMLm?U36x*+n?KpNzAq0Kz_-zW?*t8)#4(VZ;OB+9L8MgWjl?8 zs=N3$?}Xr+vM?9hccpj+kl-D|3kVfcpYGO5(sTI{)& z?BCldbIiJAP8;6gD)K5Um7zG#iOnKW--9!vx+ZkvtTMZ`Oux0vXU3NelDG`|uC#G0 zqLbmH9>VErnt40TlAu*dJ{#QdeRCvw?B}P>@*N4h6w8Uv$f6V`&-cOx3Aopkuuwnd zKF0jyz($Ft|G}&h{=+w^m!c%ts6IX)zJ6_m!%5aRsHwDw3>2{tPRIB0!NWmrg&Qh; zOcFcrZQp~?*oh#$ncVm#Bf4T2yu1M1Btl5v>hEAVI8A(WBVHaXRqA}+?TOZXj>yi`S$|xBoSim$OGr`i zf(Y*h!B5j0QR{Jk^fU8~--V3uD$iM0PUk>T-RlJHN@-#$(e29=!K!Km5`@f=8pqts(167CoV6jk?8 zJ%`w1GDhFPwcI*?W9S$FG_j>ncaQ}dsVSxvJ|}nPRH1x z!9oSx%j=KNI$4olS?5H2c;5aiFxe2u=vP1}5JRgn1cc@1yLfRH?ndDlzinItWi(38 zZNG{73E^WhM;vdyL$3V}Uly?-iyuHQh+E|iYrR-~wXv+azuG7iM74ruFn@>zotf2W zaM(R&MWZ!x`{xiXHOSW$O}xwr-A{t?$FzaNy12jhP}v68-q?Q4(&G%Fwy~cCwwRX! z_;d7~{tKc8j3KNu2FUm$cmba>2+{}E29(QOdGtuxzQi=aBZw#szk6Vt{9e}MX~gH5 z-0icaJVi6Zxi3OzQ_S2NSmXMyWrDQna$#h7ryNE&=;r2_I=5Q4o8$Yg-Dvo_i`Oo_ z9swha0iJ!|I%_w$VyHJMSBgHz-9A}MPTf4cu33BWeC=Mheag3)FQ)+}q)1-lH*D{J zNh;a7*6tJC6Pa>EVZPPAwamU%q$nA63_0f8ufcA3N1;&kk;E`(?)9wRz5UoK?A=C$ zgwu~+D%d_0g55Xkb8)PC-5HUBA?Jp6z$5fK`*X29li+&_rCN2+0!eOT5H(ul zZ_9!`@DIcuDy1S*A1CpIty!G$d_c&kqa zNmNe&cD<|iBXw4HVn62Gd7o6uT9&q35j)%cmY4 zTo&}8+R`ic%VA*R-I=DK67tt*avP-BfsCf^R+5wNW z&a8jgcKl-C>o`#EGuKJAhYa_?APmBNv{-#5Dhr>zo{4Dz5uL&oRN)2}J)lTuh011u zdRj=uRX-_o$2|KN0`7iDw9Rp6hP&DJ#stE)jQD}EFTA;z7c#s)_LkXiBr zfh;{I*Jwfd3#s?nuD!bW4M#KmZGYhs!kth!K{dQS0-HWJd~+a7T*soK%B5j6GrYNz zKLi6NA>6k$^+DaPZPN)1$OK!U>-eutLGg%_ulsbMiRFgs#U(?aHT z0M2&qVE9#{1PU;<)|(>o6AK;!{u4aQsij4#y|@)sVG+LigcocgI@{i#3w$Ub@f&7i zJFLcw#%=pi)B)8`8$(h=?;}*LK&-)r1b6g&js~t=z09ub%HUk8)nL2lf7{E=g;gA(!@8#Rf ze5rL+^vAZfYmZuFUM`BnRvZ}h*_cT(YD{XVvt*2b33X&$$in%Q-WdEBpPJp_Q@N6z zaktIz70NJmGG7g$?cx(57q8_HCqsuouLGFak!DM-L8kGm0A+cLrgu`}I|WD$SCRVb z0iOR_cM*%yjbcu7DOx8<2gIAsJ*WFm^N((f9yJ5RRx`!EO9afs`N2KQgRPw}w>lSL zYhd)!5Jl*=-z7jdBg&%12C7^9r&4%Av<=cTjC4Djv}|j*VfQ~vd`qnoerfg+FteF> zihpZ_ag^_HpkeMem8#XnLox3iVPhG2A9m{0|51$Z<^8GDVfIl zv$N3%EnjH9+9&+2GYO-EYHEl!BY9t7Gl{UAelF{hq5 z5a+|E4`@GZ-TMEY!Wc={+6!a5#rBlD{9}?6yS@|>`J{vf3%HMVc{ey$E@yqjnr!W( zodxw9jd-_s79;FW>@>b5MGMGITQp_mRQ#MUYBfTqoR2)I5kx#9!-q)x1l3*bg^{k7 z_U3jj1H=6ShCTo0ZEf36E-12a+<1;_AM%gnGVh!6{>p>|XP}LoAD?6_`}X;Q^C5LK zD^(Z|bi}%7$u*$r5s(uYLDQz=V^miHp6pxf-B6EMNJ_KX*7R_@ zc{OjjDErDP=XXk=fKuk4xvo?}mTMh!Su&bf0%m-Yp*OugbWwx}r=Xe~#=o3m>3-xK zO1yc1%#ASYrLb)?nP(PVo^2>W2*Q z!;B(Bt7NCIf%RZgsml(`!cpyF+rehbOR>G3H>_T(@6Cbg;A{F8?}Rpetds~cAIs(! zkoRZTPR)RpJLO`XD#fIT4+!s)@%rZ`e0N_^S$`3KGF%0FoXh_WsES)H@jzh&<~zcF z#KoO2cQN6y>}i7ARd1J^ zj{6Pb!0F}kN<$SmFRkJA_>a;jyscYaACJ%{*a#&@^hJoz_lwApaYq9|`Wes@!v!Q3 zM6U0hZSO@wTL1z(AAH6_&3Q$c^BrdGAe!3|qmGH5|glQ>c6iUtu#okZ>4Yw8L^D7XgHncYXOt#@} z%8Y;cTPM-l+x-9(QB(pwea5Qp%a;ZkNNlRHEJR_}H}f8@-1r}(UPz{`t=)Ki0$XUH zIi#njhl7)lkPwp60Tmk*I~>O+dE1ITKYQh%`KjGy-30bgaJAXQu^D3~V+oZ2*{kPA zD?C(B4>tF_(_a?6QrS|Gl9f0mAOzc90-%6g-uYq+YpMhinSh|)=(XjPMu{;?cFueZ zQUDN?3~X(}SOvQNw1?oXnNQ<_tPUBbidjO`Sb9PMe=DbkkrjsJt7qJ;OHu~tjdZ`K z;C+zBET1XLRQjK`pi4Zr{S$FX$sll@5Wmuk-QH*hA|bCvBse3ZMRHDVfK!Bl2qNgc z=emDLB}91Le?jHsu^~`!l=(G)2>Ye~N6b86L*NeyeOAXW!MX2>MlJK?kIS;`U5L31 z&H*Jt?E#e!4^+45GVf)s z1w=+{oxLEWAi-H3%!onesI@E;&DPLPvPMbX_~yCsKV*BIoc3Tsx3#tP_sGcS)fD%g z=AK4yz~38iWO(_vZ-111fhIE70|GkjcFNQ7sKSTf6oN|gt!#Jo_4COy15hafZLL|n zl%s%~a5RVv;3J^T*iH`z2+E6REw*xffo9+FV)&iIx#@n|ssG5>yUSBHZ$jXuL0c(5 zP9Z=o&2S4%PZ%Q^;T&)67`nG9(`;Si34{g6Su5rpVC#Qns@FHNS@&;?385B)V=^TP z2nft1dfEyG`G;V4lGyS6K~T4!o04I7Gv-PpYZONsM+GD}sMIIO7JoDU&_c@syOhDF z;P4-qbop>m>~TZTmoN8eNLuoO2b3UNcOVw$4FZX~B- zAO;1`q*>P_|9*?G?7NhgSN&=|BR#DB6)ZIZXz!cn=zn+4Jm&b-T#D)I=l6t~`kvV5 zu`zYIL{51Ra77%PPklWv5;F1z{r+ccB0guvZAy<_^P1qYuIVMS?~~Dvw{rz0k3XH; zOljF(TV0ufvp!_#6dy7KsV_9Oo+5pCK5fChf|)3*B6U@8r2hoS_i+R%1j0|i=A=(w zwnE}V+19E3W6j=nD1?LjKz%MNwh%A&(3)I6@tU(>Izgw5+q>+{XopVUKLXn5!iU+ROAGLD;y}?tyE#iPbhd}X&MCWTC;G?4*-T6Q1Q&KeUIZjt*VI< zS=F0)w&dM@Y&q56R{WiE0`~#n;suvq$@p-x=0qU?mr6Kn zErrPqHHvkNLoSp?E_BQV@3yLX3;cc(VwuFO2paiqBkV%ta_S8!s z0C{mxXlw|~!?K zvIxI-LR@lealApCZeJ@rK4Ee7e%Nu1i*&W}rd>D@^Yq`m2*LQ{m`!cR%8lIj&T|!p zKYH##m0G-b9#Zn8+#zGp#ki?>-cLGD9?P;NnJOvkK4j+7>!j#=3ED5_X(<3jv0)WN z;JPf0F3p2oh)deEvpA{z$M>!GW`xy>qQA52vxjMal9116RwVt$(%3=)j@norIMvdk zpcAn&+QkJ&<_`SWo_fcsOUK$2!=*>IsAJ#ky+|3j}(SPm*9x|{u51cie3{|j} z{Q&jYj+6%H#Pzv8N&aJRmc#T@wKKmmuAZm{m)oF47UkH^N7H-+Hrq zoz>h{ZBp8jC>_?OqZ}YD{9UP?FrYc86T}qo6b^>jkd0iyHL%bptKT}=N(|7v5~zg( z40M*JNqO)!Jenw)loU2a;y8*2xV1nvY;X?|RhT+?M`sx+RO1PyFxhj0iMWY3Km*CO$wP*V zlfs=gtAmGnGmnM{6%mhf1#v(CBc$))7T5@zXtfDSxli?A1*?(me@SRu$`zK@mOC3O z>q}u_xch_W&$UEFMdgyX!x;{UF3Vn#2S=c|~lXbh?9E zayRX4`Gp&VX2EU3dG4n>Vcei9a5Dh}NH&BQxd5>-0q{d&_SKn3UX@+_drmBtp9}eH zv5c)o2lKv0Sc{7E`!CQyvvCdl@y5IfJL_9-3D8K^&+5NPTqjO^yJj}3jQtSWIY2Oq zK#2tKnlObhG2%z18J!SNPExk8^#D{GXC!dik^BpIPmsXw>4qG4`HX8*??y>tOru}{ zPTmB!M2$#)(P;nARb?C=pgyi{zZ{fvE9Trh&7M-ok`0d?u}B>STQc_(ljZ_a$#3c) z{!p}7lW%ru#_N8_sFiyUa|KgL<|QgT(HP>>-=LJD^ne1v1*I3>6mf4oP7$WIIs;hF zD%)R4;j$S7+?}4Dp8KNynBs4@t4?K(Aj(#IJXQgWw?h7a1HX5`iuwMd^`%A@7pGC7 z)d>3n*fj}x!=PRVs&HiGaL7mUVOp%d8%a=SmF}zc#%KbC0v`37^e~dBIqJnqDxg9g zSCNO}joSPITzfnhxocFchA`BB{j4q4jxYSq`XDnqzI89cMnFVH_W{>yrKB5z#HJ`& zRO2^}#6GUYTZt=jvSde9@kz#orwLky<*COnY7;Nm{&oJ0CKYVx&2dB!Zwh&@u3&H;Voo<(ZYwXLqhydjAbQ%FA zPukl8C}hk=neC46@)UNQr+q?Z6I%LtXL4;r-CqA-ZSa5oxNyF*|EU6hMN?E zu-9^MzYER90oHmywPHTCR3m-w6paP}SU+akHqwZ2*w|1?T8O_LnDy+^P+qxIUD1KcJvE&`{jCXpMd&pP{;{@MdJDYO^@7xZqp*YmJ9^NaA9F4<6_%KUB|q zLeYdQBH~sTe|WhlLLqY>o7-Xj*n_`yNzcZ0;ODA>sh~@*L$R1aYrm#xTro2y=mz8x$dH{#155sEGeYb-HE_NHfwoK4Ba%iig zuP~7bksn=X?5KA=>0c?k7e*o-81`kn$4Q}@h0*JW&c$(7%G2x9v)hYUuDWTrPKFk< z@aSk;PwAl=&AE*>4)O#+jLV6ScJS`2@ZkO7C8@BY%MVA*Ym&(aOdi2;8*X`w!m3tF zj&EU$E!FNJJDer#EC;vXC_v1C0&eD4qz|~_B*R=sW`8dr^O-E-IULt$xc42#B(8>e z&dv@U5F}zpD(!bzFWJHDExY1N$;arN6b4V44zo?=oSs*Mw}d-sf8DVCn)D4zsNjX! zZmhmn_|yJmzVY4PX`<)1%gcle%bB-_{Mv=>wJI79`L_N_V7kdm zT+DQptb(T_VrfHafn3^2#(Tr#bkn8@t=lbMpVU`GfTHmquxTkyI!R7e$u!ru&u;oGvxYwFice%+#bHM1 ze>1+6jSk(tdG?&iM7}WM+84A_DP+mtg1*eLnIG3VA*+2v)m)CM!!MRWK|9%WU;g6* zqp5DRx6hO}oiA(A`kKdJ)QTFlCE8bSm<~wP%u}x76e3a!j2jAN^j*C1-WQoH(@oy? zfDX+6auw%D?YG*?);!EeonJJhB-#2ua(liu>^ zOnj)*EEyOIjJ?A*pV2F^O~$EGNB2jE!`Kk<3sPFs+yWVN(_b$-@1DNM%6&E>!aDCH zqvSWt7@_Qy8!5*vE3LK89Tc@&6=}sy0gLE2X|j-EPTb@T3{=%ONvjWJxz?mybk;5} zl%qd*ub+G9l+2;w?~>U5d6P{QcxFUt^{SN+sgJ!%BCUEwWL)p$*kvWW)(@^p75}C zs05v6?O;jMF?b!PN|dKW%}B`TQ!I1FW;Zhz@f~9aqnBF75`>Lh)?L$2kAq*ygkI$k zh59w>HZp2@%;^DGAXSQwO;)|%^*kK!Mytd0S+gJ01UCeO>!D9~W~v-;ZQqg&3yts5 zhS^`L6=gDWjUPwg6gGQtwzoU%-Wk<_g75{)4dK&c_l+5+ z#zGH82fwX4FH(V5W=A2tgy>8gz8tsb`9u40D zoV+@2)p6g}JXhE}{iGPXIbYV)ctowe{h5or^i{P2JGm`!)@}p}3GAG%BZfMkT}WcI zm1C5(D6*fSe{{O&P_B`%KyP%QGI?V*Gd$ilA*%y+not@+7db@uY_cFbZX>=|;Feui zb@PF{Vw=Bp#3`&GZnv;4 zAD6dy64YCYvd*Nd`ny>P^RFU*UVHy6i%nMfQ|WbU>(yxUC}}6z)hgW6S@^`JBGW6` z`lyh;&?DIMeTDwCnP~J1kEyUgiS%}9r%>~xYyVMgQ?Z6RO~2a@&uhIsGkM#dsn`A9 z9oO|guWhs9!kX?jmf7Oo6zA|G<+S}==jz?F{AMQ!>Iyywy;%wxibar-mchJKpl z5KLUM4A^V6xZX<%ZK()#k&9GmIz5$JJ^wlw;(%5o+11GER_5w_(6h1?Ny^(l{~&~4b?SUI)wV#bz|^L0IcA1=4HwsN zq*<*=l-M#hI9!C423yn6h|2ZjSL!S+0`6}CGPT3~SaDy4GQSd~L98E)9sy2(B zeNNOv=?D=@7HkgO2jZc%)0Qz7_*hNBVOVOeHx|<21_cCFBk5kz%}0U-uh?D`>eGsE zYiDuy9~^HTM?C3kd~B_q@2@r0vd*FvRnZ;#nxnO`?n=Ib*R<+^d|Go^L2yW!g?3Jn ziqR_$^S%btRyTct&7;ck@dgREb=3^Tf(+s-bLgvQa6g#4J-cklxm&=IM_h)0|NLC3E#vV=@sU ztdc7)KS-{;Hfm|_>H=5U>YY>$D>&SQP{F+!n98gkr0U`~)JUpnz1@3p&3b)MfTb$Uj-cJ%su9b{jhwtFG`Tal9Fo+H^=b=N1j za7wNz`y&N5ESCylE?XDPpzx!<$TW2QW5f9uz}G%ow<%O$*yRKabp zL?TpkUUsA^6>h46X81pXT9I6$iUt?JJZe<1=|{jdQR42o&BFcm!|mwPVgXk$7`nwRJE9s*TsbZicXat>{SToZ>RNpEXXNvqn!l5kDr=M!{ylQmv9w7SQf zQCskp71flPEv6AmKT1*H8Q2J_^72ikVbaY(fP>>+GFyU&vqQ3ool;PA^60f$Yxr^3 z@Ao8i--yy=rk6jlrVrgxBQ!E(cU)IuGjDr(=6lF{-b}(Iv;- znOJTwvm}JWdA-u~p!J>wPjgnRT7uJ>1UC4D+~|Ydh;V;U35*P(-sQXaJjl6-MSR0B zqgS@A0+Ws?m#r`{F)?Phnc-$*GD=ui&hY0QU(=b_$zwp%$RCew`iW6t^IS%+rSG%{ zV>N&KtXj_?9#$X+J_Fofu*73HxUWyMy0Y$i?%pcn-fF7ATuVW}E+-SGJhj)CR3*trm1A@@Y+u!A_KqjPbuy@=qr-q zW#x%m_AuLtKG}^4t#zESp4LSKjcuG&E9cN}?f4dzW)HQn0_K7DQ!0&FDkTl{-qGuv z>2>G(v76R%S{QjwmU(X#3(n3y@$d#XqrH{Q&?|m#ADu{Em9IOeOab133mO2QnZUz^ zk(`(0F!0Cv+0>gk-Sc8sgMBnp@gEPQRh*@(pVAqN0K8jX?*6_i;5^NVx5NFXTnj9+ zEMjOVc&EN^qr|&PMv6PY=u>Xh1z`r3Ucp3n!+`>cEBGt{ZE00-aNkrms7Ho%i+0QV zE*Ven`lq?$n3I)8IRdYGUWQ}8`tri8;Be=JimTct`mx9FwWDDH>x=D<7oe4JV!y%o z!Ce-5YPmN}ZL1|Z8N}Sa@9JWh`vLnUmFiqwe_-m}X7Qd35#$ou!@ zlFuJxG1hQC;+&7`J-rN$E^g`>zj8N>iivemdIvh{@0{d*vmZDvYE3>wRU;L$A;mCD zUoIofTFy81KC5hqlvXCyx#A+PSxq88iriMMx@dCz+3;F@N%eMsMoCKILx__gKy&-g zchCK_!SYn+X@wFd-K$l4!95&hYRZ=zQ)&qu1y{urD(2r~hkRHnGue%9>`E=+CO#bFq7;UF;Y1B5)}zNRg8<-8N3;0D5*`U z#pJuW6kJ>?f%D9kQxs@(qGQz0SNV^lM!{W_jdI|>h4H}e1usr< z-p*Vf_N=mLTJ55yl`Uy9b?PE}6oCPrvV{yy1UtJbOWNzRj1ofqO5XLSbezszw^dej zGfuIi>Eg}Bb$&Ep6~mRH=xl*^edJMAHsk}Zj)?qPlfioONmizmy?t>c)FsNFpyke8 z860jg=9mn%Fo5@w78>{p9%ArNW1)vI-Z!XS(BuE&habLf!g2FQorlloClS5Ky!=m!^hpl5u2*vzTy%=68ZE?z)Dj3(AW&P9@oi+`B zIwvz(_airc3nB04jzHgNU+{YE@OFsi(0BS@EoM%}X9F(AL$C4=qEBZ=ZpHmFa(W8q z7C8Hoc*4JglMb_DA2Tpm|GF=6y@D?@@5AUFTNH%8>n%#dI%^%XeavSV_;Z27rh-wI zl=zok+jE$bt8d4UgL~c_^R9NrJ-%kT!dPaE{Dp+uQ>pl9QsbQ#nQ8^6 z8V)wogrBiOy-6_9ldZ=#6>_(nu;2vExu>!HgSalv@ATMeup(l$qL}(w; zuixFG(Row<&P!ehB!Mi`gQ%$Wxr~WGey?Zx>V+fqY{_Qg$NnrcxLk)vOSAT zb`)FtpzW^4nq7%Wj2$DUavCmx^I!N=MiB1)fAnD zbf2!zb5Z&Gp-}df_6$w+4`HnKi~(eA(nGSI=sOfYV~lbH7E$YIc}Ym5In2?vZ;l|s zCckvhZsTRQ1MJ(UimNH|(-xZOn0r)x1*uZRnm@V^+9J9sKQy^_>&^CA$E2<n2twCMbvv?dbYs8MixzB24tBE_oryu9%_$Xpl_G3$pN071U zu1t*WjC)rc8t;2J3ihXhsR^P8u%OgrY zU;~~}3Zn?Md-n97c!+%u0v&e`Wgk&X@GZ6#b%%h1)JipMt}}rq6b~aQ8Vn^uk9eP#Z9H zb{PuY^yhi+-TiEgyAn$A;1PfTWiROA^Gp~52GHZ5AENBcG6dICyDke|*YBU+y)d7M zumSwa+9AWdTDz|8WbeDcuX_I^?w=RJrNcoP2zpRFxcgktxBpLm_*v(!Z70u|_44&9 z7Cb4{jo{(Fhx>C-fEFQX4>YR}UuuoBf`8~_g!|JQ)D&s0#$=jbJgU)r+H;x}QHl_r zl`9nirboXvRhwo|)<=;V&6vUv^w<&?A!~J0F~4}87sIRF*M8~IfilyIgwpi%As)@A zFl;5c7X_0r^64^4mRH}$S1on-!VR#? z3t@P~%&*s*y)@o-lK!9Sz5}SKt>4#kJoW>61VlhYrAb$+bQMKHl~6-ffgoLa4~im( z-V>=&X;MS40mMQtN{0YJ=`|8cAO+rv_|3iFyZ6q0@6DU{&D%3NOp?9!+H3#I@Avz! zwKhcX$MPSK#?Ws&yrS<-$kQjVnLj922-Yv4vOHIwlf%;HN zgC1Xim>yJcOR+f+IHYyej5@z(Y*8}k%!ed#jUn~!B?xC~eY&*lk-h;gn|z}L?ZpLz zQlNqgA9H+(tG_}1|9c*2rACb?9?`ny$jd3`+wzm9KDOoGfGd$21% zdn_5Gx|M0`X--Ta%_XmE>CZfKraT*!>VEjdxnu$>#4}%`$zdL$6qSBC;XHr(C`b)D zjY*}3OXa9p!kZtv`_{yW;8IZu_qZ;y2sHN`w7<@IgL;F1xUdU#wo=P%UOgx0Q`5m6^l#R5F#sp4{!M>)u+#|g!n59p*GP_u0 z0xobug9+ZDuMw|JSOJ4Vjna&a^E>t$!H*EryAbQtzMm?Vkj$O_#Uej_PP#YVU=;o> zWhp;Bg&_V?Vg*=#>4Cucee0dF0iKbtSl`1G^b(uMW4B=}+uK)z##W|gmx+zr%FbIP zhe_DL8?H1t2vx%}*hSs(1>z(UDsaW5I_UU04AR7s7h}shD$rZE&&?)hZjtU`H*svu zA!jdyDD9G@PBLr?lD$zK@sGIx4I>NXUvsEl5ng@wPUTDP=XtjUNZo@kKG_wyKzO=M z8n($e&$qD|Rpw1{HM&nMeeJkJWKiwCK zNj*uLc+zB0u^=rSU(OT_cS~<&bbK$8bs8dzuIufqYr=kZ z;FSmPL4zxaP0C=d{~57&aZS4Oi)-Ig+t~@XXDg?wYt2#ZJqdl3w&h9H6xmO-fN*6! zi^@L)Hmpdn6bn9=LDlxt?};RAQ+wpT;an3|NOs_oain5hm7_Awk0(x@OKRTzB2 zNLOAC-7Jx^6OgK%sEY=_nqm(fqVpzj=su;7J^eUyWVZt;NK2PVSGlcw(imJ;FvxCqB|OU9}9S9jb*4 z4^F&mIEEM^UYa@4R}t1C%PWhFEkQx2-EVY++}!r| zK@WeKKJz=W!E8=9oe)@P146)=@RJZQ`XW_AgzL|}Ba0aGrH&|^hcS2U-a&?Ulqc>_ zpIQE1e-~+f3JU0vKZJDGYv#T5z46Z_;EoJFNW}GO2WZBz4?v!zshTt@l|8jH>GeEo zH>=uCdJcBpgXd-G=c7bNm#}h*3 z#zgAxQg*Lqq$7h3nuMh97|J~_p_(ULPC`O}DsVkICbX?Bo*0p}$(-jWRShGa%+?x( z-cHkRnp@*C@-N6r3nxhx^0Mf!3$_xs;&%IQ9N(&^i?+1lp4bmIfVn-4&^Hzy)J%dV zOu$Dg8t?eV-|jF1vkgC9g*Lr;)#lbx$+FCQ#M(SyP&H5Z((?CZiR`y$^9()nOy1&z zM{A*m8Hs6^1|i<5*@c=WNtZvjsNFmA8gaN%1lI1kuJUjKo>!xvpDM~LH;EiiF)bAF zT~`km^W^yS7rXDfv+FqNeEH#B)t>=ci~BSBvKTMKMB1#UTyl-yWp(a>^{?>UqImh= zhv#Zb9?D3i8(E~|`yh0=U?zR!mRCo)bGaIH!Nb?H@42qTmRMr7uLExxb&x%UWyrR4$^(a=95 zmCqSTF7j=}V&6WnUUW^sCP!p(z3lLwpf)&_bWL3s*P+7po+<$bw__nky679IoyfU6 zbx}zp!P>+7GtG!kQN&UtUpi({0OK2Iw|o0G9g| zdYgh!LE7Hcu$R#ZM-_w!Del&mHQlRqnqC=+llLsuzf`ASzP@8{G{b>r?y&H%NO^AJ5Fw0|5Pad&r-TxyJ?OA)Xv-JJQu& zheIFIznJ=Q2Vm?cQ)COPK@$70z|YT0TK*f5KXri~Ye_K59NiVy9aPrY3 z%hQjHKclEnSAtu)_jnI(N%W^cb0O#dD2SR>o;!4ZOMD1I+HYSWZEyv4cSMZ3`j7dz z6Fz*MTQbY*kr=vKdQyC?2i%&inYGXy+XHT><#D2PB$*0G9 z#felM2j=>SW7NPB185~@byMmi$+dJrUxFoNTBpUMJxP1#J$oR3UaK7xy~olTIK`&C%=}qm9+WQlx2Rjj^&sl7{Z&9bXDt}U+rk$NcDD89O;V?$f3%; zUuBo^kyIrZWxp;-w>)}~jnx24n68crkLYmmn%K^iji1wZ%?QHYFO93Vp1hcAQ{bc= zpH+J76|cy|61ZUkWrs5}xI?CVj|Xy>l04fuFDOk!ir#oFN1ooc6inc)O&P2WD)hng z6WC|1R|z_*EegUP)e4YL9)9lJ09&5BT z`%q4@tE=a)iy1tg5wWyp9I62pc}JeH=qG#oXD0UKu?P@62j7VdI_P_1Y!RON=9Q8* zJd#k?F#V<+=J3(2{`@=kWEVogu$nxVNxhV1M>`7OZfJb|4z0m5u+>n+Gp1h}^ZE|V z*OjRM?xiQ=kC|tAQV4&`2Guc?ii45uojDy^3T1r{j&BKc0Xj@!Qgps~*sDIIAz9CS*aD5TodM*;Z+`cC-BpBCG zfu6oNHrRd?WJN}opStVX-ep5lK{$q7lF;c=ib8ltG^TKuZ<_ClEOAwMk5%fNqsZd} zmOPXBU$!d*X0A-o7{B{y^zj}p6lE*Hk~n#+cndth>*1g+Tm2V2<+;~m zw-juPXBG>=4xLf0!D{B<*<49c)aL9(p^SJ~bS8U+t>!N7+;DW?(;~t}l7Sw8Av6+V$Lj4Yv%`mIkG=fJH z9tEawyfzjTP5OQt>K2R>erVYt68k7JF=a(Mg1Lm;jVfd?q~-8Iu17b$><~k$t7pG3 z7ND!EZ?RH>1G?f>`h$9pCSHE6B1=KHE(IWoyhpMS)pHt+QJSn)q3@Su#KixE(vDu%aDKxqnh^gz+ov@GOD*IT4096D$yP5o7deZu z&5~TI4!$(x;&$7bb0qu2*}Qi=Wm@;U*NMYpZF1nPTr$^|zdLHIL3JU2|%{f*2R$EyEB0_y!=5|Nbpo?9yHK$H$8I=38y5aR_uXLbU&cDa; z-rO?Xg9(N?UZ|;PKxlOE;5}*1mV0C%5<{OZd8b~oJ~2=i$;F0DRF%GJev%TWQ(h?? zkoGm{TVt&G<6MF7-$_X$KaPt7%wc{|aVtii-PT0mKPWw?!*Oo~V z2bj~sd-nU4h*#4~f!QvhBoC|!?*VogQ!xC69pHJ7%P7M0<3bXyIbAYh?S!!oCuHu+ z&T&$H7rrf(xy8P^)8oUMv{SQAlkAaX#Vy8U z%v_srfGHw^>|9Qz9r&H^bKrO5LtS6~Vq?En@y4irlRI^NHl$~HSYFFwTCOjg1ls7{ zn4M)gl8Nvi=W&_4Vn;hhbjYgfcEdeLZnma^_Fv*>);_x<6T z17A@!wog3o!CQ(jCniD01$IqIW0*0~*FCrk7nBeL^i-I0pihVeb?Gt2cl>d^2ay*{ zqpI29T#vVMA1Jn_4QBIg0XlLOc}^G=e>MxukAlZ#5rUA9!0AYE`o8Ivq$FUV^J1uz5!t|U!d!t& zH*(dfBYtT(F*<)$y0c|VsM6kn>GOy%Cr*08={y7vV%Fun&adF~a#kQ?O_)Y1)*76Nbt5^?5?=;|J0Rm8Lt}S`5iRabu zoDVvMEKVwO<6dU;%m+j&VDYvSW3^sd*+8DwHDr@(DbSYa*E*-63LQRF7_ncYFUZhV zx6@mYofeRHS0zt1948Fqc?a<<_B?3BQRi|XnsFi3PPjrt=Kv?@D@5~J1?x5`3x{c< z!KHNyvarEz_~ikfYM>-`3@>$An|m)^pp2=c7vdyCFM^ptVKxrLfww_Y+duwzs#0X# zz-)x4i+@Ds|4>%){3J!ZU3_@zM=Uef?uti|ycW92tBi0XdWm|=LOp)7^NDP)TNiq! z2!F*U8dPt-hgvu8pu14LorIYkT(PZktnW&bxc62%N;Z z+I}m^NJz_Mvx*{IMCt78ijxKkm_MFh!s=O945r-$I_!kJH;MFhPVC|iif>rG4eBlU zuNQLw91ms;Lp$uAo7F)bb!E)mO`^b&RWJli?1n%0VztafB9N z|0E9k>b%{S8VOd%5KHRx6Icdf(B5WN*FDS5NWVJ>jMW5umvCn>nswG3Z@Rt6F(De{ zek!YlHp7X7lE1@98+mI-i1AEWHr(U3i#|nIsnjfy-;DRR{qMku!pZHuZG<%+m-Sow zv}T3PTdeLL6C%#fZ`=meKf#%#_N}Ag%%d!0=Z{>8sCH8Re91a&Vml0joKn&uR;h3^ zfm<0xymEC^8}dZ1D)iDk;&^)RFh-rYjURHw0X z3tS31RmG*9%KntBHBv=oxJ~18G2Lr9GUd|jWpUHKMFN@}9LEbcrRVnmN9*%1$Wf>6 zRux$3e^==Ay8fHV-ccLcWj?j#?>J;rd)V5Bqa`Zr^cSKGAKxX|2y#4ob@{u%Gh{{7Whk|Jpjp|Fx?hz(d!oTFK&{cN#IOQS75bMW#mW4x(l?_wfN6 zQLuWiB5CLGcjMz`y!`wHV4+3bT#ADu6benk>YPbLA_b+Tw`E9}$=HGxRIbnBqdh__|%3)B|0GQgVOKr`DIAs`8{Qc~=Anlcrh!86ymF;v2yZEXG|8PDsZp|n9%lW6Rn8{G{lcBng9E{yo?4mg zu%+?G4gGd{XBqAKf(`kkO4V{JOy&ZVqR_Y@pNv7-R$;dc8{!AUoGi)nxOhCZ?~`MX z#!%QjOo>$dsWdpOF6R?9aTHdaF4d_NWGOS>KX+gLExRv=wBvmmc}o#eH;Xh~IVFaD za#MoZK$?`;r_L*PQ+GGKDX+3MP-GF4iW{-jClbN3;GS{UR?enm=Z&Ctz0l=?($E7@ z_{IWxYp1=DWR2r4j$?J>oVxEvZ0FZ9!1jPO(4Jd^MyZ%aa=yBSSjwO5l5J@|-@~fF zLMN6rOdRqmL~d>e)rI$m0m zqkMX+HNg;9Ved?OoU*Q`)eTFA2j7=imSEcYO4{nyl{z&DK%WZ_uU%W#9G|RGVS%>! ztU~89T1M(2{_*?d&U9P0tu0-+yK`Fca^;5>*~!es&fUtb{uJg7igfb!s~j+C9@eOI zi)mi+T9m!xpl_><99l<(Pg*7~?4h}9IE5UzRi4vglF9M={U+`l8S&BG(z^9Ro1q&P z{yPEM<{~h(qc)+Wy~=*UD<&2_ob$K?JC9HKojdVDeFlQZDo>gpTpsbhc zJ+|aA#+>wNp<6&pvuNOvx!VVm`HjUSNVn!yYtMMxhg@W)!`OR%o=@fXht( zC{nrASBmAWg%GP|)Uwg_y>7%+Voq<8A(p=hVqOhf^Ea5w`zGEyF6=+d>+Aq;`F-(| z>1;YleiUF2OL`qDWgrO|^Tbj@st09spNB=u3!P1gBEo6HPMl)OgX?UkoY{Cb`a;Dq zlOp@%N$2taa{2&8$rqKuN7*~Pwmb~VPIYk=3r;}t95c0mB_}F>4MZ{ycceC%mFZ2` z*3Jne?`tRe?myf`Wq_(^y``Fwk|?3h-Nup<1geA4B3ZJSTC@(twljhOVyf(l&Nc}0 z)foCC6Cvh%XYRR}4WE+8exb8-`JcPSb-T($=X&GqUDNR)!rX;CFH2v=F?D?744AKh zzKk)6Pm~=BMDn59O@Vl><%*sir&#j^@Jnpi>`oNS3F}atru@Z8jaPu~S+YjxqGcNeDHlS-^%{1t;`8P4I^fp#sotSmxzpRXeWyWCBnot_r69`Y`J=4 z7b&Ncdo$uz2L}VYFW85!Ui9_G2Uh#8e`eLv)g27%k3*v|5BL_Q_;ITo z*ajqyZ!(fif;!ioYP9J8$LOo;Ry+`8+Gav~cXy?LfIu|U!PysN8F8wU3^L;n{I`}2 zzBN|k5vFKhZiw_`I9Q;?F8TQWu6yWdF6Yw~VW_ycH(0C!1X^TLnV6Cy2gJ0c1nrM8W}OBC)>?c9O-Xd2+Pk~d!O{NDsH&a!N_xUtnzl|Vm+Zz z;%Da?COKTN4?lb>;mPwROwy-%%TMtQae9wGRE<3UkY_O)Wl_dz_T*XM&bQER`cKt0 zg8VNBXT~UvVZqBC_aOY~o2mO0Vy9Sll92^$@^July>(T2Z#_m4o+j&_gV?9w{dsxg zm<$@hCL2{j`+M~PJz%GVuI-iw5J*@{G-p9$AjJ@2eK3emZf-Yrp0h3*lqX>ICVA@D zTQ~}+ic0%bch7|{&Hl4VMqZ$!W#S*koy^ zF=xD(kdOw*EkZ8eC%kY@^-kX*ePDbm8sZiyjbP6P?@#8XtOp~L{n0B1n_m&I>sACS zSB+=p;=))o_`x88SFe5{;e#+qLPjZX-(Cs1eObzBOn;)zvmAFip{Ve>Rm-bY48Gpm z#eXi@HviW1qa7U`eJ@ThNxIFbO;Z|Ao;*3w6cpsr9DD?E`}XaS;ho-~JuBPZ3@3O` zT3%V1IBh*9XcMvX?O2o7>KMyK+oHkBVGD;U%ZlklJ?+x{JyH{?hBOK0zU#kfuJK$p z6frKjxr(dC=dK^1()W9B;*3rxQ-7%JPTGPUKL+A?H8uzIbdtYj-Xjq6YfhXvF$avh zN=IS+#l(c{yw_}sLvd-0;`WG3hWns_QJ!TJqHgDVgcb~zj1)1=k(u=QI3BRk&647> zzP`>bWuF+dQrha5oSbX|5E_|8a8aUd>eX-T5|1*lTPu+lxVa%9Mh>;baXVeDpg@fq z{5*ZR-RtjP%{sxv_iB3;=bmvespqK*n8hUY@Y{!w*(Qu}>FsoR?``nL!3lHeb)LL; z@63+$;QPrtp$|2)i=#4Mg z3G_O2h~qWn&Mp1GYOJe_T_2Ccld1Q_R>VGyR0n`UBo8kS&*RO7uZVOx&+S)E)SY#{ z>(@&^1cro!kf)xX7L;=S`S`)yCrP5dz(F;Xpw%7m5j-fg5XtZbMxVk)5(UtGUdM^v4pn+n*z#9V2uu)L@2w9?Ll zT`Ml1soyrhaw{Qu`T7sO>lTPz8(fiTbweWo1d&8EdOww7+nr+L=I%}+2OM{mug>sHD-HFb64fPHx^7geV~68JcOTz)rE z?``DhSg5X{!NM3?`Jnmt)#ATM?+>3IVUvJACeF0;U0_@WPD+yVSa^UEvlNBYZ4H?U zt$eGN|NQy$&$z*36C6amW-H#BEXHd5pK*`FQpe%8YodO zZ!PR9d3)7?%XcP@I5#wp4sssCSO&e9ePm@-x$&kt6*%E)l9gg_|Br7Uou*r&c<=VO zlNNK)>=3!#nS?DchKBjO(t%`;Y2S><>*|`C`oIbX>pUG&U%h(w;lu4HPWkQkswWQy zCFwS}G#~^WL8QPYXm9NvdAU>U(l<&Yu$V?%4sH)VihEcjs{OU2vI!L;*eOr%!hn*u+0oIpMr0`$p|P2eIct@8cPR+kSq2 zy98NT*(5*hD0V5q6-6T!{}~7iiBVHqdvbZYv!la$H9DOj?J`*?>prJnx4Zoa7N#bYLb^TWxTi}6`ZItsdnk1%p^qyAK;FPF)gzW>ycX)CM*?TCSyu}4Qk_S5CXv?Ucxs8`EsuTx4kaURa_?| z)pr(RO!0yF^BG+Y3{ozK24Mf0+xY8`W;A>`%Mkx1gQvA__=Pn^H1_UXmVb7hM2qAptXzt?O?c7RqUUh2xw^Kt^z7`cRMkX-?tFf!t1Q) zPu@SKnoUqE2v%fmG?%g{#2()mSovT*z<+y1*Uhag4!ArBWRGwacGG(b0s`&PY!WSj zj^LZx5je}o(9_ij1R_P!F5Vw*0gTUYYiYKlyW5~OnsX4iPDBRBL63^lnsQIom?(iv zw2E(PGV$96YyRrdgo%2iQ>>ze+q+KH6L5f2>{l>)x!QgpoUZ~KiR7Wb92(x6p$P9t z6p)sd9y$tk?U`zR&L<^hpn(*24GjPhp`4ztcx?;mi>`&u>jvobZPKk%r0&~G4t-t97pt5)L6*Yjs)XYBwgtdwSCU@%Zu4__TD7(RlAIczOt#-srAg~l_p zvgR6D_-41b?-q0Mi;DIbn3)yQC?p4v1mLqBNvsuwubH92&z~EZnB)UI5lC6BCT?u# z-Mt$EK7<(_thjg*RZ>xv0fyNL?>o=Q!NjEJkP1?>1Gu&qXhd34vO=4-_noz=gj+ST zM5?>u)bZnAVlS(#tWDIX|91H3!1*)~f;zF3wR+WfU<+?qgbZ&yd-kj{nw5zu(|2R8 zmZGkuCABfvU4QA0N3B_SOiZ=taFAG1c=$<3l7=^U3;3E`I@d2t=^}ZlPL9{QuNK0} zm!wP!bIug;(cK?lmF);exp(A9kdAW1$zOTaJ;=Z{%>eg&~face-Y>hC;ANr9hrM3z|g`Z3`u3VQoFbi&LQf_*hj_Q#0n+0QUM};FQ}eYdL7_CatvM z9BQD#P7mzIX5^2lsWA?uY|rlb91O(p@mm*{cmN(5qftO8#sGKnE4lLXyQeK7mri^3 z0qD4VBbuijkGE8L`R7CFgIQqjrS8Or$}NKD4j?6ufphp*@VnrDnB{_h{b-2T!dPX2 zo10r9Po?w3SpTe9V&`lhcZ|X#Jt#DihaPz|L{I#@(rewzZ5yu_`*U&NpMPDX9EarN zSde8sfFa?YK0Ty^I+&O86PgsN=+yXmiu)+Wcu-zW>>E42}vdNU` zgP?3?C0um3K{H)$unLUyQ17W8h-@zA;Vss0Hzsu>Jb7S*ciDwl;&oCa^VykElW=uG zu2T_;`?d+D_jip==5|f;eY&?CgqpB#ki_tgR=wT6T4?VOlsd8HU!}%3U!g#b;m9p0 z#zP4^1Re;>qzA1--ac<qHTmVPu@sGF`jsm@a{85)4rdI2ru5e zX*VPTlUuS=zdU>jvDd9=x@$bgJB-oal_-%J_3r3>?_B{)+bKdQJ1O$q@9m)LH+^7lhl!AO?E}Mf>NN&VaLZ_@q<%JJ_ z>iis}CtFTw+>|e?lYi}5XmquyJU@Oq4ncWAq8=gppgqjrG51gQC#hLph&vgPmGL#B z_>Novl)Us}puT{4YJSv2J3VgO8ZsfL5%(>lD7!|JY>gRM427ni%P6`VFWwlNVlH;K za?69aZyuH;(XdwY88nv6pOwjhL_~TuY-SCpEHy2q?sW3HhOW z@6Z(p&PziiRb9rv8NPzPSV%>!;9Q8HvF3fX{aw{Mg0zm&mGrhGMr)1IYPE088AY>> zr4<`bJ>2CNU#ij1H&se7*}u6IkeMqK=XD{LO3hkJS2PdX47!)?Ld^1aruVE=ukVc& z)GU#C>D%Cu-H;@_?b|VMRiCPJt$EZvTvhhK`q7_#A!*#Y1+9(-V=zWCnEkV6@;dml zqbIH`T0^m3t@+}l{58x?GBuXDU)TQPw^0-I&g__NwN+CCgzs3ZfA;bR?6h~gVrX{x z-9B@31@kbEndJ_Q#dYXw6b~dlXhD(fQ{|_~14lXZ{#Xg~*#j&7@!KE2)}uW=ed!hSSj(CQG^&Ddj@sTn!KbgK>863s3!3t&ucm$G0G*ul^%Glr=&J?6bTpGI zz?R)E+(RSCdEn-(4KWr)fSQ7ioB@!t(>2ZB$>HRB4`Dv&BX$Gq(wCo=bq9#J_nQ#3 z6%dNv?(Fz*T)c=d=sj3C@?P&a@yn&g-8qg1z(?o0W$==KrFqJ=%(~?P0OnR}k9VI1 zdnyVF2*g!x$J2IqcLgqAZth8w?e_wCtf%rIPVM|2tRVS{NOUVQuQvpdy?7V!ye`0o zS>#fB{04?p3NVAQI?n=#mX-mK4+X@;P?d(++1YE-moHyt2TU6WkNef^cY?Rl#~_mC;prxfhV_9FS(!(f{s`hsgP!_D*GJm!gS-Ota! ze<;=1UxZ-i(6599{Orukw|ua%+(4;Ss|R`0P6u^h^i%!EiR`t2o~49d^lPJ%hnu7s zzT$zvrUkHPSah^kNN8yNhmRl4oyIC$@2aZSnN&G0j+I*7Q#qJMe(1}>j(^=DA8j~1 z2dbwRHGSjci4&zpeZ!Lt{!$M={84Vxi8MLfoR)TBc7C1%VkCL6YU!rco!DVDPGy8y zrfQU%Y_$95zy`pA;Q&wpEYU@?P*eN6&TBOvbOZ+oR|-JD%V1A5BZYsQeixh`u(vx; zw*asy*jMHKU7f(qulE3E+zh9fHwM%KwjBv1jaVMQHh*eo0NF_M@MPsDCuIQukOu&h zkpkb(K|AP8R&H*pC2h%w!Ho+m2!p}Eo>pn{pENWzE5JKnkPM8B#ifBB9v&);2d?tv z-I*Dwv*3XPUSm~B0xpmVFtxcCdb%}623#{fODT{%0;ESAnPStl-k+wYb#5Y-c2sX)IBT zEYHt--?Cg>T!e2e4XzE-RjAd`@_Al|omH10hvbp3uOAtlAWufIRN zq@=_iO#;rV4X{V0ZSO@$AE0pEw&=h)Nr$1rp> zl4nr4r=Ge1`NaL0FF?YNf9&CXrWxpU0q?(A|N86At5@fN0(q;2N-_+>RXG7L)pvxn zck0xs{(OUiG!Qi)eZa^!0eWY5aL9O2?Jz7#7h%=nud4!XP_C=1%Yd*rxE3nn(&;TP z;Fp15yP$X;RA)9YTO8o?_LEp&_|*GnIY4142BKiCoafTa5|EviQBBnByc9MgBM|!B zYR!O-Q2{#PP9zf3#2R-#w0Cs4iAzYN@6jmrOts#VzOBHlEYHkMc=T(T03R+oxd#TQ z8|bm3k5Pacp{XqWT>+d620dU4*pmR4t^yeC8Tt@J-ocGtWtXukr)9sj z+W9WP=(~Vco0y(nR9GnNgsakWpX<5~1W|V&1NNlJdlze4d8~}+av^r!(4PsRcK%|m z*?xn*yvM@pii!$*5;he?$0!;P2*I`)zAc;`-LI~HqzlG_VCUI+Wf`C~#%KK`V$?aA z7YA7R{qmWaHM4f$|UO zL+?{T-XQ^ZZPELJxSH>0qGh4p)Og%ClDq6WXFHars)J9i5$(pzz3!JMzaLS9y6q+l`iG z^9abxo5m>b)yVEQ`T&%W-q+Wc#;27cdB^U+UKpZ*y&%zO0^%YEM?qxdpSD2esqOxa&E)SILhcA^A zQ)FD$7?1a!KJVZLv{oFzLUlMJkU3ER={mD zffJFI0pF8U69E)*Z60hXf^yuaZuH{l)^2(1*s|y-gNm~km@;FUD>k^z}wRTK%}Xu&VUWM zJOOKz<214WE21q>ytSTSr@2{+x41gb<%p_4a3kHo6(V_n4yXC;+qd{EVA3h9mI0-= zkmQ4$WhU~6@4IT}iMl-d>Ji{Ot0SeE+O3?jZl5@j=}p)zH}L13dodT2^nLM-8H)Zx zb$7NGzGl*ObdV69xHzwaU|EZ6q7pm#6cy929mI1V5x}BSfxbXb+}_8l9yAAQ9DQJk z*v$t1Jb~#|-g9dJYUi3OaHb4)@9reQk1asG90sBoH^gsy)OPjhZ--aFbylrF!G*Wo z^#X0hR#^g+nHu}b4$P=QZ?L4HP*G$u7E9QCdZe1>$wVSPLs^8fYCu zp!zg!rJbQrJ5-gxrzMW7c0us~dDw2K5H=qgD=jFf9-jzmte^u%=BSegPsvQ<4-m8g z!oYxFJD<@l4ay(oj-%q<#zwqiVmekWk=O0)>{R2y_K2I{Nm;nrh5-rT70MDZdO_AL zYmAbsgB}8sfELiE%z=6up9RpNJlHLl-F8O=R`yBQtmcav6m+NJ(dU!%)O6>MA3h|; z$A5qE;)Pl~Fq>>pY$*A3kfD7zt%{0%c>GmE1}xoE0?1tUzYPKd1@fc-Bq>#=!hSG+ z>^exvULev|iazV^?9?+eOIrrJ`K^&au+0L`Z45AZ8(Ui;ZtjoF=gz6en~8#V37FBL zxv3SpKUYTvP~N&rcR=LVTOKN+YcHTsV2G3C2BTwI?d)k%@!0iN5J;I&!eqmQ#EM~& zi46d9CetG>*!7oeU_W;M74SMS9N0badpK(Uz_=utgLGJ*I&gyl0~nwZVWF?@(L4{b z9qX!uxHy!JjjiYJzqq@A2S)MOz~O=t5*xwS=xLGMX}Jeo?yB9VOXt8j-T^oWu(KSf zg&c9B*KANCrtnlL2d`#ZfCJd29bd_R@+U9mIxwvBEVlEbe+sX32*soI>RPi0VRFI? zbX#=xm(2sL7j*{&yQH)4}NfX#X|YdY|=2B)?Z&MJvDdu zCdJ4pm&JWA`aE4ZZCD+zEzi|B7?~gJ|AvAe*wOc+0muKu77srBFIvBJ;QxpI_Xz0! dS4}?MKNiDRy8UhU>IA*tkh|J + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + 128px + 96px + + 16px + + 16px + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vshampor/preenc_single.png b/vshampor/preenc_single.png new file mode 100644 index 0000000000000000000000000000000000000000..cbc8f3e4c1bcf1ad47867692e5a55b64a76ee042 GIT binary patch literal 38567 zcmcG$1yq~c*FH$yy_DiD?i46)r7aS)#hn%{?k?eSZv~15_u>*DMT4h?yE~!9CBX?0 zLgobS@Be+X=9`(dX4apzSWVl!=bZPP{qFtj{XF{(ex)jZ<0{ovGBUCo5QUfOWMt>J z$jHvBU%m+b1;W4j8~kzJ^*KcIGWhYkZ2k%S{fd)@ciDl)Q1WRRCKnqDbuQ=VR$7Sqj}c+mo; zjE}}SL@|pAAr=Ol(e$;yI^y70f8M@O?x6l5V;vo|;D$*;L`%J+r1aliu z5gxlr&Ow8d(k{}?U{;DtNQWNlwY9a0xh+Mq>y)GvYZoW+)GjgXE)TQ_IZhu{OY;U31;#1|uI0M{;7 z$6+?To?3Q3-0l?UN$BkCG;>%K33osdJn)mQJ!6Wo9I*usCllS)J@5Su5*ZVdEo46~ za_JUbC(3Z)kt2Q&Q>cTOcD~_?FhG_(;MSkBAN~59582kJJvycRnX&3yOPthbR9jsA5P zZ_+I3+D6nM?l8*jzo;+Ng4=Ybhr_3sf#dE=IF+SF3BmT}vXn&uDh-_yEI zT%z;(rkwEPJADPWVRi3Bfkqy~1ZJ#U-hE}z*~QhB8FK9b*Vj~WPbte4+;U!el9d19 z!!}_*PjEWZ<=tX%<=qo23jFp(o^GZrNzm4&D@kyA6@hB}bz2SA97<^h*Mcvgjt&;n zDJFcepLrXf96UCjs&&^=|HKMu@WoXf{B$d>JeCzdS@g4wH$(a}BC@iw*2hN&vO%TT zsXA)Ji0>r++O1nUUS4Cz_p>rH2Xj@Z@ncr$ln_N#RUVHzN#DKw9(j5Bcx$wv?eHMj zSSCn8K|zy4xy=xVg_V^OOux#mAR*zQp6xCHhqfIq(z676r>Lf;15RitOa9g_c4|US zPVPY+;@7X2^U@}F?%WYuuisWI?$d{p{^b^X$?L2SE5pSGxh#&+J`sE7}qDSyO=9vJp{bi{Fw*Sf>0p1ynaCjg4a2on!-RgxdnsQ|M| z1r`qJ-AUu0Mq8D|slhLtw4RrV7sZ6h7U{`fuSi#D*QHiK*fHLi=MFY5i^ zItcA}9;2h*CNdFHhxwSktdB&I7ay+kp^gt%`EZryZFeOPm$KxxE+lB=DwnDxJ!LOe zK3RY&^?C1Sm-Ubiso)`-Lrs<#)tMsUCa6Yi{kAGpVQ)U(BkcY zk9Y*Vs7p4tPKjYbWoMe?gs$yS{%c0k_#V^s-W~^A^V5j}R^!x;^?eJTuO{m}5g)Eo zs?Sx2&n}^e1Vq<5dqPrDA?d%tBg^4Z1ny^fA=z<{cbd;;F#kDx|14=`PAnwDV!Cqc zmgm;T2D+ZY%9dGP=)vbi3GcEfKi6^liK?z-^fvZgO-)TLrfQ;dut4M5`*(|}o@pO$ z(%K+U$3D~)6nT$dd`R#_`;RE}M?QYB2FyI5GqA{dAoqCMJ(feOzuI|r?TgIccFwLx z4O{J;3)8;qyYzVQwLOoe-i%g~NeqB3z|QqF2)ix4!mq#)hqhmbR|?@J{8z4Ak%JxL z)*Y=n6W*SC3Zo<~JMfE4gv6tloyo#I_1oRz$2%*VE%eBitD8({1}f6tuQ%dLiq&&M zh=;M9IyPXETo25+DlqO;(lURww5&=K44T^Z^S_1gHtR3P*BMW6KsoFXPR4hG8vHZ68b`siz0clR(S zqTUqgFtuU?j1gDbLRWr4r_>18eXaYdWl0@YEIKjKyWa*i2U9xwJqiACZqqBg1_RE@ zp+|I)2GXDL;-ed3cSQVP`;TaN7H-Fxb~p7ix%{Ysl)Tt|1ec2AmQYI?vS1hxa>!+70#;{=u2V7<3%7?ohV!+1p-zKu0~ z6!@ycbz!sS>jRaxBc>E7`;03ih;`}oOfn}3|25_O%z~#jGpn)4`<1(t%gppk5 zls|4t_W6?*=Ru^SNgW6q>tEmgJVNs7mAwRRU7S6-x2d`LL2XK0oc5nRU^4^Rkm##J zK2790qw@6rF}F{^gi|wV&Q~rjWjv@Om&L_&XdKf}frh*Zlu#)QU9z@jQAwJ+^#@x$ z8-Dl2*NqLQhW$UEt6Uemk1<<1&62O>aci6%M;;)AbGRL}&_kbsa4JqYe{#^b`$-2~ zZrMd;X;pp7{3HIl>~+ZY=XB z@TY2HP2&x|NTQ#vZUXR-hJW_Jw1R5V1hoz07Vho} z>w%+1MD?E#v%Tv8>kNV?b=4#h2cDPG5ff-bD!)aDu=G)~lz*-Fa!&l^?UOsS;sVd> zw>Id;2I;5CBF>p${W;U?vk}sJ>FfPD zp9L3w@t4bfeqasIhuju-kuz`tbONn)z!Rw)`vn3ZQ~~fvwWN#j!6LKu{>JZx2}hJ< zM(xS8m$lA&2X@f;EIr;{%8H<}Ev9MdBl$<*so!4PD-<`zO}a)9kH7%=C<4Zj`Gfa5 zirAPaXd4&BWET427QOLGy$?7Ae)(IoUC1}z&ki6=q*;jx!0CJiM#=jN2fc8j_ny0xj85*E-CX)W?cj8EkM2BaIWx*{iz>YxD9-$IH=-pAm~n-I zXC-iN)VBTyBfyv2`qMKbZ025;Y}dL4CbMv0;rmqJAsHd$kyOR2lJ*@5k|~ z36Vpn)poq1fE1Wfh8fQ<>@M~F1R&T1fJQ|Kq%TwUb0>^h>1g7q!=$eC@qQN4v?)Y( z59zk7?!GqK2jF(ptVoi$X9)&4oG3Hm-h`vgRBdIs=ejXOJx?`t?dV|J9^#5U!TV0N zz%~5`_l4I9uL20w0I&eBi~sjW>k&&+b;wi!E7hnCPH}OAbC+&D6|n4Nt8i>_)+{xZ z&B@6j*YEw|Oe1-;@pCVoc(lm(afps~E_NZswfpt+=Rv@u*2+4N43npR<2msM-7U$T zNMHzSe7XliOAWDv;fq0X6^nLB;*^kd)=u|C@0T*ylgfH8SD7|-)^JU*aP!(pc| zz&0H~)G}C@Z3I^6vD}}N1dLwPZ{I0n0wA6|;I+r`>nOCGtSosAVq|s5$aBh~e_%kX z#>Jx2VQO3oKyT)%f;36r@sD&q3Lvi6Mr8|6d;N67jBDu_d(Tr&Jor4j>9qsSI$?j` z>*S;gwK_1clUriFBDv+Hyu7@uU+t8U2u@_7E7__F_?-!dx~|6EVS_#0jQc?&z^hk) z!z%}&3hZdX^Y>Q^0R|dJIywS0texwBe=Q2>ci^%P;%z=i6N-%+^a1+kgK44y{3kMH zuca=TQtr>HtE&%^{5gO?HbaH6K08ZqeRo$Z!0p5DCHmeuF>9V6iTg10D$nD`GHAc; zFkmX1Xt_@x{{%^#&x?tc@Nlk(pnck(g(1)7<@+^0JIHqh_*eJ z8+^u;cpGABc1bfndbYpko-|r&_7UU&dAqA4oZQ@)e{k_pQ5hhAi~N%j`St6Q`H!C9 z04gGGn-yz0IAE`=TLBE95AX-@?&csjvIICN;BMhb#uW2`&O0w7H1xVZ9)l$9?BQIr zS|JILP1fp~T~=0K(y! zf|I>;vI9q)FuPnVpK$4)#{TCLK2sz=@@(l*tN2fVW)uN1WdY_zDhSX;$gB6gaokzz z^O$Rm`UgP$O|Y`fX6h~g)d-J`zoRhK)gmOL+glk@lJwbmBdAZ>y~{4=y*HFpRE8)_ z{N65fr^5gF=X^N;>d%q)7S3d3RfANKbUdVo4eZ1~l_T01WMB@lmq{0ksGj#0$^GNA zdS~Ixcf^}dI*oqbq__#bsR1&->=9bhxBht&BoPC#u_=7OrTgaY-Mb{8Jmu2C4HkKO z%S*xsU?&xj3op5!q;Gi?K@y~^z`>puaiBQ;NcP3;e=3hWtDd+pvu|Nx!7e6NyL3Zz ztUqq|FW&sk^z5%^RhE0tSmXA-(bSoe7c{?yJ2@D$db^x;J(A}I*X{gX-zqXhsDt$0#jfmp(`o3*GV^F;j4vlWc^gD2>S7+HWro^c;&eaOi3r4%N#wN~ zV%!Q3c+p9MNLS|FqNFYK*SS{MjAbe#*C7dKc@w{64*?UY?#YcY${gXP6n&kEU>p1$*=~=iW>x$x(@p0nw5pGD$Yi<|e3v>JS99<*r z8`ti`ztJF=L<=+RnmARdE#|PbDr3uaF~5s*N7>l<`8$X7i+;rB%7yex$*u&#D;3m} zsMwVgIr35+;xmxo>$5=jp(#A zy$LHG>|6PnWtuNa#~I|$tbB61?j552VTDt_?taEka5-g8VF82ttJmYJF5F<)RyH-h ztFNqL@0u6nx{$*~?A_|_qj*i=i;@W08^{iktlM+%6Xxd3C5cDdr0jm!`6^U*%=ghV zNtbX~oB^aDw~*+fZEW`~9J49MKBQ6E>n;??*lgs#R+Z4=aE{_pwl4?!5FWoNu>I$( z$J(T+v8UrBdf$@d2+97Tmb5G{3wTI99S-$n?)?aCK>G(;*z zvDVj`ht{V(MJkE*2V}*p1jlR6A?Lph!+f7rZ80nMjFmizs3Sh`QVHGq!O7OM%Er}0 zKwf;IGwZ%`uYdcsE7 z5%piMwr^9tpO=jZ_^8p|dhfMNjgX>f4fc4H8IpDx?Q4|QGDTm<#4enfTXaLm#^H8_ zNnTL>TdhjzCIWpz_fstK+TXMMpb}H=h?epPD7_Zs^iw?YunG%He?;NP0U;#fPgt7z zvqs73Y0mEH^q%D;XH)Hw~`1O;UhwbS|Oz*RYHr@SA!zjeMAvV^MT*9L#D2H=Mubz>Pz3Y#)8(=RGPe#7)~y5=_5tSQTvhyOdxb+Dg;S2jJaH zVg~X({(LVnlIz5p(83cIwj6Z_T8>IZbI3RS5!{j=5_@-4%w!M~5OlY4N!E&zK;JaF3roQbpU-V+)P`{5?HB0JJmK3u7 zcAZ4zY(5iBhF%Nbo4{caohg+%)kOFv<)pxcxc(10p}cRdt;0j1*}s1ARM?bZ-k$W;BAi4XX5177sQ zXuy$*`5yV>hV2bVNsi*~kN62g>)oT!yX!6^=M(+e`{lD#zs8N<2$?39jui2?OU+rX z-M1d|N$m4%t;#n!+wRD~g)BBDvQ;xsk1G7`ik3cUhrv-aAP~;|j$aov%j3 z)BM_wOoq=aI}YQ_p2Ms9P3YVjMov(5Tv(lo?>}n zgY!w>0@_NZUe&}>jYp)%*5nsv^PEYO$nlS0Xh{}SWnmE`s~jxk1wrf&-+~HkEY3T` zrlx4Z8ROdaJW&m)aLbSZUaqz=3T}Y@Yiu9ll7*Bpo1lHB^xk9z#-~56Cp5Lc1<}3+ z*KXD?91+cdS_!nYYX);>#3_kgT5~Mg{gkKNu@>9^gYiXhG4rnz^h);jo&9AJ0w>m2 zY6DV2^r!79-O+vfy!T_K$B(~1-1ha*c`|wej>x_(hdttb$;8TP3o5?v-Vx!zP@7@YsQ&~3_LnPuw*GW!EXJU ztZ~(H(zX9iZ<6Fr!1$P(`xN`!a$-Y?!KH@z;2MS}qf4pB< zYI+>Tr>LOt=KCr85hy?gqPcFxyC{f~5~oW;r8X)31IT9fxz;Htxy%$)fBRMcN`)~( z2jJyAol~ynn{_xCmI-8Be->Ig;^}u{J4Es~mx;ZCoF;re4%O=@cj-%or$t$^l;>R5 zRCBQC=t=;k&88hf`v9vEI8PA$gN*KbKzv-XP>gUE1&?R`Q85X0G)gugHaCP zeTt}$ULeOWj?D&XFNNYxy)i<_Ve%#76uAWs+?O%FohUupjirJ#Ih_ct~Bz|tZI87y%Q5D|Kd7H!pO+Z+{b#H zVKY8H@|tal;2pfT{_fyy8aT^N!h5=Z3Aa3UpScSeAldUjn2%pr`d!filkTk2f)D37 z{A&umAocl&v^8`f8!`PjK=&E23nS|`{~}dzzb|b6?j^BM;+|N)Nu0oksLE3t$>SuA z4U&v%qf-+l3%dSaoZjWF({Bad{zl_X7Rc{gcIeulabE@cys^pMX7Fv)34XqO8_e@8 zkvn8LQ*sPe8%%amLE*8&@3LE(-EY>|0`^yY1&}*kRf0+DgCQ_BYA&20(RL=e={Ppe}WHmlwm-E#uq(4)322R)u*e z_-bVP4CT}gO%~6;Nvd#wYd+Q%tIMdXRA}6zzVp~5J(fJC?LAee(E_3D8*-AlQ z2*=ej?VIOYtTrnRa$=qqK#U6XWj$@jYD~tDPPr<5shm6qb+PyhGVW zRG2g>Jtzyo3<&#vXT+GvEuL?-O~n?=$faEAe$rm7FE(mPlO9aj7Ss4K&C-Hv5QMFm|8h znGOA6N|6^Oro|s(h#U7W>+!sDl)YoEryhOAMPD%PSlD>_89njcu49Y_kD@-Y1Yc?R z^ksLre}8wx6kB7 zFSgw1=Z&6sucK%-b8M?WDCk^5wIAk=8+YaYx1N;o2dGD&yqm)0@_l9tqf% zcFujA!*~bRRn`;VyP-K<9F|$|Yev{Abdg=b?=F{ad!{bvFkz(NdzecmL(Yx$8WW}D z3>TQ^r54XN19?ANDYR2!!D~_EPMEX$ow-!G6jMuyqKBMXMKt^R?X+$88Grb{V4kvM z5@ivwxgx&xm+(WI;P9PP?c${XNpds4Jm>jI1T$lYClxspMrVuQf9VDHrSMuB^54cr zujR)b{Vw3rlk=U>ml9ex>_%*Z(d*)2;cnn*q@ZKMj5sr0XhU6yy3;sdBeQW#t<&wP z|5$DqqtBtUks9~e<>YG*XevdTg+m1&+O*N%G;V<5U;O;}bBihBK6T>!p{oXaw4xa{ba1+ zkJg{Uj7l<_s-fk>Y-Ca{%fq;{~y#F z|K;hQMV?0F|I_mR{|$K?ggCOm-cs&AT%t8&Hpc6%OH(eI^Re8J5L^ULe$iA>xKtE==wj(Z*++~Q9& z^t2UCSP?f$r=dY;7^k_3NQ&4{&T;#B4?QqrK`p3q+aeEDiMx%;ezKl6t??Deq^RuO zOWl08s(1Y6CcM9S*4lhFM99L5b{p0&yuhn;MY?PUWyNLFHV(9~cKnJ3j^aXxe;&Ob zbGbLNuF`ILS)5?~BC^5+;(PO=d~#cBT>bP+zC}lr$JWd$|63l8hvIvU(;SJyWEX!Q|^1i?7aK!tQ{=msg5gSG27m~@b!&}>k#sNp|YIJ~W zgdfFd8LNnKUWy?*FL*?7GzTdZ*DH(023i%=t6Fx|yG5GylNUYiu+;%lcH%nK(^e)H z79UV7Hd?CMRO=p*wshm%akhDOP~aoIvJg0Yd1j}QAt$!G!)O^L!C97msFSd4Dl5Za z)^LAd62n0@(-~c3%+Z49C9Aerk@oO%#A>x*_`BJ5TE)XO5edbH?>ioHn)wy%+e^|; z?9p8CV>s-zOZ;dYUqH(n_VAJ8GESrt>np4JGT|wf2WRWOChc-FPl@-YD=JRMvb{(x z?f_+Lkvx3GPn>-T+F>q*SIsrE9kUe&0XOXhzZ!~h;LC@dZ(&%JDio@xTEn(%@?sLv zbYS(HowD239HVq@5^xIpuPg^t6F**R9S-qCb}4&p3zsvO*I27BQ;4q(yBZZ+tU4qv z=5{#HJm?@s*)oMyhf0KEI7|wv@su-FuS#1JKyw+p7-v)W%nq zZ5)XK7;{8@@09+qU38*|Qm(}d>vtwNlf1XQZ3TwLJ~ol~%A;99QEy}GAClM+e#?*) zBYe){dX#X{Qeb2t)$Si5tMnwe3SoCIE}#pu!rRhE7epBkdr4PS_AWk|mn zvc{z29O6()TEY_v7Ug5m-T1R#NoGUOuiuKFQ@H*53bSv(Jx?^0g2=G_J)YlQUlHl% zj;jG|5~bAZTy3r8-+zHU!Zk3kN+?s&pGPy(r*p9~3yO@dQ`6fyX=7}13(1$LAG|y7 zofp+fwM%|yy|0(^!GuowJ4faMd=&94#_7Y_3q;3Y2?xOhN^w=BUD`OhUG{=JcaIA0 zM)dU$`cftf)KWegtYaPB9>};9!{zezY$f8{v1_?^o?SrXq6at3jmW8PR|8BQv-BgL zddLM)qW3vlMLfr#uC40bjOvX8fV;m`nJeyc+6@wSrALPJ??#)aC-V$cSJ^$ONJOEy zv_h|h?}@L_wnbCWkm6*8e}r3$R+%72ok7P!YClX%ees(4y8A(m-hTPoFFk(ll?(EC z!bFF9>=t;0=*wkS7GX@L10?;+})Td^H5QAGCxJd`knNfe%?9`jsi zOL5h9szATxg$!|Al27*DS7~DtvGI0~t5|n(8Z2`wu5ih6a~B_j^MdER+NF%%kKI*9 z12^F)7q_+%%wGvV-?F+h^O9YRA6)gDaru>v(;z?e*wJIC#FB+!a_IVD=r7ZMtI_`* zaQ`=jUIOF#)4QornRx%Y3%n;E=}8sbEtW6OFQ>H1 zR2=O%Hr~9bpfY77^KZVw8vZ}Q`u{e||JFz{qaPWcoV+@maT-hmBkz5E#LLVq+as|Z zY7Mw(4Zx7HGBErI4heBddTRaWI-SI*ACZ7#tsr)GDid%hq9BR4q%d^)OCwK}mX)8s zpY&Cda3U12CMnq;{|)%(4ZR-SWZ84O#b`Qtp~MItgQ0X!VZJYVbonf*3b#` zCsxGmbmA!B$6~zE!}<+`y$MqJ)e=xQO%Bm1DMdiTvHTC+zo=j)SA%XYj9z+LVt?lE z<7ubT(o&m|lGI|o^4yy=B42Wp;*$Wc0f9*!E-{w7uWE0138THTva*z>JOMXBQZ6}?CsC3BLa(!iMTf4sFa6;%eyPTDQ%U?%O-H_F+V zi{7paoqQy|Roa|H!D9q^B+NLqi|pl@3H6@2g7doS31z?uG?rB~-+ zAB+d2TXoy5O>mDpB$3PVK;8tXJdgv~Qi1bq^U8FCxQW>5;omM()r-WDWML#loVuoF zOmjG`foY@=&?xwhS;RwNAnRvQPH^g^a{~<2aI#~gHB*~24M}0Tw>lIzmdb!EDIAUChMDG|*I4xy-jWVOL3a zjC)xGbWInpKYV555174SKv%Ea;&p7;)g~!Z?|JJ!D+n#hY#4(nrbpk6%pR|4D~yci zwikDiRZh8YGBA`xLNGJR&h0_(Niyd>%2j!>3bm5eG3lW92hXBVJY+T z)hRk4Tm!~$vo=~Px79U80VG9mMB`zCJDrrDE&-483?S_4K>F>GQ-C?vu-djm3KiMm+fR=@b7{-s>#6kdTlq6JvlRqJCG>`|m`dgpEJmq_qPzIJJsFQlWLQP>XXB z=pQ#edI5Tu!=%wK1LJ|6BoXvtfJ>F#+XPbC2_U@DgOm-gkQ94kK&!(Lb!#Q&_AF_LQ_tvdeUN zT&*G24By_bsZn+7q-cQwhPiIuT9hF)KvRx zdsX6bqocaSgBKrflGjM`@%7QCkY)mlt8oqe+TEEdt^*2G!a%P9Ogg%rB;3>hiw&sr zKR#HrCrMz8OyJtwVL&ScioZkjGo;1Xp&O?_aR=F31S(j;mWKI!xV8Jis_M=>2}kEZgO$(2SF8`XJG2|J_^ zy%PXodazdB6FQe1zfKYcm6eq-L3*U{MHoQp7wn#C>6(p_3+M9v6wmwk`%(vQ1TlcL zNV1t25eu1VP<6`!l=;DnBGQ_Ub`elP@R)LxVSw)8sYUz!0U$}qn|U%B^wW+RedK=d zJ$^2^4qG+hP!3AdH7XFuCQ#vw)S0#1boB735@u&_^F{mP^jbD0e*q2jN;9>i+trKj zp_*P^)rULFvrxMWAMtdUbtti}0XVUv?fEH>340b%(VE_RFX-`hItJXTYW1Rqb`6IQ z{{p0oFz^Uom%c+7Su^y}OkHj~XnxlbC+&guL`xRroZLsCG&bf2D5dI{_z#sBH;mKk zHUPQGLaOI9ACMZTg8%`PXC*-C&B4wNIk={K-^~qC4a%bwwMRf{Jq8qO_o+1nfj;RT zi}F_>3+M-SyT&`6@FI6r?xs3*Z)-*e8NLcq`}LcGI`G9iyCWtPiZ?ug&jmV5ahT&fdmkU4 zKFv)wfO`r+Cqmr)MWM8!i?%na}e|2N*V1d5YG45Wm3#MNYecLLOoW0o17^_H} z*NY+yqJAh-a9+Jjxtf!(;GlDc%qp#OS6y@(dgdFq2C?*~uPZ{#A>LU|G)Hoa$wW66 z=of)~DXAp#%Yy2&vajt58f;hZoDSgj|Ed3LbJA^|cd(1n}aCPVy_{=P@Il zS{d{fHd;|74zsF8CWsgaWV=C*+)N35ta)e&2@z#0P~71$3XoCJG08h;-JcoDAKRfs z+wG3(^4ls_HveaE+Z1Peu;!a6ffz&<<177Yo-a7n4q_O_T%vLup&ZOCZFUokl|{UQ z@Kvm9Ij8IbUQpdnwqR-uE`|A+0*tGKMk>; z_=p|nSYD`4Df+tqPA{xEoV1(ZjNXT9mY2Cflt6x~))*Tc468L>3gC8)19|CA%^(hu+>fMhO3!D$*NKp`omw>3K1%LZ0L`7vc9bs7GQZTWOMw#4W zRpr`wZ3W=bLhWivxrLDm@+rBQ1b^AeL7w1VEb9b5jbgSt5B`%xi$Y7%r|AL-TSg$9Q9;#va_5%%Ab3y|~t!naW=b z=dCQ4!@2IdD=Ncy9&!!jzBlx>zk0P!Jy$Mg<{fJ2u#HCXR=7S-oz6ntP=6hxg>B3+ z-=COTqSo@kEemX;{xHj=}Q-5bNV^@z~QvZ0)-_^}i#}*XH*4))vmtCR&Q$GbwRc1~6P#Kif z^=o&_pYzHGef?>cat8yD%sPcW1r+@(vdwqb0z1s_owjC@7B#TQKQ@AO1*L}*g8nIj z8Wh)Ru#NQYK^bw)=PZPXpbWgz;2XP8)bQh%93b8FU)}h5v_Bg`SiDx^WAJKw0p*#B z2Bf7kJtG%ak;1cmdCCK+g|=ewM*Oy7CJMs8!QJ z3fD~{1|aj(J^QvKu0de@A6-N1K?>@8V%Z9?jrq&RiG)*9H1N?Kz+^f& zlz_gLKXbe;&a+{{lVUU z{=R0}@g`M;>hps`r&WXS&#N-V0Gl{~^o5kPD!_o~JoCyu78-$vb)Z4T4q#Vih_$tK zEoeDWy#9@pB>C>y5rvQdnp_!L-SrU!9WBw)G<*F@yhEVVPk*fn0)f;5kP%kd_));R zpUHPv&k6z~$yn*VX3h|2X@u+&fH?Kw%Fsm`jWDYIymTL}xF)S1Ke89o%4?Tdd}dFO z?(LVV-jVSj5eH(w`!&7v82{Qjz z>fYbzJ@>}?`*Vy5y9n*yEeVN-Kd$9mxK>Xk6nfe+5%|cpeI${j9IlxS7w@cDOoNNL zuZ)7c6N5&fk3gbxJY>bp&cUHb@3-+62537A&in{Gb8ZbNGmhZjNQDE-h0erW&-Ll5 zw?99K`R#utL2%GB){Leu0WjbY07iqA#_g>wQb#1Xk%t-}^#VO1I-pFk2TWA}G+AgI zA2V44Usna{6iryGjhXM3pqufe=>aur`~kSgBq94G97vcwZ_-KRa~ps%+_%3L%6p{B zS8$f#hrl@kf#x-x+Z@bGBhXz7oaUi=g z$hp4l4|;Dv#?3A%X$1CT1jvb*3wi@0Xhv>Q@@J2}*#MOTAy5PoglPg*D5)qwO-`=0 zwh5Z0;R^`k1_xqhd}#Vgm^-y^n=rRUZn(H zFTQc@8n~uXb(xQ*oTMtRabcI#x4Ap6GxA1#Wyc`hQ%`n{bleL9%+CPfG>O5>_DxbK8^c|Ms@%@CNC+ zGb9R)tsHAzKE&^v^Hd%XPnfSm-`?`*obXLn7e5UDd*lCacS|NSZOxR^u6Grll;>M= zFm5B8ro*{azv3IbsXjK z=+$ZY5R@1pD}G^G(d9-Z*H3Slwf(fE+5!g}MUp{>Tq0;;bp((8wJD*ugNwDqpV;?4 zdN;YGwbtd)G`&>YV~ibk4`>-#IUCz8V7H9LAP8T>Dyz;aTE zfKdT+LK;PKqiTrtV|Im=E4hmw^h3x)D;2(L-KxAV$K5nbz>VhTc**E}CPJ}S+rfWd zz0$0oD_QZ=*DOK|`@r=>hCJ92v9fn8)Fu!cUG|MiuZI5NDRr*|O@t5N=_S68bHEbk;jw2@<;yj;i!&5O94!A2*-xiYSZ#(FV; zo#S~S?K+Ty1i=XoWApf4%IoW>37le@VS$4n8Wrm>?)yry1IEz=iS4K}Q1XeZSp0II zmVKyj#etNkeGpYAP&w!@*pIQdotMX}Of$|eKyKJn6D|0?HF8~;o8&ADeTH00hOGlH z@RRCfr1t{Ak{)Bk=kUr}$PvZYvfY}s*eGM*ypfa;5t{~z`&i%=J*9{@REazX=aX!#uOsww~9*@=4QD5VQ9?3j(8$v)?7p9 z{GHJre>Q<5twi6$j&NF~pJ!n492~COJ z^^8IrQXGNzztgH1GO2{@I_{_W=F7f)- zwI_zXnn<~AG2I|vdXw9bX}`HI5Amd2$U{J0(7Qzy>XSWikcv_ka|^9Et7}uO>A?6y zV9yD2f^FL^N5jQ8@8m250pU56grEUd?zuNoVG1cv8T@xeq(s)F>f3juLF>g?2OC5H^Cs5oI ziFElJT>NCuOu%}xltuh+VejFm0b~CX;kU`||K}wSWUv0CA~^F55bY;F=gDMGC4^-E zJHB{@xFF+b3gwxwtX>AK?KR+vzl9fn;C)^UjO2f#CX+pUY$cV@+qWC79)*N)4(Xg-qZw z{z=W!evK+tQvaN8)C}3PC{HqWwT|K4W#sA)p7jZt_~VeXrvuE8{V@1fdHR1den|Z} zg}ptk!pWp;Xnp@kzAr2f3rWjuG&fv4b^qUZTX^e4B6Z;}-!pTvtGnwxUE%+isN1{K z{XZ?)ta!d5{>;{(hFYR)w`faey0kQ@LOa!no55(f^`SnjX?KSg;DU_X9vWp)GDEb- zjk&z#+S~IF;KUM)EG#M}by+O9B8%-9yGTQC&dwEv!izC#DY_lW;mfWT)T=8h#`J4U zS_aF_uCmNQ-2102qmBeoM576niddl*O zeD)uH_V3aB1b&oU`(9gV=v?#xcbutqd)up{n^=CO741Al;?W^rHT8Lz zKV*kK&3nz%WYw(J@(0$lFmg#Rq&-`KDPS#m&cjd3m{OWfP5G0wuv5q)3~ei4YVOaE zYRb9uKI=-7*HrB&{kMso;<9kgB)Hf5UW+q``lD$nn}*S^}u7i(%AGSvdi

P`8H)5P5*E-03?W;l(i<>`F_U2RHw@ zG{e`@ar2hW6Qa)kJZo|{%Ruq<6D;|?GUpik%@dJmcQ?XxWD_&@Lf1u z%0D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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + 128px + 96px + + 16px + + 16px + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 1d9a87d6ce7ff66091a3451d629754a732edae56 Mon Sep 17 00:00:00 2001 From: Vasily Shamporov Date: Tue, 15 May 2018 14:20:15 +0300 Subject: [PATCH 3/7] [vshampor] Added more details to SAS --- vshampor/SAS.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/vshampor/SAS.md b/vshampor/SAS.md index 7c8e676..9c2e996 100644 --- a/vshampor/SAS.md +++ b/vshampor/SAS.md @@ -1,16 +1,16 @@ # "deshuffler" ## Software Architecture Specification ##### Vasily Shamporov, Apr 2017 -# # -###Overview +### Overview The program is written in C++ (with the support of C++14 standard). The basic program control flow is presented on the figure below: ![alt text](control_flow.png) The input YUV file , which has every frame (except the first one) shuffled in random order on the basis of 64x64 blocks, is first opened for reading; next, for each frame the data which describes the correct position of each shuffled tile on the unshuffled frame ("permutation data") is calculated. Afterwards (optionally) the original unshuffled stream is completely reconstructed and output to the disk using the input shuffled stream and the permutation data calculated in the previous step. The calculation of permutation data is based on motion estimation between consecutive frames of the input YUV stream. More details on some of the steps of the algorithm follow. -###Details -#####Calculate permutation for the stream + +### Details +##### Calculate permutation for the stream ![alt text](perm_gen.png) This step incorporates frame-level parallelism to improve performance - the input stream is divided into M equal batches, with consecutive frame sequences in each batch, and each part is assigned a worker thread. Each worker thread then calculates permutation data between pairs of consecutive frames inside their batch, starting from the first one in display order. @@ -18,17 +18,17 @@ The batch containing the first, unshuffled frame and the corresponding worker th It is assumed that motion estimation between a shuffled frame and an unshuffled one will be more effective in producing correct permutation data than motion estimation between two shuffled, although consecutive frames and the calculation of permutation data for some of the frames in the non-primary thread batches may fail (see below for more details on the failure status assignment). To address this, the failed frames from each non-primary thread are aggregated, and then, after all threads have finished their calculations, the failed frames are processed in sequential order while using reconstructed preceding frames (which should be available by this moment of time, either as video data or absolute permutation data), and the correct permutation data is calculated for these frames. -#####Calculate permutation for a sequential frame batch +##### Calculate permutation for a sequential frame batch ![alt text](perm_batch.png) As stated above, each worker thread processes its own batch of sequential frames starting with the first pair of consecutive frames in display order. Calculating permutation data between two frames is performed using FEI PREENC, which performs motion estimation on a 16x16 block basis, while shuffled tiles have a size of 64x64 pixels. Theoretically, it is sufficient to only perform motion estimation for a single 16x16 block inside the 64x64 tile to calculate the tile position on the preceding frame. This may be prone to errors, but brings obvious performance gain; therefore, as a first step, for each pair of consecutive frames (K_(i - 1), K_i) a pair of special frames (S_(i - 1), S_i) is constructed by taking a 16x16 block from the center of each 64x64 tile and putting them side-by-side in the same raster scan order as for the original frames. The permutation data is then calculated for frames (S_(i - 1), S_i). If this fails, the algorithm falls back to motion estimation on the full-res frames (K_(i - 1), K_i). If this fails as well (if, for example, it was not possible to reconstruct frame K_(i - 1)), then the whole frame K_i is assigned a failure status and the processing progresses to the next pair of frames in the batch. It is assumed that the primary thread should not fail at this point, otherwise deshuffling as a whole fails since no other means to improve the motion estimation accuracy are included in the algorithm. -#####Calculate permutation for a frame pair +##### Calculate permutation for a frame pair ![alt text](perm_pair.png) When permutation data is calculated for two frames A and B, one of them serves as a reference for the other in terms of motion estimation. Let A be the reference frame - depending on the situation, it may already have absolute permutation data (calculated previously by the primary thread), relative permutation data (calculated previously by a non-primary thread), or no permutation data at all (if motion estimation by a non-primary thread failed, or frame A is the first one in a batch belonging to a non-primary thread). If frame A has absolute permutation data, then frame B will be assigned absolute permutation data after PREENC run as well, and it is marked as such. Otherwise, frame B is marked as having relative permutation data. Next, PREENC is run on frames A and B with A as reference. The output of PREENC is a map of (multiple) motion vectors per each 16x16 block of the frame and corresponding distortion values. Afterwards, if frames A and B were down-sized using the algorithm described in the previous section, a single best motion vector is selected for each 16x16 block (representing a 64x64 tile on the full-resolution frame); otherwise, if frames A and B had full resolution, a single best motion vector is selected for each 64x64 tile. Either way, at this point a per-tile map of motion vectors is produced for frame B relative to frame A. If this map specifies a valid permutation of tiles (i.e. no two MVs point to the same tile on frame A), then the calculation is deemed successful and actual permutation data is computed and assigned to frame B; a success status is returned. Otherwise, the calculation is deemed a failure - no permutation data is computed and a failure status is returned. -######PREENC call specifics +###### PREENC call specifics As stated above, PREENC works on a 16x16 block basis. However, the range of produced MVs is limited by the PREENC window size (roughly 128x96 pixels) - see picture below: ![alt text](preenc_single.png) @@ -47,11 +47,11 @@ The resulting motion vectors and distortion values from each call are aggregated Since each PREENC call associated with a search area is independent from the others, these calls can be distributed among threads, achieving, roughly speaking, a "search-area parallellism". -######Checking the per-tile MV map for consistency +###### Checking the per-tile MV map for consistency Determining whether the per-tile MV map specifies a valid permutation of tiles is performed in the following way: first, a 2-D array of M x N boolean values `bool hitmap[M][N]` is allocated (where M and N are width and height of the frame in tile units respectively) and each boolean value is initialized to false. Next, per-tile motion vectors are processed in tile raster scan order; the coordinates N_x, N_y (in tile units) of the "target" tile , i.e. the tile where the motion vector points to when centered on the tile it belongs to ("source tile"), are calculated. If `hitmap[N_x][N_y]` is `false`, then it is set to `true` to mark that the corresponding "target" tile has been associated with one of the "source" tiles. If `hitmap[N_x][N_y]` is already `true`, the MV map is deemed as not specifying valid permutation data. Otherwise, if, after processing all per-tile MVs there has not been a situation where `hitmap[N_x][N_y]` is already ` true`, the MV map is deemed as specifying valid permutation data. The complexity of this algorithm is O(M * N) in computations and O(M * N) in memory. -#####Permutation data +##### Permutation data The permutation data format for frame B relative to frame A is simple - it is a list of integers (one integer for each tile of frame B in raster scan order), each one representing a position of the corresponding tile on frame A in raster scan order. -#####Reconstructing the original stream +##### Reconstructing the original stream Since by the time the original stream reconstruction step is executed the absolute permutation data is known (i.e. each frame can be reconstructed using only its own pixel data and the permutattion data), this step is easily parallelizable on the pixel-level - basically, a single thread may be assigned to each tile to be replaced. From b37825f6f7f9114989c6dc679984a0fa934df1bb Mon Sep 17 00:00:00 2001 From: Vasily Shamporov Date: Mon, 2 Jul 2018 20:24:29 +0300 Subject: [PATCH 4/7] [vshampor] Add basic CMake project structure --- .gitmodules | 3 +++ vshampor/deshuffler/.gitignore | 17 +++++++++++++ vshampor/deshuffler/CMakeLists.txt | 24 +++++++++++++++++++ vshampor/deshuffler/include/deshuffler.h | 22 +++++++++++++++++ vshampor/deshuffler/include/input_params.h | 14 +++++++++++ .../deshuffler/include/permutation_data.h | 10 ++++++++ vshampor/deshuffler/src/deshuffler.cpp | 17 +++++++++++++ vshampor/deshuffler/src/input_params.cpp | 5 ++++ vshampor/deshuffler/src/main.cpp | 14 +++++++++++ vshampor/deshuffler/src/permutation_data.cpp | 9 +++++++ vshampor/deshuffler/unit_tests/CMakeLists.txt | 18 ++++++++++++++ .../deshuffler/unit_tests/deshuffler_test.cpp | 8 +++++++ vshampor/deshuffler/unit_tests/googletest | 1 + 13 files changed, 162 insertions(+) create mode 100644 .gitmodules create mode 100644 vshampor/deshuffler/.gitignore create mode 100644 vshampor/deshuffler/CMakeLists.txt create mode 100644 vshampor/deshuffler/include/deshuffler.h create mode 100644 vshampor/deshuffler/include/input_params.h create mode 100644 vshampor/deshuffler/include/permutation_data.h create mode 100644 vshampor/deshuffler/src/deshuffler.cpp create mode 100644 vshampor/deshuffler/src/input_params.cpp create mode 100644 vshampor/deshuffler/src/main.cpp create mode 100644 vshampor/deshuffler/src/permutation_data.cpp create mode 100644 vshampor/deshuffler/unit_tests/CMakeLists.txt create mode 100644 vshampor/deshuffler/unit_tests/deshuffler_test.cpp create mode 160000 vshampor/deshuffler/unit_tests/googletest diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1df876d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vshampor/deshuffler/unit_tests/googletest"] + path = vshampor/deshuffler/unit_tests/googletest + url = https://github.com/google/googletest.git diff --git a/vshampor/deshuffler/.gitignore b/vshampor/deshuffler/.gitignore new file mode 100644 index 0000000..959246a --- /dev/null +++ b/vshampor/deshuffler/.gitignore @@ -0,0 +1,17 @@ +/Debug/ +*.yuv +CMakeFiles/* +bin/* +*/bin/* +*/CMakeCache.txt +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +lib/* +*.pc diff --git a/vshampor/deshuffler/CMakeLists.txt b/vshampor/deshuffler/CMakeLists.txt new file mode 100644 index 0000000..511105b --- /dev/null +++ b/vshampor/deshuffler/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required (VERSION 3.11) +project (deshuffler) +if (NOT CMAKE_BUILD_TYPE) + message(STATUS "No build type selected, default to Debug") + set(CMAKE_BUILD_TYPE "Release") +endif() + +set (CMAKE_CXX_STANDARD 11) + +set(CMAKE_BINARY_DIR bin) +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) +set(LIBRARY_OUTPUT_PATH lib) + +include_directories(include) + +add_subdirectory(unit_tests) + +add_library(deshuffler STATIC + src/deshuffler.cpp + src/input_params.cpp + src/permutation_data.cpp) +add_executable(deshuffler_cl src/main.cpp) +add_dependencies(deshuffler_cl deshuffler) +target_link_libraries(deshuffler_cl deshuffler) diff --git a/vshampor/deshuffler/include/deshuffler.h b/vshampor/deshuffler/include/deshuffler.h new file mode 100644 index 0000000..1aa56f9 --- /dev/null +++ b/vshampor/deshuffler/include/deshuffler.h @@ -0,0 +1,22 @@ +#ifndef DESHUFFLER_H_ +#define DESHUFFLER_H_ + +#include "permutation_data.h" +#include "input_params.h" + +class Deshuffler +{ +public: + Deshuffler() = default; + Deshuffler(const InputParams& _params) : params(_params) {} + void CalculatePermutation(); + void OutputPermutation(); + void ReconstructStream(); + void OutputStream(); +private: + InputParams params; + PermutationData permutation_data; + +}; + +#endif /* DESHUFFLER_H_ */ diff --git a/vshampor/deshuffler/include/input_params.h b/vshampor/deshuffler/include/input_params.h new file mode 100644 index 0000000..b9bac64 --- /dev/null +++ b/vshampor/deshuffler/include/input_params.h @@ -0,0 +1,14 @@ + +#ifndef INPUT_PARAMS_H_ +#define INPUT_PARAMS_H_ + + +class InputParams +{ +public: + InputParams() = default; + InputParams(int argc, char* argv[]); +}; + + +#endif /* INPUT_PARAMS_H_ */ diff --git a/vshampor/deshuffler/include/permutation_data.h b/vshampor/deshuffler/include/permutation_data.h new file mode 100644 index 0000000..6040f7e --- /dev/null +++ b/vshampor/deshuffler/include/permutation_data.h @@ -0,0 +1,10 @@ +#ifndef PERMUTATION_DATA_H_ +#define PERMUTATION_DATA_H_ + +class PermutationData +{ +public: + PermutationData(); +}; + +#endif /* PERMUTATION_DATA_H_ */ diff --git a/vshampor/deshuffler/src/deshuffler.cpp b/vshampor/deshuffler/src/deshuffler.cpp new file mode 100644 index 0000000..dd2a525 --- /dev/null +++ b/vshampor/deshuffler/src/deshuffler.cpp @@ -0,0 +1,17 @@ +#include "deshuffler.h" + +void Deshuffler::CalculatePermutation() +{ +} + +void Deshuffler::OutputPermutation() +{ +} + +void Deshuffler::ReconstructStream() +{ +} + +void Deshuffler::OutputStream() +{ +} diff --git a/vshampor/deshuffler/src/input_params.cpp b/vshampor/deshuffler/src/input_params.cpp new file mode 100644 index 0000000..e4225a2 --- /dev/null +++ b/vshampor/deshuffler/src/input_params.cpp @@ -0,0 +1,5 @@ +#include "input_params.h" +InputParams::InputParams(int argc, char* argv[]) +{ + +} diff --git a/vshampor/deshuffler/src/main.cpp b/vshampor/deshuffler/src/main.cpp new file mode 100644 index 0000000..b4cb27a --- /dev/null +++ b/vshampor/deshuffler/src/main.cpp @@ -0,0 +1,14 @@ +#include "deshuffler.h" +#include +using namespace std; + +int main(int argc, char* argv[]) { + InputParams params(argc, argv); + Deshuffler deshuffler(params); + deshuffler.CalculatePermutation(); + deshuffler.OutputPermutation(); + deshuffler.ReconstructStream(); + deshuffler.OutputStream(); + return 0; +} + diff --git a/vshampor/deshuffler/src/permutation_data.cpp b/vshampor/deshuffler/src/permutation_data.cpp new file mode 100644 index 0000000..fba6599 --- /dev/null +++ b/vshampor/deshuffler/src/permutation_data.cpp @@ -0,0 +1,9 @@ + +#include "permutation_data.h" + +PermutationData::PermutationData() +{ + // TODO Auto-generated constructor stub + +} + diff --git a/vshampor/deshuffler/unit_tests/CMakeLists.txt b/vshampor/deshuffler/unit_tests/CMakeLists.txt new file mode 100644 index 0000000..304f0dd --- /dev/null +++ b/vshampor/deshuffler/unit_tests/CMakeLists.txt @@ -0,0 +1,18 @@ +add_subdirectory(googletest) +include_directories(googletest/googletest) +include_directories(googletest/googletest/include) +include_directories(googletest/googlemock) +include_directories(googletest/googlemock/include) + +add_executable(unit_tests + deshuffler_test.cpp) +add_dependencies(unit_tests deshuffler gtest_main) +target_link_libraries(unit_tests deshuffler gtest_main) + +add_custom_command( + POST_BUILD + TARGET unit_tests + COMMAND unit_tests + WORKING_DIRECTORY bin/ + COMMENT "Running unit tests..." +) \ No newline at end of file diff --git a/vshampor/deshuffler/unit_tests/deshuffler_test.cpp b/vshampor/deshuffler/unit_tests/deshuffler_test.cpp new file mode 100644 index 0000000..165e375 --- /dev/null +++ b/vshampor/deshuffler/unit_tests/deshuffler_test.cpp @@ -0,0 +1,8 @@ +#include "deshuffler.h" +#include "gtest/gtest.h" +TEST(DeshufflerTest, ShouldCalculatePermutation) +{ + Deshuffler deshuffler; + deshuffler.CalculatePermutation(); +} + diff --git a/vshampor/deshuffler/unit_tests/googletest b/vshampor/deshuffler/unit_tests/googletest new file mode 160000 index 0000000..ba96d0b --- /dev/null +++ b/vshampor/deshuffler/unit_tests/googletest @@ -0,0 +1 @@ +Subproject commit ba96d0b1161f540656efdaed035b3c062b60e006 From fc82d51afa2aadad502600ed9b5b4a99c5557691 Mon Sep 17 00:00:00 2001 From: Vasily Shamporov Date: Mon, 2 Jul 2018 20:24:29 +0300 Subject: [PATCH 5/7] [vshampor] Add MSDK code --- vshampor/deshuffler/.gitignore | 1 + vshampor/deshuffler/CMakeLists.txt | 7 + vshampor/deshuffler/include/deshuffler.h | 11 +- vshampor/deshuffler/include/yuv_reader_seek.h | 15 + .../msdk_api/include/mfxastructures.h | 162 ++ .../deshuffler/msdk_api/include/mfxaudio++.h | 101 + .../deshuffler/msdk_api/include/mfxaudio.h | 60 + vshampor/deshuffler/msdk_api/include/mfxbrc.h | 110 + .../deshuffler/msdk_api/include/mfxcamera.h | 233 ++ .../deshuffler/msdk_api/include/mfxcommon.h | 176 ++ .../deshuffler/msdk_api/include/mfxdefs.h | 158 ++ .../include/mfxdispatcherprefixedfunctions.h | 145 + vshampor/deshuffler/msdk_api/include/mfxenc.h | 71 + vshampor/deshuffler/msdk_api/include/mfxfei.h | 545 ++++ .../deshuffler/msdk_api/include/mfxfeihevc.h | 271 ++ .../deshuffler/msdk_api/include/mfxjpeg.h | 101 + vshampor/deshuffler/msdk_api/include/mfxla.h | 94 + vshampor/deshuffler/msdk_api/include/mfxmvc.h | 99 + vshampor/deshuffler/msdk_api/include/mfxpak.h | 74 + .../deshuffler/msdk_api/include/mfxplugin++.h | 721 +++++ .../deshuffler/msdk_api/include/mfxplugin.h | 203 ++ vshampor/deshuffler/msdk_api/include/mfxsc.h | 52 + vshampor/deshuffler/msdk_api/include/mfxscd.h | 59 + .../deshuffler/msdk_api/include/mfxsession.h | 50 + .../msdk_api/include/mfxstructures.h | 2131 +++++++++++++++ .../deshuffler/msdk_api/include/mfxvideo++.h | 188 ++ .../deshuffler/msdk_api/include/mfxvideo.h | 100 + vshampor/deshuffler/msdk_api/include/mfxvp8.h | 69 + vshampor/deshuffler/msdk_api/include/mfxvp9.h | 52 + .../msdk_api/include/mfxvstructures.h | 22 + .../mediasdk_structures/ts_ext_buffers_decl.h | 141 + .../mediasdk_structures/ts_struct_decl.h | 1376 ++++++++++ .../msdk_api/mediasdk_structures/ts_typedef.h | 49 + .../opensource/mfx_dispatch/CMakeLists.txt | 177 ++ .../mfx_dispatch/include/intel_api_factory.h | 33 + .../include/mfx_critical_section.h | 66 + .../mfx_dispatch/include/mfx_dispatcher.h | 219 ++ .../include/mfx_dispatcher_defs.h | 66 + .../mfx_dispatch/include/mfx_dispatcher_log.h | 240 ++ .../mfx_dispatch/include/mfx_dxva2_device.h | 142 + .../include/mfx_exposed_functions_list.h | 141 + .../include/mfx_library_iterator.h | 130 + .../mfx_dispatch/include/mfx_load_dll.h | 49 + .../mfx_dispatch/include/mfx_load_plugin.h | 85 + .../include/mfx_plugin_cfg_parser.h | 83 + .../mfx_dispatch/include/mfx_plugin_hive.h | 109 + .../mfx_dispatch/include/mfx_vector.h | 210 ++ .../mfx_dispatch/include/mfx_win_reg_key.h | 25 + .../include/mfxaudio_exposed_functions_list.h | 71 + .../mfx_dispatch/pkg-config-shared.pc.cmake | 10 + .../mfx_dispatch/pkg-config.pc.cmake | 9 + .../opensource/mfx_dispatch/src/main.cpp | 1030 ++++++++ .../mfx_dispatch/src/mfx_critical_section.cpp | 22 + .../src/mfx_critical_section_linux.cpp | 79 + .../mfx_dispatch/src/mfx_dispatcher.cpp | 328 +++ .../mfx_dispatch/src/mfx_dispatcher_log.cpp | 424 +++ .../mfx_dispatch/src/mfx_dxva2_device.cpp | 20 + .../mfx_dispatch/src/mfx_function_table.cpp | 133 + .../mfx_dispatch/src/mfx_library_iterator.cpp | 21 + .../src/mfx_library_iterator_linux.cpp | 373 +++ .../mfx_dispatch/src/mfx_load_dll.cpp | 20 + .../mfx_dispatch/src/mfx_load_dll_linux.cpp | 121 + .../mfx_dispatch/src/mfx_load_plugin.cpp | 453 ++++ .../src/mfx_plugin_cfg_parser.cpp | 374 +++ .../mfx_dispatch/src/mfx_plugin_hive.cpp | 20 + .../src/mfx_plugin_hive_linux.cpp | 391 +++ .../mfx_dispatch/src/mfx_win_reg_key.cpp | 20 + .../deshuffler/sample_common/CMakeLists.txt | 7 + .../sample_common/include/abstract_splitter.h | 72 + .../sample_common/include/avc_bitstream.h | 232 ++ .../sample_common/include/avc_headers.h | 161 ++ .../sample_common/include/avc_nal_spl.h | 101 + .../sample_common/include/avc_spl.h | 142 + .../sample_common/include/avc_structures.h | 1104 ++++++++ .../sample_common/include/base_allocator.h | 236 ++ .../sample_common/include/brc_routines.h | 402 +++ .../sample_common/include/current_date.h | 26 + .../sample_common/include/d3d11_allocator.h | 115 + .../sample_common/include/d3d11_device.h | 21 + .../sample_common/include/d3d_allocator.h | 23 + .../sample_common/include/d3d_device.h | 21 + .../sample_common/include/decode_render.h | 34 + .../sample_common/include/general_allocator.h | 61 + .../sample_common/include/hw_device.h | 50 + .../sample_common/include/intrusive_ptr.h | 62 + .../sample_common/include/mfx_buffering.h | 398 +++ .../sample_common/include/mfx_itt_trace.h | 59 + .../sample_common/include/mfx_plugin_base.h | 40 + .../sample_common/include/mfx_plugin_module.h | 40 + .../include/mfx_samples_config.h | 24 + .../sample_common/include/parameters_dumper.h | 73 + .../sample_common/include/plugin_loader.h | 267 ++ .../sample_common/include/plugin_utils.h | 67 + .../sample_common/include/sample_defs.h | 190 ++ .../sample_common/include/sample_types.h | 49 + .../sample_common/include/sample_utils.h | 855 ++++++ .../sample_common/include/shared_ptr.h | 20 + .../sample_common/include/surface_auto_lock.h | 69 + .../sample_common/include/sysmem_allocator.h | 78 + .../sample_common/include/time_statistics.h | 264 ++ .../sample_common/include/v4l2_util.h | 120 + .../sample_common/include/vaapi_allocator.h | 110 + .../sample_common/include/vaapi_device.h | 233 ++ .../sample_common/include/vaapi_utils.h | 474 ++++ .../include/vaapi_utils_android.h | 43 + .../sample_common/include/vaapi_utils_drm.h | 94 + .../sample_common/include/vaapi_utils_x11.h | 67 + .../sample_common/include/version.h | 45 + .../sample_common/include/vm/atomic_defs.h | 37 + .../sample_common/include/vm/file_defs.h | 33 + .../sample_common/include/vm/so_defs.h | 34 + .../sample_common/include/vm/strings_defs.h | 71 + .../sample_common/include/vm/thread_defs.h | 168 ++ .../sample_common/include/vm/time_defs.h | 50 + .../deshuffler/sample_common/include/vpp_ex.h | 59 + .../sample_common/lib/libsample_common.a | Bin 0 -> 879306 bytes .../sample_common/src/avc_bitstream.cpp | 2154 +++++++++++++++ .../sample_common/src/avc_nal_spl.cpp | 535 ++++ .../deshuffler/sample_common/src/avc_spl.cpp | 815 ++++++ .../sample_common/src/base_allocator.cpp | 291 ++ .../sample_common/src/brc_routines.cpp | 1004 +++++++ .../sample_common/src/d3d11_allocator.cpp | 21 + .../sample_common/src/d3d11_device.cpp | 21 + .../sample_common/src/d3d_allocator.cpp | 22 + .../sample_common/src/d3d_device.cpp | 21 + .../sample_common/src/decode_render.cpp | 21 + .../sample_common/src/general_allocator.cpp | 138 + .../sample_common/src/mfx_buffering.cpp | 204 ++ .../sample_common/src/parameters_dumper.cpp | 1037 ++++++++ .../sample_common/src/plugin_utils.cpp | 199 ++ .../sample_common/src/sample_utils.cpp | 2349 +++++++++++++++++ .../sample_common/src/sysmem_allocator.cpp | 418 +++ .../sample_common/src/v4l2_util.cpp | 357 +++ .../sample_common/src/vaapi_allocator.cpp | 582 ++++ .../sample_common/src/vaapi_device.cpp | 531 ++++ .../sample_common/src/vaapi_utils.cpp | 462 ++++ .../sample_common/src/vaapi_utils_android.cpp | 84 + .../sample_common/src/vaapi_utils_drm.cpp | 608 +++++ .../sample_common/src/vaapi_utils_x11.cpp | 198 ++ .../sample_common/src/vm/atomic.cpp | 19 + .../sample_common/src/vm/atomic_linux.cpp | 62 + .../sample_common/src/vm/shared_object.cpp | 21 + .../src/vm/shared_object_linux.cpp | 41 + .../sample_common/src/vm/thread.cpp | 59 + .../sample_common/src/vm/thread_linux.cpp | 317 +++ .../sample_common/src/vm/thread_windows.cpp | 21 + .../deshuffler/sample_common/src/vm/time.cpp | 21 + .../sample_common/src/vm/time_linux.cpp | 45 + .../deshuffler/sample_common/src/vpp_ex.cpp | 277 ++ vshampor/deshuffler/src/yuv_reader_seek.cpp | 30 + vshampor/deshuffler/unit_tests/CMakeLists.txt | 3 +- .../unit_tests/src/deshuffler_test.cpp | 9 + .../unit_tests/src/yuv_reader_seek_test.cpp | 29 + 153 files changed, 33363 insertions(+), 5 deletions(-) create mode 100644 vshampor/deshuffler/include/yuv_reader_seek.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxastructures.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxaudio++.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxaudio.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxbrc.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxcamera.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxcommon.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxdefs.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxdispatcherprefixedfunctions.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxenc.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxfei.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxfeihevc.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxjpeg.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxla.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxmvc.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxpak.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxplugin++.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxplugin.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxsc.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxscd.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxsession.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxstructures.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxvideo++.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxvideo.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxvp8.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxvp9.h create mode 100644 vshampor/deshuffler/msdk_api/include/mfxvstructures.h create mode 100644 vshampor/deshuffler/msdk_api/mediasdk_structures/ts_ext_buffers_decl.h create mode 100644 vshampor/deshuffler/msdk_api/mediasdk_structures/ts_struct_decl.h create mode 100644 vshampor/deshuffler/msdk_api/mediasdk_structures/ts_typedef.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/CMakeLists.txt create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/intel_api_factory.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_critical_section.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher_defs.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher_log.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dxva2_device.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_exposed_functions_list.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_library_iterator.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_load_dll.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_load_plugin.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_plugin_cfg_parser.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_plugin_hive.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_vector.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_win_reg_key.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfxaudio_exposed_functions_list.h create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/pkg-config-shared.pc.cmake create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/pkg-config.pc.cmake create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/main.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_critical_section.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_critical_section_linux.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dispatcher.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dispatcher_log.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dxva2_device.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_function_table.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_library_iterator.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_library_iterator_linux.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_dll.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_dll_linux.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_plugin.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_cfg_parser.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_hive.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_hive_linux.cpp create mode 100644 vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_win_reg_key.cpp create mode 100644 vshampor/deshuffler/sample_common/CMakeLists.txt create mode 100644 vshampor/deshuffler/sample_common/include/abstract_splitter.h create mode 100644 vshampor/deshuffler/sample_common/include/avc_bitstream.h create mode 100644 vshampor/deshuffler/sample_common/include/avc_headers.h create mode 100644 vshampor/deshuffler/sample_common/include/avc_nal_spl.h create mode 100644 vshampor/deshuffler/sample_common/include/avc_spl.h create mode 100644 vshampor/deshuffler/sample_common/include/avc_structures.h create mode 100644 vshampor/deshuffler/sample_common/include/base_allocator.h create mode 100644 vshampor/deshuffler/sample_common/include/brc_routines.h create mode 100644 vshampor/deshuffler/sample_common/include/current_date.h create mode 100644 vshampor/deshuffler/sample_common/include/d3d11_allocator.h create mode 100644 vshampor/deshuffler/sample_common/include/d3d11_device.h create mode 100644 vshampor/deshuffler/sample_common/include/d3d_allocator.h create mode 100644 vshampor/deshuffler/sample_common/include/d3d_device.h create mode 100644 vshampor/deshuffler/sample_common/include/decode_render.h create mode 100644 vshampor/deshuffler/sample_common/include/general_allocator.h create mode 100644 vshampor/deshuffler/sample_common/include/hw_device.h create mode 100644 vshampor/deshuffler/sample_common/include/intrusive_ptr.h create mode 100644 vshampor/deshuffler/sample_common/include/mfx_buffering.h create mode 100644 vshampor/deshuffler/sample_common/include/mfx_itt_trace.h create mode 100644 vshampor/deshuffler/sample_common/include/mfx_plugin_base.h create mode 100644 vshampor/deshuffler/sample_common/include/mfx_plugin_module.h create mode 100644 vshampor/deshuffler/sample_common/include/mfx_samples_config.h create mode 100644 vshampor/deshuffler/sample_common/include/parameters_dumper.h create mode 100644 vshampor/deshuffler/sample_common/include/plugin_loader.h create mode 100644 vshampor/deshuffler/sample_common/include/plugin_utils.h create mode 100644 vshampor/deshuffler/sample_common/include/sample_defs.h create mode 100644 vshampor/deshuffler/sample_common/include/sample_types.h create mode 100644 vshampor/deshuffler/sample_common/include/sample_utils.h create mode 100644 vshampor/deshuffler/sample_common/include/shared_ptr.h create mode 100644 vshampor/deshuffler/sample_common/include/surface_auto_lock.h create mode 100644 vshampor/deshuffler/sample_common/include/sysmem_allocator.h create mode 100644 vshampor/deshuffler/sample_common/include/time_statistics.h create mode 100644 vshampor/deshuffler/sample_common/include/v4l2_util.h create mode 100644 vshampor/deshuffler/sample_common/include/vaapi_allocator.h create mode 100644 vshampor/deshuffler/sample_common/include/vaapi_device.h create mode 100644 vshampor/deshuffler/sample_common/include/vaapi_utils.h create mode 100644 vshampor/deshuffler/sample_common/include/vaapi_utils_android.h create mode 100644 vshampor/deshuffler/sample_common/include/vaapi_utils_drm.h create mode 100644 vshampor/deshuffler/sample_common/include/vaapi_utils_x11.h create mode 100644 vshampor/deshuffler/sample_common/include/version.h create mode 100644 vshampor/deshuffler/sample_common/include/vm/atomic_defs.h create mode 100644 vshampor/deshuffler/sample_common/include/vm/file_defs.h create mode 100644 vshampor/deshuffler/sample_common/include/vm/so_defs.h create mode 100644 vshampor/deshuffler/sample_common/include/vm/strings_defs.h create mode 100644 vshampor/deshuffler/sample_common/include/vm/thread_defs.h create mode 100644 vshampor/deshuffler/sample_common/include/vm/time_defs.h create mode 100644 vshampor/deshuffler/sample_common/include/vpp_ex.h create mode 100644 vshampor/deshuffler/sample_common/lib/libsample_common.a create mode 100644 vshampor/deshuffler/sample_common/src/avc_bitstream.cpp create mode 100644 vshampor/deshuffler/sample_common/src/avc_nal_spl.cpp create mode 100644 vshampor/deshuffler/sample_common/src/avc_spl.cpp create mode 100644 vshampor/deshuffler/sample_common/src/base_allocator.cpp create mode 100644 vshampor/deshuffler/sample_common/src/brc_routines.cpp create mode 100644 vshampor/deshuffler/sample_common/src/d3d11_allocator.cpp create mode 100644 vshampor/deshuffler/sample_common/src/d3d11_device.cpp create mode 100644 vshampor/deshuffler/sample_common/src/d3d_allocator.cpp create mode 100644 vshampor/deshuffler/sample_common/src/d3d_device.cpp create mode 100644 vshampor/deshuffler/sample_common/src/decode_render.cpp create mode 100644 vshampor/deshuffler/sample_common/src/general_allocator.cpp create mode 100644 vshampor/deshuffler/sample_common/src/mfx_buffering.cpp create mode 100644 vshampor/deshuffler/sample_common/src/parameters_dumper.cpp create mode 100644 vshampor/deshuffler/sample_common/src/plugin_utils.cpp create mode 100644 vshampor/deshuffler/sample_common/src/sample_utils.cpp create mode 100644 vshampor/deshuffler/sample_common/src/sysmem_allocator.cpp create mode 100644 vshampor/deshuffler/sample_common/src/v4l2_util.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vaapi_allocator.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vaapi_device.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vaapi_utils.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vaapi_utils_android.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vaapi_utils_drm.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vaapi_utils_x11.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vm/atomic.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vm/atomic_linux.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vm/shared_object.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vm/shared_object_linux.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vm/thread.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vm/thread_linux.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vm/thread_windows.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vm/time.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vm/time_linux.cpp create mode 100644 vshampor/deshuffler/sample_common/src/vpp_ex.cpp create mode 100644 vshampor/deshuffler/src/yuv_reader_seek.cpp create mode 100644 vshampor/deshuffler/unit_tests/src/deshuffler_test.cpp create mode 100644 vshampor/deshuffler/unit_tests/src/yuv_reader_seek_test.cpp diff --git a/vshampor/deshuffler/.gitignore b/vshampor/deshuffler/.gitignore index 959246a..6deb4d3 100644 --- a/vshampor/deshuffler/.gitignore +++ b/vshampor/deshuffler/.gitignore @@ -15,3 +15,4 @@ compile_commands.json CTestTestfile.cmake lib/* *.pc +/build/ diff --git a/vshampor/deshuffler/CMakeLists.txt b/vshampor/deshuffler/CMakeLists.txt index 511105b..66c1d25 100644 --- a/vshampor/deshuffler/CMakeLists.txt +++ b/vshampor/deshuffler/CMakeLists.txt @@ -12,13 +12,20 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) set(LIBRARY_OUTPUT_PATH lib) include_directories(include) +include_directories(msdk_api/include) +include_directories(sample_common/include) add_subdirectory(unit_tests) +add_subdirectory(sample_common) + add_library(deshuffler STATIC src/deshuffler.cpp src/input_params.cpp src/permutation_data.cpp) +add_dependencies(deshuffler sample_common) +target_link_libraries(deshuffler sample_common) + add_executable(deshuffler_cl src/main.cpp) add_dependencies(deshuffler_cl deshuffler) target_link_libraries(deshuffler_cl deshuffler) diff --git a/vshampor/deshuffler/include/deshuffler.h b/vshampor/deshuffler/include/deshuffler.h index 1aa56f9..7a32859 100644 --- a/vshampor/deshuffler/include/deshuffler.h +++ b/vshampor/deshuffler/include/deshuffler.h @@ -3,20 +3,23 @@ #include "permutation_data.h" #include "input_params.h" +#include "yuv_reader_seek.h" +#include class Deshuffler { public: Deshuffler() = default; - Deshuffler(const InputParams& _params) : params(_params) {} + Deshuffler(const InputParams& params) : m_params(params) {} void CalculatePermutation(); void OutputPermutation(); void ReconstructStream(); void OutputStream(); private: - InputParams params; - PermutationData permutation_data; - + InputParams m_params; + PermutationData m_permutation_data; + YUVReaderSeek m_YUVReaderSeek; + CSmplYUVWriter m_YUVWriter; }; #endif /* DESHUFFLER_H_ */ diff --git a/vshampor/deshuffler/include/yuv_reader_seek.h b/vshampor/deshuffler/include/yuv_reader_seek.h new file mode 100644 index 0000000..04732f6 --- /dev/null +++ b/vshampor/deshuffler/include/yuv_reader_seek.h @@ -0,0 +1,15 @@ +#ifndef SRC_YUV_READER_SEEK_H_ +#define SRC_YUV_READER_SEEK_H_ + +#include +#include + +class YUVReaderSeek: public CSmplYUVReader +{ +public: + YUVReaderSeek(mfxU32 frame_number = 0); + virtual mfxStatus Init(std::string filename, mfxU32 ColorFormat) override; + mfxStatus Seek(mfxU32 frame_number); +}; + +#endif /* SRC_YUV_READER_SEEK_H_ */ diff --git a/vshampor/deshuffler/msdk_api/include/mfxastructures.h b/vshampor/deshuffler/msdk_api/include/mfxastructures.h new file mode 100644 index 0000000..eeb5e02 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxastructures.h @@ -0,0 +1,162 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXASTRUCTURES_H__ +#define __MFXASTRUCTURES_H__ +#include "mfxcommon.h" + +#if !defined (__GNUC__) +#pragma warning(disable: 4201) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* CodecId */ +enum { + MFX_CODEC_AAC =MFX_MAKEFOURCC('A','A','C',' '), + MFX_CODEC_MP3 =MFX_MAKEFOURCC('M','P','3',' ') +}; + +enum { + /* AAC Profiles & Levels */ + MFX_PROFILE_AAC_LC =2, + MFX_PROFILE_AAC_LTP =4, + MFX_PROFILE_AAC_MAIN =1, + MFX_PROFILE_AAC_SSR =3, + MFX_PROFILE_AAC_HE =5, + MFX_PROFILE_AAC_ALS =0x20, + MFX_PROFILE_AAC_BSAC =22, + MFX_PROFILE_AAC_PS =29, + + /*MPEG AUDIO*/ + MFX_AUDIO_MPEG1_LAYER1 =0x00000110, + MFX_AUDIO_MPEG1_LAYER2 =0x00000120, + MFX_AUDIO_MPEG1_LAYER3 =0x00000140, + MFX_AUDIO_MPEG2_LAYER1 =0x00000210, + MFX_AUDIO_MPEG2_LAYER2 =0x00000220, + MFX_AUDIO_MPEG2_LAYER3 =0x00000240 +}; + +/*AAC HE decoder down sampling*/ +enum { + MFX_AUDIO_AAC_HE_DWNSMPL_OFF=0, + MFX_AUDIO_AAC_HE_DWNSMPL_ON= 1 +}; + +/* AAC decoder support of PS */ +enum { + MFX_AUDIO_AAC_PS_DISABLE= 0, + MFX_AUDIO_AAC_PS_PARSER= 1, + MFX_AUDIO_AAC_PS_ENABLE_BL= 111, + MFX_AUDIO_AAC_PS_ENABLE_UR= 411 +}; + +/*AAC decoder SBR support*/ +enum { + MFX_AUDIO_AAC_SBR_DISABLE = 0, + MFX_AUDIO_AAC_SBR_ENABLE= 1, + MFX_AUDIO_AAC_SBR_UNDEF= 2 +}; + +/*AAC header type*/ +enum{ + MFX_AUDIO_AAC_ADTS= 1, + MFX_AUDIO_AAC_ADIF= 2, + MFX_AUDIO_AAC_RAW= 3, +}; + +/*AAC encoder stereo mode*/ +enum +{ + MFX_AUDIO_AAC_MONO= 0, + MFX_AUDIO_AAC_LR_STEREO= 1, + MFX_AUDIO_AAC_MS_STEREO= 2, + MFX_AUDIO_AAC_JOINT_STEREO= 3 +}; + +typedef struct { + mfxU32 CodecId; + mfxU16 CodecProfile; + mfxU16 CodecLevel; + + mfxU32 Bitrate; + mfxU32 SampleFrequency; + mfxU16 NumChannel; + mfxU16 BitPerSample; + + mfxU16 reserved1[22]; + + union { + struct { /* AAC Decoding Options */ + mfxU16 FlagPSSupportLev; + mfxU16 Layer; + mfxU16 AACHeaderDataSize; + mfxU8 AACHeaderData[64]; + }; + struct { /* AAC Encoding Options */ + mfxU16 OutputFormat; + mfxU16 StereoMode; + mfxU16 reserved2[61]; + }; + }; +} mfxAudioInfoMFX; + +typedef struct { + mfxU16 AsyncDepth; + mfxU16 Protected; + mfxU16 reserved[14]; + + mfxAudioInfoMFX mfx; + mfxExtBuffer** ExtParam; + mfxU16 NumExtParam; +} mfxAudioParam; + +typedef struct { + mfxU32 SuggestedInputSize; + mfxU32 SuggestedOutputSize; + mfxU32 reserved[6]; +} mfxAudioAllocRequest; + +typedef struct { + mfxU64 TimeStamp; /* 1/90KHz */ + mfxU16 Locked; + mfxU16 NumChannels; + mfxU32 SampleFrequency; + mfxU16 BitPerSample; + mfxU16 reserved1[7]; + + mfxU8* Data; + mfxU32 reserved2; + mfxU32 DataLength; + mfxU32 MaxLength; + + mfxU32 NumExtParam; + mfxExtBuffer **ExtParam; +} mfxAudioFrame; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + + diff --git a/vshampor/deshuffler/msdk_api/include/mfxaudio++.h b/vshampor/deshuffler/msdk_api/include/mfxaudio++.h new file mode 100644 index 0000000..1abbf11 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxaudio++.h @@ -0,0 +1,101 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXAUDIOPLUSPLUS_H +#define __MFXAUDIOPLUSPLUS_H + +#include "mfxaudio.h" + +class MFXAudioSession +{ +public: + MFXAudioSession(void) { m_session = (mfxSession) 0; } + virtual ~MFXAudioSession(void) { Close(); } + + virtual mfxStatus Init(mfxIMPL impl, mfxVersion *ver) { return MFXInit(impl, ver, &m_session); } + virtual mfxStatus Close(void) + { + mfxStatus mfxRes; + mfxRes = MFXClose(m_session); m_session = (mfxSession) 0; + return mfxRes; + } + + virtual mfxStatus QueryIMPL(mfxIMPL *impl) { return MFXQueryIMPL(m_session, impl); } + virtual mfxStatus QueryVersion(mfxVersion *version) { return MFXQueryVersion(m_session, version); } + + virtual mfxStatus JoinSession(mfxSession child_session) { return MFXJoinSession(m_session, child_session);} + virtual mfxStatus DisjoinSession( ) { return MFXDisjoinSession(m_session);} + virtual mfxStatus CloneSession( mfxSession *clone) { return MFXCloneSession(m_session, clone);} + virtual mfxStatus SetPriority( mfxPriority priority) { return MFXSetPriority(m_session, priority);} + virtual mfxStatus GetPriority( mfxPriority *priority) { return MFXGetPriority(m_session, priority);} + + virtual mfxStatus SyncOperation(mfxSyncPoint syncp, mfxU32 wait) { return MFXAudioCORE_SyncOperation(m_session, syncp, wait); } + + virtual operator mfxSession (void) { return m_session; } + +protected: + + mfxSession m_session; // (mfxSession) handle to the owning session +}; + + +class MFXAudioDECODE +{ +public: + + MFXAudioDECODE(mfxSession session) { m_session = session; } + virtual ~MFXAudioDECODE(void) { Close(); } + + virtual mfxStatus Query(mfxAudioParam *in, mfxAudioParam *out) { return MFXAudioDECODE_Query(m_session, in, out); } + virtual mfxStatus DecodeHeader(mfxBitstream *bs, mfxAudioParam *par) { return MFXAudioDECODE_DecodeHeader(m_session, bs, par); } + virtual mfxStatus QueryIOSize(mfxAudioParam *par, mfxAudioAllocRequest *request) { return MFXAudioDECODE_QueryIOSize(m_session, par, request); } + virtual mfxStatus Init(mfxAudioParam *par) { return MFXAudioDECODE_Init(m_session, par); } + virtual mfxStatus Reset(mfxAudioParam *par) { return MFXAudioDECODE_Reset(m_session, par); } + virtual mfxStatus Close(void) { return MFXAudioDECODE_Close(m_session); } + virtual mfxStatus GetAudioParam(mfxAudioParam *par) { return MFXAudioDECODE_GetAudioParam(m_session, par); } + virtual mfxStatus DecodeFrameAsync(mfxBitstream *bs, mfxAudioFrame *frame, mfxSyncPoint *syncp) { return MFXAudioDECODE_DecodeFrameAsync(m_session, bs, frame, syncp); } + + +protected: + + mfxSession m_session; // (mfxSession) handle to the owning session +}; + + +class MFXAudioENCODE +{ +public: + + MFXAudioENCODE(mfxSession session) { m_session = session; } + virtual ~MFXAudioENCODE(void) { Close(); } + + virtual mfxStatus Query(mfxAudioParam *in, mfxAudioParam *out) { return MFXAudioENCODE_Query(m_session, in, out); } + virtual mfxStatus QueryIOSize(mfxAudioParam *par, mfxAudioAllocRequest *request) { return MFXAudioENCODE_QueryIOSize(m_session, par, request); } + virtual mfxStatus Init(mfxAudioParam *par) { return MFXAudioENCODE_Init(m_session, par); } + virtual mfxStatus Reset(mfxAudioParam *par) { return MFXAudioENCODE_Reset(m_session, par); } + virtual mfxStatus Close(void) { return MFXAudioENCODE_Close(m_session); } + virtual mfxStatus GetAudioParam(mfxAudioParam *par) { return MFXAudioENCODE_GetAudioParam(m_session, par); } + virtual mfxStatus EncodeFrameAsync(mfxAudioFrame *frame, mfxBitstream *buffer_out, mfxSyncPoint *syncp) { return MFXAudioENCODE_EncodeFrameAsync(m_session, frame, buffer_out, syncp); } + +protected: + + mfxSession m_session; // (mfxSession) handle to the owning session +}; + +#endif \ No newline at end of file diff --git a/vshampor/deshuffler/msdk_api/include/mfxaudio.h b/vshampor/deshuffler/msdk_api/include/mfxaudio.h new file mode 100644 index 0000000..fd5872f --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxaudio.h @@ -0,0 +1,60 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef __MFXAUDIO_H__ +#define __MFXAUDIO_H__ +#include "mfxsession.h" +#include "mfxastructures.h" + +#define MFX_AUDIO_VERSION_MAJOR 1 +#define MFX_AUDIO_VERSION_MINOR 15 + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* AudioCORE */ +mfxStatus MFX_CDECL MFXAudioCORE_SyncOperation(mfxSession session, mfxSyncPoint syncp, mfxU32 wait); + +/* AudioENCODE */ +mfxStatus MFX_CDECL MFXAudioENCODE_Query(mfxSession session, mfxAudioParam *in, mfxAudioParam *out); +mfxStatus MFX_CDECL MFXAudioENCODE_QueryIOSize(mfxSession session, mfxAudioParam *par, mfxAudioAllocRequest *request); +mfxStatus MFX_CDECL MFXAudioENCODE_Init(mfxSession session, mfxAudioParam *par); +mfxStatus MFX_CDECL MFXAudioENCODE_Reset(mfxSession session, mfxAudioParam *par); +mfxStatus MFX_CDECL MFXAudioENCODE_Close(mfxSession session); +mfxStatus MFX_CDECL MFXAudioENCODE_GetAudioParam(mfxSession session, mfxAudioParam *par); +mfxStatus MFX_CDECL MFXAudioENCODE_EncodeFrameAsync(mfxSession session, mfxAudioFrame *frame, mfxBitstream *bs, mfxSyncPoint *syncp); + +/* AudioDECODE */ +mfxStatus MFX_CDECL MFXAudioDECODE_Query(mfxSession session, mfxAudioParam *in, mfxAudioParam *out); +mfxStatus MFX_CDECL MFXAudioDECODE_DecodeHeader(mfxSession session, mfxBitstream *bs, mfxAudioParam* par); +mfxStatus MFX_CDECL MFXAudioDECODE_Init(mfxSession session, mfxAudioParam *par); +mfxStatus MFX_CDECL MFXAudioDECODE_Reset(mfxSession session, mfxAudioParam *par); +mfxStatus MFX_CDECL MFXAudioDECODE_Close(mfxSession session); +mfxStatus MFX_CDECL MFXAudioDECODE_QueryIOSize(mfxSession session, mfxAudioParam *par, mfxAudioAllocRequest *request); +mfxStatus MFX_CDECL MFXAudioDECODE_GetAudioParam(mfxSession session, mfxAudioParam *par); +mfxStatus MFX_CDECL MFXAudioDECODE_DecodeFrameAsync(mfxSession session, mfxBitstream *bs, mfxAudioFrame *frame, mfxSyncPoint *syncp); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/vshampor/deshuffler/msdk_api/include/mfxbrc.h b/vshampor/deshuffler/msdk_api/include/mfxbrc.h new file mode 100644 index 0000000..527d1bf --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxbrc.h @@ -0,0 +1,110 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXBRC_H__ +#define __MFXBRC_H__ + +#include "mfxvstructures.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* Extended Buffer Ids */ +enum { + MFX_EXTBUFF_BRC = MFX_MAKEFOURCC('E','B','R','C') +}; + +typedef struct { +#if (MFX_VERSION >= 1026) + mfxU32 reserved[23]; + mfxU16 SceneChange; // Frame is Scene Chg frame + mfxU16 LongTerm; // Frame is long term refrence + mfxU32 FrameCmplx; // Frame Complexity +#else + mfxU32 reserved[25]; +#endif + mfxU32 EncodedOrder; // Frame number in a sequence of reordered frames starting from encoder Init() + mfxU32 DisplayOrder; // Frame number in a sequence of frames in display order starting from last IDR + mfxU32 CodedFrameSize; // Size of frame in bytes after encoding + mfxU16 FrameType; // See FrameType enumerator + mfxU16 PyramidLayer; // B-pyramid or P-pyramid layer, frame belongs to + mfxU16 NumRecode; // Number of recodings performed for this frame + mfxU16 NumExtParam; + mfxExtBuffer** ExtParam; +} mfxBRCFrameParam; + +typedef struct { + mfxI32 QpY; // Frame-level Luma QP + mfxU32 reserved1[13]; + mfxHDL reserved2; +} mfxBRCFrameCtrl; + +/* BRCStatus */ +enum { + MFX_BRC_OK = 0, // CodedFrameSize is acceptable, no further recoding/padding/skip required + MFX_BRC_BIG_FRAME = 1, // Coded frame is too big, recoding required + MFX_BRC_SMALL_FRAME = 2, // Coded frame is too small, recoding required + MFX_BRC_PANIC_BIG_FRAME = 3, // Coded frame is too big, no further recoding possible - skip frame + MFX_BRC_PANIC_SMALL_FRAME = 4 // Coded frame is too small, no further recoding possible - required padding to MinFrameSize +}; + +typedef struct { + mfxU32 MinFrameSize; // Size in bytes, coded frame must be padded to when Status = MFX_BRC_PANIC_SMALL_FRAME + mfxU16 BRCStatus; // See BRCStatus enumerator + mfxU16 reserved[25]; + mfxHDL reserved1; +} mfxBRCFrameStatus; + +/* Structure contains set of callbacks to perform external bit-rate control. +Can be attached to mfxVideoParam structure during encoder initialization. +Turn mfxExtCodingOption2::ExtBRC option ON to make encoder use external BRC instead of native one. */ +typedef struct { + mfxExtBuffer Header; + + mfxU32 reserved[14]; + mfxHDL pthis; // Pointer to user-defined BRC instance. Will be passed to each mfxExtBRC callback. + + // Initialize BRC. Will be invoked during encoder Init(). In - pthis, par. + mfxStatus (MFX_CDECL *Init) (mfxHDL pthis, mfxVideoParam* par); + + // Reset BRC. Will be invoked during encoder Reset(). In - pthis, par. + mfxStatus (MFX_CDECL *Reset) (mfxHDL pthis, mfxVideoParam* par); + + // Close BRC. Will be invoked during encoder Close(). In - pthis. + mfxStatus (MFX_CDECL *Close) (mfxHDL pthis); + + // Obtain from BRC controls required for frame encoding. + // Will be invoked BEFORE encoding of each frame. In - pthis, par; Out - ctrl. + mfxStatus (MFX_CDECL *GetFrameCtrl) (mfxHDL pthis, mfxBRCFrameParam* par, mfxBRCFrameCtrl* ctrl); + + // Update BRC state and return command to continue/recode frame/do padding/skip frame. + // Will be invoked AFTER encoding of each frame. In - pthis, par, ctrl; Out - status. + mfxStatus (MFX_CDECL *Update) (mfxHDL pthis, mfxBRCFrameParam* par, mfxBRCFrameCtrl* ctrl, mfxBRCFrameStatus* status); + + mfxHDL reserved1[10]; +} mfxExtBRC; + +#ifdef __cplusplus +} // extern "C" +#endif /* __cplusplus */ + +#endif + diff --git a/vshampor/deshuffler/msdk_api/include/mfxcamera.h b/vshampor/deshuffler/msdk_api/include/mfxcamera.h new file mode 100644 index 0000000..d4a5edd --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxcamera.h @@ -0,0 +1,233 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#include "mfxcommon.h" + +#if !defined (__GNUC__) +#pragma warning(disable: 4201) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Camera Extended Buffer Ids */ +enum { + MFX_EXTBUF_CAM_GAMMA_CORRECTION = MFX_MAKEFOURCC('C','G','A','M'), + MFX_EXTBUF_CAM_WHITE_BALANCE = MFX_MAKEFOURCC('C','W','B','L'), + MFX_EXTBUF_CAM_HOT_PIXEL_REMOVAL = MFX_MAKEFOURCC('C','H','P','R'), + MFX_EXTBUF_CAM_BLACK_LEVEL_CORRECTION = MFX_MAKEFOURCC('C','B','L','C'), + MFX_EXTBUF_CAM_VIGNETTE_CORRECTION = MFX_MAKEFOURCC('C','V','G','T'), + MFX_EXTBUF_CAM_BAYER_DENOISE = MFX_MAKEFOURCC('C','D','N','S'), + MFX_EXTBUF_CAM_COLOR_CORRECTION_3X3 = MFX_MAKEFOURCC('C','C','3','3'), + MFX_EXTBUF_CAM_PADDING = MFX_MAKEFOURCC('C','P','A','D'), + MFX_EXTBUF_CAM_PIPECONTROL = MFX_MAKEFOURCC('C','P','P','C'), + MFX_EXTBUF_CAM_FORWARD_GAMMA_CORRECTION = MFX_MAKEFOURCC('C','F','G','C'), + MFX_EXTBUF_CAM_LENS_GEOM_DIST_CORRECTION = MFX_MAKEFOURCC('C','L','G','D'), + MFX_EXTBUF_CAM_3DLUT = MFX_MAKEFOURCC('C','L','U','T'), + MFX_EXTBUF_CAM_TOTAL_COLOR_CONTROL = MFX_MAKEFOURCC('C','T','C','C'), + MFX_EXTBUF_CAM_CSC_YUV_RGB = MFX_MAKEFOURCC('C','C','Y','R') +}; + +typedef enum { + MFX_CAM_GAMMA_VALUE = 0x0001, + MFX_CAM_GAMMA_LUT = 0x0002, +} mfxCamGammaParam; + +typedef struct { + mfxExtBuffer Header; + mfxU16 Mode; + mfxU16 reserved1; + mfxF64 GammaValue; + + mfxU16 reserved2[3]; + mfxU16 NumPoints; + mfxU16 GammaPoint[1024]; + mfxU16 GammaCorrected[1024]; + mfxU32 reserved3[4]; +} mfxExtCamGammaCorrection; + +typedef enum { + MFX_CAM_WHITE_BALANCE_MANUAL = 0x0001, + MFX_CAM_WHITE_BALANCE_AUTO = 0x0002 +} mfxCamWhiteBalanceMode; + +typedef struct { + mfxExtBuffer Header; + mfxU32 Mode; + mfxF64 R; + mfxF64 G0; + mfxF64 B; + mfxF64 G1; + mfxU32 reserved[8]; +} mfxExtCamWhiteBalance; + +typedef struct { + mfxExtBuffer Header; + mfxU16 R; + mfxU16 G; + mfxU16 B; + mfxU16 C; + mfxU16 M; + mfxU16 Y; + mfxU16 reserved[6]; +} mfxExtCamTotalColorControl; + +typedef struct { + mfxExtBuffer Header; + + mfxF32 PreOffset[3]; + mfxF32 Matrix[3][3]; + mfxF32 PostOffset[3]; + mfxU16 reserved[30]; +} mfxExtCamCscYuvRgb; + +typedef struct { + mfxExtBuffer Header; + mfxU16 PixelThresholdDifference; + mfxU16 PixelCountThreshold; +} mfxExtCamHotPixelRemoval; + +typedef struct { + mfxExtBuffer Header; + mfxU16 R; + mfxU16 G0; + mfxU16 B; + mfxU16 G1; + mfxU32 reserved[4]; +} mfxExtCamBlackLevelCorrection; + +typedef struct { + mfxU8 integer; + mfxU8 mantissa; +} mfxCamVignetteCorrectionElement; + +typedef struct { + mfxCamVignetteCorrectionElement R; + mfxCamVignetteCorrectionElement G0; + mfxCamVignetteCorrectionElement B; + mfxCamVignetteCorrectionElement G1; +} mfxCamVignetteCorrectionParam; + +typedef struct { + mfxExtBuffer Header; + + mfxU32 Width; + mfxU32 Height; + mfxU32 Pitch; + mfxU32 reserved[7]; + + mfxCamVignetteCorrectionParam *CorrectionMap; + +} mfxExtCamVignetteCorrection; + +typedef struct { + mfxExtBuffer Header; + mfxU16 Threshold; + mfxU16 reserved[27]; +} mfxExtCamBayerDenoise; + +typedef struct { + mfxExtBuffer Header; + mfxF64 CCM[3][3]; + mfxU32 reserved[4]; +} mfxExtCamColorCorrection3x3; + +typedef struct { + mfxExtBuffer Header; + mfxU16 Top; + mfxU16 Bottom; + mfxU16 Left; + mfxU16 Right; + mfxU32 reserved[4]; +} mfxExtCamPadding; + +typedef enum { + MFX_CAM_BAYER_BGGR = 0x0000, + MFX_CAM_BAYER_RGGB = 0x0001, + MFX_CAM_BAYER_GBRG = 0x0002, + MFX_CAM_BAYER_GRBG = 0x0003 +} mfxCamBayerFormat; + +typedef struct { + mfxExtBuffer Header; + mfxU16 RawFormat; + mfxU16 reserved1; + mfxU32 reserved[5]; +} mfxExtCamPipeControl; + +typedef struct{ + mfxU16 Pixel; + mfxU16 Red; + mfxU16 Green; + mfxU16 Blue; +} mfxCamFwdGammaSegment; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 reserved[19]; + mfxU16 NumSegments; + union { + mfxCamFwdGammaSegment* Segment; + mfxU64 reserved1; + }; +} mfxExtCamFwdGamma; + +typedef struct { + mfxExtBuffer Header; + + mfxF32 a[3]; // [R, G, B] + mfxF32 b[3]; // [R, G, B] + mfxF32 c[3]; // [R, G, B] + mfxF32 d[3]; // [R, G, B] + mfxU16 reserved[36]; +} mfxExtCamLensGeomDistCorrection; + +/* LUTSize */ +enum { + MFX_CAM_3DLUT17_SIZE = (17 * 17 * 17), + MFX_CAM_3DLUT33_SIZE = (33 * 33 * 33), + MFX_CAM_3DLUT65_SIZE = (65 * 65 * 65) +}; + +typedef struct { + mfxU16 R; + mfxU16 G; + mfxU16 B; + mfxU16 Reserved; +} mfxCam3DLutEntry; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 reserved[10]; + mfxU32 Size; + union + { + mfxCam3DLutEntry* Table; + mfxU64 reserved1; + }; +} mfxExtCam3DLut; + +#ifdef __cplusplus +} // extern "C" +#endif + + diff --git a/vshampor/deshuffler/msdk_api/include/mfxcommon.h b/vshampor/deshuffler/msdk_api/include/mfxcommon.h new file mode 100644 index 0000000..8b1cf83 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxcommon.h @@ -0,0 +1,176 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXCOMMON_H__ +#define __MFXCOMMON_H__ +#include "mfxdefs.h" + +#if !defined (__GNUC__) +#pragma warning(disable: 4201) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#define MFX_MAKEFOURCC(A,B,C,D) ((((int)A))+(((int)B)<<8)+(((int)C)<<16)+(((int)D)<<24)) + +/* Extended Configuration Header Structure */ +typedef struct { + mfxU32 BufferId; + mfxU32 BufferSz; +} mfxExtBuffer; + +/* Library initialization and deinitialization */ +typedef mfxI32 mfxIMPL; +#define MFX_IMPL_BASETYPE(x) (0x00ff & (x)) + +enum { + MFX_IMPL_AUTO = 0x0000, /* Auto Selection/In or Not Supported/Out */ + MFX_IMPL_SOFTWARE = 0x0001, /* Pure Software Implementation */ + MFX_IMPL_HARDWARE = 0x0002, /* Hardware Accelerated Implementation (default device) */ + MFX_IMPL_AUTO_ANY = 0x0003, /* Auto selection of any hardware/software implementation */ + MFX_IMPL_HARDWARE_ANY = 0x0004, /* Auto selection of any hardware implementation */ + MFX_IMPL_HARDWARE2 = 0x0005, /* Hardware accelerated implementation (2nd device) */ + MFX_IMPL_HARDWARE3 = 0x0006, /* Hardware accelerated implementation (3rd device) */ + MFX_IMPL_HARDWARE4 = 0x0007, /* Hardware accelerated implementation (4th device) */ + MFX_IMPL_RUNTIME = 0x0008, + MFX_IMPL_VIA_ANY = 0x0100, + MFX_IMPL_VIA_D3D9 = 0x0200, + MFX_IMPL_VIA_D3D11 = 0x0300, + MFX_IMPL_VIA_VAAPI = 0x0400, + + MFX_IMPL_AUDIO = 0x8000, + + MFX_IMPL_UNSUPPORTED = 0x0000 /* One of the MFXQueryIMPL returns */ +}; + +/* Version Info */ +typedef union { + struct { + mfxU16 Minor; + mfxU16 Major; + }; + mfxU32 Version; +} mfxVersion; + +/* session priority */ +typedef enum +{ + MFX_PRIORITY_LOW = 0, + MFX_PRIORITY_NORMAL = 1, + MFX_PRIORITY_HIGH = 2 + +} mfxPriority; + +typedef struct _mfxEncryptedData mfxEncryptedData; +typedef struct { + union { + struct { + mfxEncryptedData* EncryptedData; + mfxExtBuffer **ExtParam; + mfxU16 NumExtParam; + }; + mfxU32 reserved[6]; + }; + mfxI64 DecodeTimeStamp; + mfxU64 TimeStamp; + mfxU8* Data; + mfxU32 DataOffset; + mfxU32 DataLength; + mfxU32 MaxLength; + + mfxU16 PicStruct; + mfxU16 FrameType; + mfxU16 DataFlag; + mfxU16 reserved2; +} mfxBitstream; + +typedef struct _mfxSyncPoint *mfxSyncPoint; + +/* GPUCopy */ +enum { + MFX_GPUCOPY_DEFAULT = 0, + MFX_GPUCOPY_ON = 1, + MFX_GPUCOPY_OFF = 2 +}; + +typedef struct { + mfxIMPL Implementation; + mfxVersion Version; + mfxU16 ExternalThreads; + union { + struct { + mfxExtBuffer **ExtParam; + mfxU16 NumExtParam; + }; + mfxU16 reserved2[5]; + }; + mfxU16 GPUCopy; + mfxU16 reserved[21]; +} mfxInitParam; + +enum { + MFX_EXTBUFF_THREADS_PARAM = MFX_MAKEFOURCC('T','H','D','P') +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 NumThread; + mfxI32 SchedulingType; + mfxI32 Priority; + mfxU16 reserved[55]; +} mfxExtThreadsParam; + +/* PlatformCodeName */ +enum { + MFX_PLATFORM_UNKNOWN = 0, + MFX_PLATFORM_SANDYBRIDGE = 1, + MFX_PLATFORM_IVYBRIDGE = 2, + MFX_PLATFORM_HASWELL = 3, + MFX_PLATFORM_BAYTRAIL = 4, + MFX_PLATFORM_BROADWELL = 5, + MFX_PLATFORM_CHERRYTRAIL = 6, + MFX_PLATFORM_SKYLAKE = 7, + MFX_PLATFORM_APOLLOLAKE = 8, + MFX_PLATFORM_KABYLAKE = 9, +#if (MFX_VERSION >= 1025) + MFX_PLATFORM_GEMINILAKE = 10, + MFX_PLATFORM_COFFEELAKE = 11, + MFX_PLATFORM_CANNONLAKE = 20, +#endif +#if (MFX_VERSION >= 1027) + MFX_PLATFORM_ICELAKE = 30, +#endif +}; + +typedef struct { + mfxU16 CodeName; + mfxU16 DeviceId; + mfxU16 reserved[14]; +} mfxPlatform; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/vshampor/deshuffler/msdk_api/include/mfxdefs.h b/vshampor/deshuffler/msdk_api/include/mfxdefs.h new file mode 100644 index 0000000..eee2116 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxdefs.h @@ -0,0 +1,158 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXDEFS_H__ +#define __MFXDEFS_H__ + +#define MFX_VERSION_MAJOR 1 +#define MFX_VERSION_MINOR 27 + +// MFX_VERSION_NEXT is always +1 from last public release +// may be enforced by MFX_VERSION_USE_LATEST define +// if MFX_VERSION_USE_LATEST is defined MFX_VERSION is ignored + +#define MFX_VERSION_NEXT (MFX_VERSION_MAJOR * 1000 + MFX_VERSION_MINOR + 1) + +// MFX_VERSION - version of API that 'assumed' by build may be provided externally +// if it omitted then latest stable API derived from Major.Minor is assumed + + +#if !defined(MFX_VERSION) + #if defined(MFX_VERSION_USE_LATEST) + #define MFX_VERSION MFX_VERSION_NEXT + #else + #define MFX_VERSION (MFX_VERSION_MAJOR * 1000 + MFX_VERSION_MINOR) + #endif +#else + #undef MFX_VERSION_MINOR + #define MFX_VERSION_MINOR ((MFX_VERSION) % 1000) +#endif + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + + #define __INT64 long long + #define __UINT64 unsigned long long + + #define MFX_CDECL + #define MFX_STDCALL + +#define MFX_INFINITE 0xFFFFFFFF + +typedef unsigned char mfxU8; +typedef char mfxI8; +typedef short mfxI16; +typedef unsigned short mfxU16; +typedef unsigned int mfxU32; +typedef int mfxI32; +typedef unsigned int mfxUL32; +typedef int mfxL32; +typedef float mfxF32; +typedef double mfxF64; +typedef __UINT64 mfxU64; +typedef __INT64 mfxI64; +typedef void* mfxHDL; +typedef mfxHDL mfxMemId; +typedef void* mfxThreadTask; +typedef char mfxChar; + +typedef struct { + mfxI16 x; + mfxI16 y; +} mfxI16Pair; + +typedef struct { + mfxHDL first; + mfxHDL second; +} mfxHDLPair; + + +/*********************************************************************************\ +Error message +\*********************************************************************************/ +typedef enum +{ + /* no error */ + MFX_ERR_NONE = 0, /* no error */ + + /* reserved for unexpected errors */ + MFX_ERR_UNKNOWN = -1, /* unknown error. */ + + /* error codes <0 */ + MFX_ERR_NULL_PTR = -2, /* null pointer */ + MFX_ERR_UNSUPPORTED = -3, /* undeveloped feature */ + MFX_ERR_MEMORY_ALLOC = -4, /* failed to allocate memory */ + MFX_ERR_NOT_ENOUGH_BUFFER = -5, /* insufficient buffer at input/output */ + MFX_ERR_INVALID_HANDLE = -6, /* invalid handle */ + MFX_ERR_LOCK_MEMORY = -7, /* failed to lock the memory block */ + MFX_ERR_NOT_INITIALIZED = -8, /* member function called before initialization */ + MFX_ERR_NOT_FOUND = -9, /* the specified object is not found */ + MFX_ERR_MORE_DATA = -10, /* expect more data at input */ + MFX_ERR_MORE_SURFACE = -11, /* expect more surface at output */ + MFX_ERR_ABORTED = -12, /* operation aborted */ + MFX_ERR_DEVICE_LOST = -13, /* lose the HW acceleration device */ + MFX_ERR_INCOMPATIBLE_VIDEO_PARAM = -14, /* incompatible video parameters */ + MFX_ERR_INVALID_VIDEO_PARAM = -15, /* invalid video parameters */ + MFX_ERR_UNDEFINED_BEHAVIOR = -16, /* undefined behavior */ + MFX_ERR_DEVICE_FAILED = -17, /* device operation failure */ + MFX_ERR_MORE_BITSTREAM = -18, /* expect more bitstream buffers at output */ + MFX_ERR_INCOMPATIBLE_AUDIO_PARAM = -19, /* incompatible audio parameters */ + MFX_ERR_INVALID_AUDIO_PARAM = -20, /* invalid audio parameters */ + MFX_ERR_GPU_HANG = -21, /* device operation failure caused by GPU hang */ + MFX_ERR_REALLOC_SURFACE = -22, /* bigger output surface required */ + + /* warnings >0 */ + MFX_WRN_IN_EXECUTION = 1, /* the previous asynchronous operation is in execution */ + MFX_WRN_DEVICE_BUSY = 2, /* the HW acceleration device is busy */ + MFX_WRN_VIDEO_PARAM_CHANGED = 3, /* the video parameters are changed during decoding */ + MFX_WRN_PARTIAL_ACCELERATION = 4, /* SW is used */ + MFX_WRN_INCOMPATIBLE_VIDEO_PARAM = 5, /* incompatible video parameters */ + MFX_WRN_VALUE_NOT_CHANGED = 6, /* the value is saturated based on its valid range */ + MFX_WRN_OUT_OF_RANGE = 7, /* the value is out of valid range */ + MFX_WRN_FILTER_SKIPPED = 10, /* one of requested filters has been skipped */ + MFX_WRN_INCOMPATIBLE_AUDIO_PARAM = 11, /* incompatible audio parameters */ + + /* threading statuses */ + MFX_TASK_DONE = MFX_ERR_NONE, /* task has been completed */ + MFX_TASK_WORKING = 8, /* there is some more work to do */ + MFX_TASK_BUSY = 9, /* task is waiting for resources */ + + /* plug-in statuses */ + MFX_ERR_MORE_DATA_SUBMIT_TASK = -10000, /* return MFX_ERR_MORE_DATA but submit internal asynchronous task */ + +} mfxStatus; + + +// Application +#if defined(MFX_DISPATCHER_EXPOSED_PREFIX) + +#include "mfxdispatcherprefixedfunctions.h" + +#endif // MFX_DISPATCHER_EXPOSED_PREFIX + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __MFXDEFS_H__ */ diff --git a/vshampor/deshuffler/msdk_api/include/mfxdispatcherprefixedfunctions.h b/vshampor/deshuffler/msdk_api/include/mfxdispatcherprefixedfunctions.h new file mode 100644 index 0000000..f823793 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxdispatcherprefixedfunctions.h @@ -0,0 +1,145 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +#ifndef __MFXDISPATCHERPREFIXEDFUNCTIONS_H__ +#define __MFXDISPATCHERPREFIXEDFUNCTIONS_H__ + +// API 1.0 functions +#define MFXInit disp_MFXInit +#define MFXClose disp_MFXClose +#define MFXQueryIMPL disp_MFXQueryIMPL +#define MFXQueryVersion disp_MFXQueryVersion + +#define MFXJoinSession disp_MFXJoinSession +#define MFXDisjoinSession disp_MFXDisjoinSession +#define MFXCloneSession disp_MFXCloneSession +#define MFXSetPriority disp_MFXSetPriority +#define MFXGetPriority disp_MFXGetPriority + +#define MFXVideoCORE_SetBufferAllocator disp_MFXVideoCORE_SetBufferAllocator +#define MFXVideoCORE_SetFrameAllocator disp_MFXVideoCORE_SetFrameAllocator +#define MFXVideoCORE_SetHandle disp_MFXVideoCORE_SetHandle +#define MFXVideoCORE_GetHandle disp_MFXVideoCORE_GetHandle +#define MFXVideoCORE_SyncOperation disp_MFXVideoCORE_SyncOperation + +#define MFXVideoENCODE_Query disp_MFXVideoENCODE_Query +#define MFXVideoENCODE_QueryIOSurf disp_MFXVideoENCODE_QueryIOSurf +#define MFXVideoENCODE_Init disp_MFXVideoENCODE_Init +#define MFXVideoENCODE_Reset disp_MFXVideoENCODE_Reset +#define MFXVideoENCODE_Close disp_MFXVideoENCODE_Close +#define MFXVideoENCODE_GetVideoParam disp_MFXVideoENCODE_GetVideoParam +#define MFXVideoENCODE_GetEncodeStat disp_MFXVideoENCODE_GetEncodeStat +#define MFXVideoENCODE_EncodeFrameAsync disp_MFXVideoENCODE_EncodeFrameAsync + +#define MFXVideoDECODE_Query disp_MFXVideoDECODE_Query +#define MFXVideoDECODE_DecodeHeader disp_MFXVideoDECODE_DecodeHeader +#define MFXVideoDECODE_QueryIOSurf disp_MFXVideoDECODE_QueryIOSurf +#define MFXVideoDECODE_Init disp_MFXVideoDECODE_Init +#define MFXVideoDECODE_Reset disp_MFXVideoDECODE_Reset +#define MFXVideoDECODE_Close disp_MFXVideoDECODE_Close +#define MFXVideoDECODE_GetVideoParam disp_MFXVideoDECODE_GetVideoParam +#define MFXVideoDECODE_GetDecodeStat disp_MFXVideoDECODE_GetDecodeStat +#define MFXVideoDECODE_SetSkipMode disp_MFXVideoDECODE_SetSkipMode +#define MFXVideoDECODE_GetPayload disp_MFXVideoDECODE_GetPayload +#define MFXVideoDECODE_DecodeFrameAsync disp_MFXVideoDECODE_DecodeFrameAsync + +#define MFXVideoVPP_Query disp_MFXVideoVPP_Query +#define MFXVideoVPP_QueryIOSurf disp_MFXVideoVPP_QueryIOSurf +#define MFXVideoVPP_Init disp_MFXVideoVPP_Init +#define MFXVideoVPP_Reset disp_MFXVideoVPP_Reset +#define MFXVideoVPP_Close disp_MFXVideoVPP_Close + +#define MFXVideoVPP_GetVideoParam disp_MFXVideoVPP_GetVideoParam +#define MFXVideoVPP_GetVPPStat disp_MFXVideoVPP_GetVPPStat +#define MFXVideoVPP_RunFrameVPPAsync disp_MFXVideoVPP_RunFrameVPPAsync + +// API 1.1 functions +#define MFXVideoUSER_Register disp_MFXVideoUSER_Register +#define MFXVideoUSER_Unregister disp_MFXVideoUSER_Unregister +#define MFXVideoUSER_ProcessFrameAsync disp_MFXVideoUSER_ProcessFrameAsync + +// API 1.10 functions + +#define MFXVideoENC_Query disp_MFXVideoENC_Query +#define MFXVideoENC_QueryIOSurf disp_MFXVideoENC_QueryIOSurf +#define MFXVideoENC_Init disp_MFXVideoENC_Init +#define MFXVideoENC_Reset disp_MFXVideoENC_Reset +#define MFXVideoENC_Close disp_MFXVideoENC_Close +#define MFXVideoENC_ProcessFrameAsync disp_MFXVideoENC_ProcessFrameAsync +#define MFXVideoVPP_RunFrameVPPAsyncEx disp_MFXVideoVPP_RunFrameVPPAsyncEx +#define MFXVideoUSER_Load disp_MFXVideoUSER_Load +#define MFXVideoUSER_UnLoad disp_MFXVideoUSER_UnLoad + +// API 1.11 functions + +#define MFXVideoPAK_Query disp_MFXVideoPAK_Query +#define MFXVideoPAK_QueryIOSurf disp_MFXVideoPAK_QueryIOSurf +#define MFXVideoPAK_Init disp_MFXVideoPAK_Init +#define MFXVideoPAK_Reset disp_MFXVideoPAK_Reset +#define MFXVideoPAK_Close disp_MFXVideoPAK_Close +#define MFXVideoPAK_ProcessFrameAsync disp_MFXVideoPAK_ProcessFrameAsync + +// API 1.13 functions + +#define MFXVideoUSER_LoadByPath disp_MFXVideoUSER_LoadByPath + +// API 1.14 functions +#define MFXInitEx disp_MFXInitEx +#define MFXDoWork disp_MFXDoWork + +// Audio library functions + +// API 1.8 functions + +#define MFXAudioCORE_SyncOperation disp_MFXAudioCORE_SyncOperation +#define MFXAudioENCODE_Query disp_MFXAudioENCODE_Query +#define MFXAudioENCODE_QueryIOSize disp_MFXAudioENCODE_QueryIOSize +#define MFXAudioENCODE_Init disp_MFXAudioENCODE_Init +#define MFXAudioENCODE_Reset disp_MFXAudioENCODE_Reset +#define MFXAudioENCODE_Close disp_MFXAudioENCODE_Close +#define MFXAudioENCODE_GetAudioParam disp_MFXAudioENCODE_GetAudioParam +#define MFXAudioENCODE_EncodeFrameAsync disp_MFXAudioENCODE_EncodeFrameAsync + +#define MFXAudioDECODE_Query disp_MFXAudioDECODE_Query +#define MFXAudioDECODE_DecodeHeader disp_MFXAudioDECODE_DecodeHeader +#define MFXAudioDECODE_Init disp_MFXAudioDECODE_Init +#define MFXAudioDECODE_Reset disp_MFXAudioDECODE_Reset +#define MFXAudioDECODE_Close disp_MFXAudioDECODE_Close +#define MFXAudioDECODE_QueryIOSize disp_MFXAudioDECODE_QueryIOSize +#define MFXAudioDECODE_GetAudioParam disp_MFXAudioDECODE_GetAudioParam +#define MFXAudioDECODE_DecodeFrameAsync disp_MFXAudioDECODE_DecodeFrameAsync + +// API 1.9 functions + +#define MFXAudioUSER_Register disp_MFXAudioUSER_Register +#define MFXAudioUSER_Unregister disp_MFXAudioUSER_Unregister +#define MFXAudioUSER_ProcessFrameAsync disp_MFXAudioUSER_ProcessFrameAsync +#define MFXAudioUSER_Load disp_MFXAudioUSER_Load +#define MFXAudioUSER_UnLoad disp_MFXAudioUSER_UnLoad + +// API 1.19 functions + +#define MFXVideoENC_GetVideoParam disp_MFXVideoENC_GetVideoParam +#define MFXVideoPAK_GetVideoParam disp_MFXVideoPAK_GetVideoParam +#define MFXVideoCORE_QueryPlatform disp_MFXVideoCORE_QueryPlatform +#define MFXVideoUSER_GetPlugin disp_MFXVideoUSER_GetPlugin + +#endif \ No newline at end of file diff --git a/vshampor/deshuffler/msdk_api/include/mfxenc.h b/vshampor/deshuffler/msdk_api/include/mfxenc.h new file mode 100644 index 0000000..c094518 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxenc.h @@ -0,0 +1,71 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXENC_H__ +#define __MFXENC_H__ +#include "mfxdefs.h" +#include "mfxvstructures.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +typedef struct _mfxENCInput mfxENCInput; +struct _mfxENCInput{ + mfxU32 reserved[32]; + + mfxFrameSurface1 *InSurface; + + mfxU16 NumFrameL0; + mfxFrameSurface1 **L0Surface; + mfxU16 NumFrameL1; + mfxFrameSurface1 **L1Surface; + + mfxU16 NumExtParam; + mfxExtBuffer **ExtParam; +} ; +typedef struct _mfxENCOutput mfxENCOutput; +struct _mfxENCOutput{ + mfxU32 reserved[32]; + + mfxFrameSurface1 *OutSurface; + + mfxU16 NumExtParam; + mfxExtBuffer **ExtParam; +} ; + + +mfxStatus MFX_CDECL MFXVideoENC_Query(mfxSession session, mfxVideoParam *in, mfxVideoParam *out); +mfxStatus MFX_CDECL MFXVideoENC_QueryIOSurf(mfxSession session, mfxVideoParam *par, mfxFrameAllocRequest *request); +mfxStatus MFX_CDECL MFXVideoENC_Init(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoENC_Reset(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoENC_Close(mfxSession session); + +mfxStatus MFX_CDECL MFXVideoENC_ProcessFrameAsync(mfxSession session, mfxENCInput *in, mfxENCOutput *out, mfxSyncPoint *syncp); + +mfxStatus MFX_CDECL MFXVideoENC_GetVideoParam(mfxSession session, mfxVideoParam *par); + +#ifdef __cplusplus +} // extern "C" +#endif /* __cplusplus */ + + +#endif + diff --git a/vshampor/deshuffler/msdk_api/include/mfxfei.h b/vshampor/deshuffler/msdk_api/include/mfxfei.h new file mode 100644 index 0000000..9f9de73 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxfei.h @@ -0,0 +1,545 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXFEI_H__ +#define __MFXFEI_H__ +#include "mfxdefs.h" +#include "mfxvstructures.h" +#include "mfxpak.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +typedef struct { + mfxExtBuffer Header; + + mfxU16 Qp; + mfxU16 LenSP; + mfxU16 SearchPath; + mfxU16 SubMBPartMask; + mfxU16 SubPelMode; + mfxU16 InterSAD; + mfxU16 IntraSAD; + mfxU16 AdaptiveSearch; + mfxU16 MVPredictor; + mfxU16 MBQp; + mfxU16 FTEnable; + mfxU16 IntraPartMask; + mfxU16 RefWidth; + mfxU16 RefHeight; + mfxU16 SearchWindow; + mfxU16 DisableMVOutput; + mfxU16 DisableStatisticsOutput; + mfxU16 Enable8x8Stat; + mfxU16 PictureType; /* Input picture type*/ + mfxU16 DownsampleInput; + + mfxU16 RefPictureType[2]; /* reference picture type, 0 -L0, 1 - L1 */ + mfxU16 DownsampleReference[2]; + mfxFrameSurface1 *RefFrame[2]; + mfxU16 reserved[28]; +} mfxExtFeiPreEncCtrl; + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[3]; + mfxU32 NumMBAlloc; /* size of allocated memory in number of macroblocks */ + mfxU16 reserved2[20]; + + struct mfxExtFeiPreEncMVPredictorsMB { + mfxI16Pair MV[2]; /* 0 for L0 and 1 for L1 */ + } *MB; +} mfxExtFeiPreEncMVPredictors; + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[3]; + mfxU32 NumMBAlloc; + mfxU16 reserved2[20]; + + mfxU8 *MB; +} mfxExtFeiEncQP; + +/* PreENC output */ +/* Layout is exactly the same as mfxExtFeiEncMVs, this buffer may be removed in future */ +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[3]; + mfxU32 NumMBAlloc; + mfxU16 reserved2[20]; + + struct mfxExtFeiPreEncMVMB { + mfxI16Pair MV[16][2]; + } *MB; +} mfxExtFeiPreEncMV; + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[3]; + mfxU32 NumMBAlloc; + mfxU16 reserved2[20]; + + struct mfxExtFeiPreEncMBStatMB { + struct mfxExtFeiPreEncMBStatMBInter { + mfxU16 BestDistortion; + mfxU16 Mode ; + } Inter[2]; /*0 -L0, 1 - L1*/ + + mfxU16 BestIntraDistortion; + mfxU16 IntraMode ; + + mfxU16 NumOfNonZeroCoef; + mfxU16 reserved1; + mfxU32 SumOfCoef; + + mfxU32 reserved2; + + mfxU32 Variance16x16; + mfxU32 Variance8x8[4]; + mfxU32 PixelAverage16x16; + mfxU32 PixelAverage8x8[4]; + } *MB; +} mfxExtFeiPreEncMBStat; + +/* 1 ENC_PAK input */ +typedef struct { + mfxExtBuffer Header; + + mfxU16 SearchPath; + mfxU16 LenSP; + mfxU16 SubMBPartMask; + mfxU16 IntraPartMask; + mfxU16 MultiPredL0; + mfxU16 MultiPredL1; + mfxU16 SubPelMode; + mfxU16 InterSAD; + mfxU16 IntraSAD; + mfxU16 DistortionType; + mfxU16 RepartitionCheckEnable; + mfxU16 AdaptiveSearch; + mfxU16 MVPredictor; + mfxU16 NumMVPredictors[2]; + mfxU16 PerMBQp; + mfxU16 PerMBInput; + mfxU16 MBSizeCtrl; + mfxU16 RefWidth; + mfxU16 RefHeight; + mfxU16 SearchWindow; + mfxU16 ColocatedMbDistortion; + mfxU16 reserved[38]; +} mfxExtFeiEncFrameCtrl; + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[3]; + mfxU32 NumMBAlloc; + mfxU16 reserved2[20]; + + struct mfxExtFeiEncMVPredictorsMB { + struct mfxExtFeiEncMVPredictorsMBRefIdx{ + mfxU8 RefL0: 4; + mfxU8 RefL1: 4; + } RefIdx[4]; /* index is predictor number */ + mfxU32 reserved; + mfxI16Pair MV[4][2]; /* first index is predictor number, second is 0 for L0 and 1 for L1 */ + } *MB; +} mfxExtFeiEncMVPredictors; + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[3]; + mfxU32 NumMBAlloc; + mfxU16 reserved2[20]; + + struct mfxExtFeiEncMBCtrlMB { + mfxU32 ForceToIntra : 1; + mfxU32 ForceToSkip : 1; + mfxU32 ForceToNoneSkip : 1; +#if (MFX_VERSION >= 1025) + mfxU32 DirectBiasAdjustment : 1; + mfxU32 GlobalMotionBiasAdjustment : 1; + mfxU32 MVCostScalingFactor : 3; + + mfxU32 reserved1 : 24; +#else + mfxU32 reserved1 : 29; +#endif + mfxU32 reserved2; + mfxU32 reserved3; + + mfxU32 reserved4 : 16; + mfxU32 TargetSizeInWord : 8; + mfxU32 MaxSizeInWord : 8; + } *MB; +} mfxExtFeiEncMBCtrl; + + +/* 1 ENC_PAK output */ +/* Buffer holds 32 MVs per MB. MVs are located in zigzag scan order. +Number in diagram below shows location of MV in memory. +For example, MV for right top 4x4 sub block is stored in 5-th element of the array. +======================== +|| 00 | 01 || 04 | 05 || +------------------------ +|| 02 | 03 || 06 | 07 || +======================== +|| 08 | 09 || 12 | 13 || +------------------------ +|| 10 | 11 || 14 | 15 || +======================== +*/ +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[3]; + mfxU32 NumMBAlloc; + mfxU16 reserved2[20]; + + struct mfxExtFeiEncMVMB { + mfxI16Pair MV[16][2]; /* first index is block (4x4 pixels) number, second is 0 for L0 and 1 for L1 */ + } *MB; +} mfxExtFeiEncMV; + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[3]; + mfxU32 NumMBAlloc; + mfxU16 reserved2[20]; + + struct mfxExtFeiEncMBStatMB { + mfxU16 InterDistortion[16]; + mfxU16 BestInterDistortion; + mfxU16 BestIntraDistortion; + mfxU16 ColocatedMbDistortion; + mfxU16 reserved; + mfxU32 reserved1[2]; + } *MB; +} mfxExtFeiEncMBStat; + +enum { + MFX_PAK_OBJECT_HEADER = 0x7149000A +}; + +typedef struct { + /* dword 0-2 */ + mfxU32 Header; /* MFX_PAK_OBJECT_HEADER */ + mfxU32 MVDataLength; + mfxU32 MVDataOffset; + + /* dword 3 */ + mfxU32 InterMbMode : 2; + mfxU32 MBSkipFlag : 1; + mfxU32 Reserved00 : 1; + mfxU32 IntraMbMode : 2; + mfxU32 Reserved01 : 1; + mfxU32 FieldMbPolarityFlag : 1; + mfxU32 MbType : 5; + mfxU32 IntraMbFlag : 1; + mfxU32 FieldMbFlag : 1; + mfxU32 Transform8x8Flag : 1; + mfxU32 Reserved02 : 1; + mfxU32 DcBlockCodedCrFlag : 1; + mfxU32 DcBlockCodedCbFlag : 1; + mfxU32 DcBlockCodedYFlag : 1; + mfxU32 MVFormat : 3; /* layout and number of MVs, 0 - no MVs, 6 - 32 MVs, the rest are reserved */ + mfxU32 Reserved03 : 8; + mfxU32 ExtendedFormat : 1; /* should be 1, specifies that LumaIntraPredModes and RefIdx are replicated for 8x8 and 4x4 block/subblock */ + + /* dword 4 */ + mfxU8 HorzOrigin; + mfxU8 VertOrigin; + mfxU16 CbpY; + + /* dword 5 */ + mfxU16 CbpCb; + mfxU16 CbpCr; + + /* dword 6 */ + mfxU32 QpPrimeY : 8; + mfxU32 Reserved30 :17; + mfxU32 MbSkipConvDisable : 1; + mfxU32 IsLastMB : 1; + mfxU32 EnableCoefficientClamp : 1; + mfxU32 Direct8x8Pattern : 4; + + union { + struct {/* Intra MBs */ + /* dword 7-8 */ + mfxU16 LumaIntraPredModes[4]; + + /* dword 9 */ + mfxU32 ChromaIntraPredMode : 2; + mfxU32 IntraPredAvailFlags : 6; + mfxU32 Reserved60 : 24; + } IntraMB; + struct {/* Inter MBs */ + /*dword 7 */ + mfxU8 SubMbShapes; + mfxU8 SubMbPredModes; + mfxU16 Reserved40; + + /* dword 8-9 */ + mfxU8 RefIdx[2][4]; /* first index is 0 for L0 and 1 for L1 */ + } InterMB; + }; + + /* dword 10 */ + mfxU16 Reserved70; + mfxU8 TargetSizeInWord; + mfxU8 MaxSizeInWord; + + mfxU32 reserved2[5]; +}mfxFeiPakMBCtrl; + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[3]; + mfxU32 NumMBAlloc; + mfxU16 reserved2[20]; + + mfxFeiPakMBCtrl *MB; +} mfxExtFeiPakMBCtrl; + +typedef struct { + mfxExtBuffer Header; + mfxU32 MaxFrameSize; /* in bytes */ + mfxU32 NumPasses; /* up to 8 */ + mfxU16 reserved[8]; + mfxU8 DeltaQP[8]; /* list of delta QPs, only positive values */ +} mfxExtFeiRepackCtrl; + +#if (MFX_VERSION >= 1025) +/* FEI repack status */ +typedef struct { + mfxExtBuffer Header; + mfxU32 NumPasses; + mfxU16 reserved[58]; +} mfxExtFeiRepackStat; +#endif + +/* 1 decode stream out */ +typedef struct { + /* dword 0 */ + mfxU32 InterMbMode : 2; + mfxU32 MBSkipFlag : 1; + mfxU32 Reserved00 : 1; + mfxU32 IntraMbMode : 2; + mfxU32 Reserved01 : 1; + mfxU32 FieldMbPolarityFlag : 1; + mfxU32 MbType : 5; + mfxU32 IntraMbFlag : 1; + mfxU32 FieldMbFlag : 1; + mfxU32 Transform8x8Flag : 1; + mfxU32 Reserved02 : 1; + mfxU32 DcBlockCodedCrFlag : 1; + mfxU32 DcBlockCodedCbFlag : 1; + mfxU32 DcBlockCodedYFlag : 1; + mfxU32 Reserved03 :12; + + /* dword 1 */ + mfxU16 HorzOrigin; + mfxU16 VertOrigin; + + /* dword 2 */ + mfxU32 CbpY :16; + mfxU32 CbpCb : 4; + mfxU32 CbpCr : 4; + mfxU32 Reserved20 : 6; + mfxU32 IsLastMB : 1; + mfxU32 ConcealMB : 1; + + /* dword 3 */ + mfxU32 QpPrimeY : 7; + mfxU32 Reserved30 : 1; + mfxU32 Reserved31 : 8; + mfxU32 NzCoeffCount : 9; + mfxU32 Reserved32 : 3; + mfxU32 Direct8x8Pattern : 4; + + /* dword 4-6 */ + union { + struct {/* Intra MBs */ + /* dword 4-5 */ + mfxU16 LumaIntraPredModes[4]; + + /* dword 6 */ + mfxU32 ChromaIntraPredMode : 2; + mfxU32 IntraPredAvailFlags : 6; + mfxU32 Reserved60 : 24; + } IntraMB; + + struct {/* Inter MBs */ + /* dword 4 */ + mfxU8 SubMbShapes; + mfxU8 SubMbPredModes; + mfxU16 Reserved40; + + /* dword 5-6 */ + mfxU8 RefIdx[2][4]; /* first index is 0 for L0 and 1 for L1 */ + } InterMB; + }; + + /* dword 7 */ + mfxU32 Reserved70; + + /* dword 8-15 */ + mfxI16Pair MV[4][2]; /* L0 - 0, L1 - 1 */ +}mfxFeiDecStreamOutMBCtrl; + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[3]; + mfxU32 NumMBAlloc; + mfxU16 RemapRefIdx; /* tri-state option, default is OFF */ + mfxU16 PicStruct; + mfxU16 reserved2[18]; + + mfxFeiDecStreamOutMBCtrl *MB; +} mfxExtFeiDecStreamOut; + +/* SPS, PPS, Slice Header */ +typedef struct { + mfxExtBuffer Header; + + mfxU16 SPSId; + + mfxU16 PicOrderCntType; + mfxU16 Log2MaxPicOrderCntLsb; + mfxU16 reserved[121]; +} mfxExtFeiSPS; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 SPSId; + mfxU16 PPSId; + + mfxU16 PictureType; + mfxU16 FrameType; + mfxU16 PicInitQP; + mfxU16 NumRefIdxL0Active; + mfxU16 NumRefIdxL1Active; + mfxI16 ChromaQPIndexOffset; + mfxI16 SecondChromaQPIndexOffset; + mfxU16 Transform8x8ModeFlag; + mfxU16 reserved[114]; + + struct mfxExtFeiPpsDPB { + mfxU16 Index; /* index in mfxPAKInput::L0Surface array */ + mfxU16 PicType; + mfxI32 FrameNumWrap; + mfxU16 LongTermFrameIdx; + mfxU16 reserved[3]; + } DpbBefore[16], DpbAfter[16]; +} mfxExtFeiPPS; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 NumSlice; /* actual number of slices in the picture */ + mfxU16 reserved[11]; + + struct mfxSlice{ + mfxU16 MBAddress; + mfxU16 NumMBs; + mfxU16 SliceType; + mfxU16 PPSId; + mfxU16 IdrPicId; + + mfxU16 CabacInitIdc; + + mfxU16 NumRefIdxL0Active; + mfxU16 NumRefIdxL1Active; + + mfxI16 SliceQPDelta; + mfxU16 DisableDeblockingFilterIdc; + mfxI16 SliceAlphaC0OffsetDiv2; + mfxI16 SliceBetaOffsetDiv2; + mfxU16 reserved[20]; + + struct mfxSliceRef{ + mfxU16 PictureType; + mfxU16 Index; + mfxU16 reserved[2]; + } RefL0[32], RefL1[32]; /* index in mfxPAKInput::L0Surface array */ + + } *Slice; +}mfxExtFeiSliceHeader; + + +typedef struct { + mfxExtBuffer Header; + + mfxU16 DisableHME; /* 0 - enable, any other value means disable */ + mfxU16 DisableSuperHME; + mfxU16 DisableUltraHME; + + mfxU16 reserved[57]; +} mfxExtFeiCodingOption; + + +/* 1 functions */ +typedef enum { + MFX_FEI_FUNCTION_PREENC =1, + MFX_FEI_FUNCTION_ENCODE =2, + MFX_FEI_FUNCTION_ENC =3, + MFX_FEI_FUNCTION_PAK =4, + MFX_FEI_FUNCTION_DEC =5, +} mfxFeiFunction; + +enum { + MFX_EXTBUFF_FEI_PARAM = MFX_MAKEFOURCC('F','E','P','R'), + MFX_EXTBUFF_FEI_PREENC_CTRL = MFX_MAKEFOURCC('F','P','C','T'), + MFX_EXTBUFF_FEI_PREENC_MV_PRED = MFX_MAKEFOURCC('F','P','M','P'), + MFX_EXTBUFF_FEI_PREENC_MV = MFX_MAKEFOURCC('F','P','M','V'), + MFX_EXTBUFF_FEI_PREENC_MB = MFX_MAKEFOURCC('F','P','M','B'), + MFX_EXTBUFF_FEI_ENC_CTRL = MFX_MAKEFOURCC('F','E','C','T'), + MFX_EXTBUFF_FEI_ENC_MV_PRED = MFX_MAKEFOURCC('F','E','M','P'), + MFX_EXTBUFF_FEI_ENC_QP = MFX_MAKEFOURCC('F','E','Q','P'), + MFX_EXTBUFF_FEI_ENC_MV = MFX_MAKEFOURCC('F','E','M','V'), + MFX_EXTBUFF_FEI_ENC_MB = MFX_MAKEFOURCC('F','E','M','B'), + MFX_EXTBUFF_FEI_ENC_MB_STAT = MFX_MAKEFOURCC('F','E','S','T'), + MFX_EXTBUFF_FEI_PAK_CTRL = MFX_MAKEFOURCC('F','K','C','T'), + MFX_EXTBUFF_FEI_SPS = MFX_MAKEFOURCC('F','S','P','S'), + MFX_EXTBUFF_FEI_PPS = MFX_MAKEFOURCC('F','P','P','S'), + MFX_EXTBUFF_FEI_SLICE = MFX_MAKEFOURCC('F','S','L','C'), + MFX_EXTBUFF_FEI_CODING_OPTION = MFX_MAKEFOURCC('F','C','D','O'), + MFX_EXTBUFF_FEI_DEC_STREAM_OUT = MFX_MAKEFOURCC('F','D','S','O'), + MFX_EXTBUFF_FEI_REPACK_CTRL = MFX_MAKEFOURCC('F','E','R','P'), +#if (MFX_VERSION >= 1025) + MFX_EXTBUFF_FEI_REPACK_STAT = MFX_MAKEFOURCC('F','E','R','S') +#endif +}; + +/* should be attached to mfxVideoParam during initialization to indicate FEI function */ +typedef struct { + mfxExtBuffer Header; + mfxFeiFunction Func; + mfxU16 SingleFieldProcessing; + mfxU16 reserved[57]; +} mfxExtFeiParam; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + + +#endif diff --git a/vshampor/deshuffler/msdk_api/include/mfxfeihevc.h b/vshampor/deshuffler/msdk_api/include/mfxfeihevc.h new file mode 100644 index 0000000..b82234c --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxfeihevc.h @@ -0,0 +1,271 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXFEIHEVC_H__ +#define __MFXFEIHEVC_H__ +#include "mfxcommon.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if (MFX_VERSION >= 1027) + +typedef struct { + mfxExtBuffer Header; + + mfxU16 SearchPath; + mfxU16 LenSP; + mfxU16 RefWidth; + mfxU16 RefHeight; + mfxU16 SearchWindow; + + mfxU16 NumMvPredictors[2]; /* 0 for L0 and 1 for L1 */ + mfxU16 MultiPred[2]; /* 0 for L0 and 1 for L1 */ + + mfxU16 SubPelMode; + mfxU16 AdaptiveSearch; + mfxU16 MVPredictor; + + mfxU16 PerCuQp; + mfxU16 PerCtuInput; + mfxU16 ForceCtuSplit; + mfxU16 NumFramePartitions; + mfxU16 FastIntraMode; + + mfxU16 reserved0[107]; +} mfxExtFeiHevcEncFrameCtrl; + + +typedef struct { + struct { + mfxU8 RefL0 : 4; + mfxU8 RefL1 : 4; + } RefIdx[4]; /* index is predictor number */ + + mfxU32 BlockSize : 2; + mfxU32 reserved0 : 30; + + mfxI16Pair MV[4][2]; /* first index is predictor number, second is 0 for L0 and 1 for L1 */ +} mfxFeiHevcEncMVPredictors; + +typedef struct { + mfxExtBuffer Header; + mfxU32 VaBufferID; + mfxU32 Pitch; + mfxU32 Height; + mfxU16 reserved0[54]; + + mfxFeiHevcEncMVPredictors *Data; +} mfxExtFeiHevcEncMVPredictors; + + +typedef struct { + mfxExtBuffer Header; + mfxU32 VaBufferID; + mfxU32 Pitch; + mfxU32 Height; + mfxU16 reserved[6]; + + mfxU8 *Data; +} mfxExtFeiHevcEncQP; + + +typedef struct { + mfxU32 ForceToIntra : 1; + mfxU32 ForceToInter : 1; + mfxU32 reserved0 : 30; + + mfxU32 reserved1[3]; +} mfxFeiHevcEncCtuCtrl; + + +typedef struct { + mfxExtBuffer Header; + mfxU32 VaBufferID; + mfxU32 Pitch; + mfxU32 Height; + mfxU16 reserved0[54]; + + mfxFeiHevcEncCtuCtrl *Data; +} mfxExtFeiHevcEncCtuCtrl; + +typedef struct { + mfxExtBuffer Header; + mfxU32 MaxFrameSize; /* in bytes */ + mfxU32 NumPasses; /* up to 8 */ + mfxU16 reserved[8]; + mfxU8 DeltaQP[8]; /* list of delta QPs, only positive values */ +} mfxExtFeiHevcRepackCtrl; + +typedef struct { + mfxExtBuffer Header; + mfxU32 NumPasses; + mfxU16 reserved[58]; +} mfxExtFeiHevcRepackStat; + +#if MFX_VERSION >= MFX_VERSION_NEXT +typedef struct { + /* DWORD 0 */ + mfxU32 reserved0; + + /* DWORD 1 */ + mfxU32 SplitLevel2Part0 : 4; + mfxU32 SplitLevel2Part1 : 4; + mfxU32 SplitLevel2Part2 : 4; + mfxU32 SplitLevel2Part3 : 4; + mfxU32 SplitLevel1 : 4; + mfxU32 SplitLevel0 : 1; + mfxU32 reserved10 : 3; + mfxU32 CuCountMinus1 : 6; + mfxU32 LastCtuOfTileFlag : 1; + mfxU32 LastCtuOfSliceFlag : 1; + + + /* DWORD 2 */ + mfxU32 CtuAddrX : 16; + mfxU32 CtuAddrY : 16; + + /* DWORD 3 */ + mfxU32 reserved3; +} mfxFeiHevcPakCtuRecordV0; + + +typedef struct { + mfxExtBuffer Header; + mfxU32 VaBufferID; + mfxU32 Pitch; + mfxU32 Height; + mfxU16 reserved0[54]; + + mfxFeiHevcPakCtuRecordV0 *Data; +} mfxExtFeiHevcPakCtuRecordV0; + + +typedef struct { + /* DWORD 0 */ + mfxU32 CuSize : 2; + mfxU32 PredMode : 1; + mfxU32 TransquantBypass : 1; + mfxU32 PartMode : 3; + mfxU32 IpcmEnable : 1; + mfxU32 IntraChromaMode : 3; + mfxU32 ZeroOutCoeffs : 1; + mfxU32 reserved00 : 4; + mfxU32 Qp : 7; + mfxU32 QpSign : 1; + mfxU32 InterpredIdc : 8; + + + /* DWORD 1 */ + mfxU32 IntraMode0 : 6; + mfxU32 reserved10 : 2; + mfxU32 IntraMode1 : 6; + mfxU32 reserved11 : 2; + mfxU32 IntraMode2 : 6; + mfxU32 reserved12 : 2; + mfxU32 IntraMode3 : 6; + mfxU32 reserved13 : 2; + + + /* DWORD 2-9 */ + struct { + mfxI16 x[4]; + mfxI16 y[4]; + } MVs[2]; /* 0-L0, 1-L1 */ + + + /* DWORD 10 */ + struct{ + mfxU16 Ref0 : 4; + mfxU16 Ref1 : 4; + mfxU16 Ref2 : 4; + mfxU16 Ref3 : 4; + } RefIdx[2]; /* 0-L0, 1-L1 */ + + + /* DWORD 11 */ + mfxU32 TuSize; + + + /* DWORD 12 */ + mfxU32 TransformSkipY : 16; + mfxU32 reserved120 : 12; + mfxU32 TuCountM1 : 4; + + + /* DWORD 13 */ + mfxU32 TransformSkipU : 16; + mfxU32 TransformSkipV : 16; +} mfxFeiHevcPakCuRecordV0; + + +typedef struct { + mfxExtBuffer Header; + mfxU32 VaBufferID; + mfxU32 Pitch; + mfxU32 Height; + mfxU16 reserved0[54]; + + mfxFeiHevcPakCuRecordV0 *Data; +} mfxExtFeiHevcPakCuRecordV0; + + +typedef struct { + mfxU32 BestDistortion; + mfxU32 ColocatedCtuDistortion; +} mfxFeiHevcDistortionCtu; + + +typedef struct { + mfxExtBuffer Header; + mfxU32 VaBufferID; + mfxU32 Pitch; + mfxU32 Height; + mfxU16 reserved[6]; + + mfxFeiHevcDistortionCtu *Data; +} mfxExtFeiHevcDistortion; +#endif + + +enum { + MFX_EXTBUFF_HEVCFEI_ENC_CTRL = MFX_MAKEFOURCC('F','H','C','T'), + MFX_EXTBUFF_HEVCFEI_ENC_MV_PRED = MFX_MAKEFOURCC('F','H','P','D'), + MFX_EXTBUFF_HEVCFEI_ENC_QP = MFX_MAKEFOURCC('F','H','Q','P'), + MFX_EXTBUFF_HEVCFEI_ENC_CTU_CTRL = MFX_MAKEFOURCC('F','H','E','C'), + MFX_EXTBUFF_HEVCFEI_REPACK_CTRL = MFX_MAKEFOURCC('F','H','R','P'), + MFX_EXTBUFF_HEVCFEI_REPACK_STAT = MFX_MAKEFOURCC('F','H','R','S'), + +#if MFX_VERSION >= MFX_VERSION_NEXT + MFX_EXTBUFF_HEVCFEI_PAK_CTU_REC = MFX_MAKEFOURCC('F','H','T','B'), + MFX_EXTBUFF_HEVCFEI_PAK_CU_REC = MFX_MAKEFOURCC('F','H','C','U'), + MFX_EXTBUFF_HEVCFEI_ENC_DIST = MFX_MAKEFOURCC('F','H','D','S') +#endif +}; + +#endif // MFX_VERSION + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + + +#endif // __MFXFEIHEVC_H__ diff --git a/vshampor/deshuffler/msdk_api/include/mfxjpeg.h b/vshampor/deshuffler/msdk_api/include/mfxjpeg.h new file mode 100644 index 0000000..eb17075 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxjpeg.h @@ -0,0 +1,101 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFX_JPEG_H__ +#define __MFX_JPEG_H__ + +#include "mfxdefs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* CodecId */ +enum { + MFX_CODEC_JPEG = MFX_MAKEFOURCC('J','P','E','G') +}; + +/* CodecProfile, CodecLevel */ +enum +{ + MFX_PROFILE_JPEG_BASELINE = 1 +}; + +enum +{ + MFX_ROTATION_0 = 0, + MFX_ROTATION_90 = 1, + MFX_ROTATION_180 = 2, + MFX_ROTATION_270 = 3 +}; + +enum { + MFX_EXTBUFF_JPEG_QT = MFX_MAKEFOURCC('J','P','G','Q'), + MFX_EXTBUFF_JPEG_HUFFMAN = MFX_MAKEFOURCC('J','P','G','H') +}; + +enum { + MFX_JPEG_COLORFORMAT_UNKNOWN = 0, + MFX_JPEG_COLORFORMAT_YCbCr = 1, + MFX_JPEG_COLORFORMAT_RGB = 2 +}; + +enum { + MFX_SCANTYPE_UNKNOWN = 0, + MFX_SCANTYPE_INTERLEAVED = 1, + MFX_SCANTYPE_NONINTERLEAVED = 2 +}; + +enum { + MFX_CHROMAFORMAT_JPEG_SAMPLING = 6 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 reserved[7]; + mfxU16 NumTable; + + mfxU16 Qm[4][64]; +} mfxExtJPEGQuantTables; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 reserved[2]; + mfxU16 NumDCTable; + mfxU16 NumACTable; + + struct { + mfxU8 Bits[16]; + mfxU8 Values[12]; + } DCTables[4]; + + struct { + mfxU8 Bits[16]; + mfxU8 Values[162]; + } ACTables[4]; +} mfxExtJPEGHuffmanTables; + +#ifdef __cplusplus +} // extern "C" +#endif /* __cplusplus */ + +#endif // __MFX_JPEG_H__ diff --git a/vshampor/deshuffler/msdk_api/include/mfxla.h b/vshampor/deshuffler/msdk_api/include/mfxla.h new file mode 100644 index 0000000..f758285 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxla.h @@ -0,0 +1,94 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXLA_H__ +#define __MFXLA_H__ +#include "mfxdefs.h" +#include "mfxvstructures.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +enum +{ + MFX_EXTBUFF_LOOKAHEAD_CTRL = MFX_MAKEFOURCC('L','A','C','T'), + MFX_EXTBUFF_LOOKAHEAD_STAT = MFX_MAKEFOURCC('L','A','S','T'), +}; + + +typedef struct +{ + mfxExtBuffer Header; + mfxU16 LookAheadDepth; + mfxU16 DependencyDepth; + mfxU16 DownScaleFactor; + mfxU16 BPyramid; + + mfxU16 reserved1[23]; + + mfxU16 NumOutStream; + struct mfxStream{ + mfxU16 Width; + mfxU16 Height; + mfxU16 reserved2[14]; + } OutStream[16]; +}mfxExtLAControl; + +typedef struct +{ + mfxU16 Width; + mfxU16 Height; + + mfxU32 FrameType; + mfxU32 FrameDisplayOrder; + mfxU32 FrameEncodeOrder; + + mfxU32 IntraCost; + mfxU32 InterCost; + mfxU32 DependencyCost; //aggregated cost, how this frame influences subsequent frames + mfxU16 Layer; + mfxU16 reserved[23]; + + mfxU64 EstimatedRate[52]; +}mfxLAFrameInfo; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 reserved[20]; + + mfxU16 NumAlloc; //number of allocated mfxLAFrameInfo structures + mfxU16 NumStream; //number of resolutions + mfxU16 NumFrame; //number of frames for each resolution + mfxLAFrameInfo *FrameStat; //frame statistics + + mfxFrameSurface1 *OutSurface; //reordered surface + +} mfxExtLAFrameStatistics; + +#ifdef __cplusplus +} // extern "C" +#endif /* __cplusplus */ + + +#endif + diff --git a/vshampor/deshuffler/msdk_api/include/mfxmvc.h b/vshampor/deshuffler/msdk_api/include/mfxmvc.h new file mode 100644 index 0000000..98997d5 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxmvc.h @@ -0,0 +1,99 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXMVC_H__ +#define __MFXMVC_H__ + +#include "mfxdefs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* CodecProfile, CodecLevel */ +enum { + /* MVC profiles */ + MFX_PROFILE_AVC_MULTIVIEW_HIGH =118, + MFX_PROFILE_AVC_STEREO_HIGH =128 +}; + +/* Extended Buffer Ids */ +enum { + MFX_EXTBUFF_MVC_SEQ_DESC = MFX_MAKEFOURCC('M','V','C','D'), + MFX_EXTBUFF_MVC_TARGET_VIEWS = MFX_MAKEFOURCC('M','V','C','T') +}; + +typedef struct { + mfxU16 ViewId; + + mfxU16 NumAnchorRefsL0; + mfxU16 NumAnchorRefsL1; + mfxU16 AnchorRefL0[16]; + mfxU16 AnchorRefL1[16]; + + mfxU16 NumNonAnchorRefsL0; + mfxU16 NumNonAnchorRefsL1; + mfxU16 NonAnchorRefL0[16]; + mfxU16 NonAnchorRefL1[16]; +} mfxMVCViewDependency; + +typedef struct { + mfxU16 TemporalId; + mfxU16 LevelIdc; + + mfxU16 NumViews; + mfxU16 NumTargetViews; + mfxU16 *TargetViewId; +} mfxMVCOperationPoint; + +typedef struct { + mfxExtBuffer Header; + + mfxU32 NumView; + mfxU32 NumViewAlloc; + mfxMVCViewDependency *View; + + mfxU32 NumViewId; + mfxU32 NumViewIdAlloc; + mfxU16 *ViewId; + + mfxU32 NumOP; + mfxU32 NumOPAlloc; + mfxMVCOperationPoint *OP; + + mfxU16 NumRefsTotal; + mfxU32 Reserved[16]; + +} mfxExtMVCSeqDesc; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 TemporalId; + mfxU32 NumView; + mfxU16 ViewId[1024]; +} mfxExtMVCTargetViews ; + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif + diff --git a/vshampor/deshuffler/msdk_api/include/mfxpak.h b/vshampor/deshuffler/msdk_api/include/mfxpak.h new file mode 100644 index 0000000..88baecd --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxpak.h @@ -0,0 +1,74 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXPAK_H__ +#define __MFXPAK_H__ +#include "mfxdefs.h" +#include "mfxvstructures.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +typedef struct { + mfxU16 reserved[32]; + + mfxFrameSurface1 *InSurface; + + mfxU16 NumFrameL0; + mfxFrameSurface1 **L0Surface; + mfxU16 NumFrameL1; + mfxFrameSurface1 **L1Surface; + + mfxU16 NumExtParam; + mfxExtBuffer **ExtParam; + + mfxU16 NumPayload; + mfxPayload **Payload; +} mfxPAKInput; + +typedef struct { + mfxU16 reserved[32]; + + mfxBitstream *Bs; + + mfxFrameSurface1 *OutSurface; + + mfxU16 NumExtParam; + mfxExtBuffer **ExtParam; +} mfxPAKOutput; + +typedef struct _mfxSession *mfxSession; +mfxStatus MFX_CDECL MFXVideoPAK_Query(mfxSession session, mfxVideoParam *in, mfxVideoParam *out); +mfxStatus MFX_CDECL MFXVideoPAK_QueryIOSurf(mfxSession session, mfxVideoParam *par, mfxFrameAllocRequest request[2]); +mfxStatus MFX_CDECL MFXVideoPAK_Init(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoPAK_Reset(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoPAK_Close(mfxSession session); + +mfxStatus MFX_CDECL MFXVideoPAK_ProcessFrameAsync(mfxSession session, mfxPAKInput *in, mfxPAKOutput *out, mfxSyncPoint *syncp); + +mfxStatus MFX_CDECL MFXVideoPAK_GetVideoParam(mfxSession session, mfxVideoParam *par); + +#ifdef __cplusplus +} // extern "C" +#endif /* __cplusplus */ + + +#endif diff --git a/vshampor/deshuffler/msdk_api/include/mfxplugin++.h b/vshampor/deshuffler/msdk_api/include/mfxplugin++.h new file mode 100644 index 0000000..0e2d818 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxplugin++.h @@ -0,0 +1,721 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef __MFXPLUGINPLUSPLUS_H +#define __MFXPLUGINPLUSPLUS_H + +#include "mfxplugin.h" + +// base class for MFXVideoUSER/MFXAudioUSER API + +class MFXBaseUSER { +public: + explicit MFXBaseUSER(mfxSession session = NULL) + : m_session(session){} + + virtual ~MFXBaseUSER() {}; + + virtual mfxStatus Register(mfxU32 type, const mfxPlugin *par) = 0; + virtual mfxStatus Unregister(mfxU32 type) = 0; + virtual mfxStatus ProcessFrameAsync(const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxSyncPoint *syncp) = 0; + +protected: + mfxSession m_session; +}; + +//c++ wrapper over only 3 exposed functions from MFXVideoUSER module +class MFXVideoUSER: public MFXBaseUSER { +public: + explicit MFXVideoUSER(mfxSession session = NULL) + : MFXBaseUSER(session){} + + virtual mfxStatus Register(mfxU32 type, const mfxPlugin *par) { + return MFXVideoUSER_Register(m_session, type, par); + } + virtual mfxStatus Unregister(mfxU32 type) { + return MFXVideoUSER_Unregister(m_session, type); + } + virtual mfxStatus ProcessFrameAsync(const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxSyncPoint *syncp) { + return MFXVideoUSER_ProcessFrameAsync(m_session, in, in_num, out, out_num, syncp); + } +}; + +//c++ wrapper over only 3 exposed functions from MFXAudioUSER module +class MFXAudioUSER: public MFXBaseUSER { +public: + explicit MFXAudioUSER(mfxSession session = NULL) + : MFXBaseUSER(session){} + + virtual mfxStatus Register(mfxU32 type, const mfxPlugin *par) { + return MFXAudioUSER_Register(m_session, type, par); + } + virtual mfxStatus Unregister(mfxU32 type) { + return MFXAudioUSER_Unregister(m_session, type); + } + virtual mfxStatus ProcessFrameAsync(const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxSyncPoint *syncp) { + return MFXAudioUSER_ProcessFrameAsync(m_session, in, in_num, out, out_num, syncp); + } +}; + + +//initialize mfxPlugin struct +class MFXPluginParam { + mfxPluginParam m_param; + +public: + MFXPluginParam(mfxU32 CodecId, mfxU32 Type, mfxPluginUID uid, mfxThreadPolicy ThreadPolicy = MFX_THREADPOLICY_SERIAL, mfxU32 MaxThreadNum = 1) + : m_param() { + m_param.PluginUID = uid; + m_param.Type = Type; + m_param.CodecId = CodecId; + m_param.MaxThreadNum = MaxThreadNum; + m_param.ThreadPolicy = ThreadPolicy; + } + operator const mfxPluginParam& () const { + return m_param; + } + operator mfxPluginParam& () { + return m_param; + } +}; + +//common interface part for every plugin: decoder/encoder and generic +struct MFXPlugin +{ + virtual ~MFXPlugin() {}; + //init function always required for any transform or codec plugins, for codec plugins it maps to callback from MediaSDK + //for generic plugin application should call it + //MediaSDK mfxPlugin API mapping + virtual mfxStatus PluginInit(mfxCoreInterface *core) = 0; + //release CoreInterface, and destroy plugin state, not destroy plugin instance + virtual mfxStatus PluginClose() = 0; + virtual mfxStatus GetPluginParam(mfxPluginParam *par) = 0; + virtual mfxStatus Execute(mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a) = 0; + virtual mfxStatus FreeResources(mfxThreadTask task, mfxStatus sts) = 0; + //destroy plugin due to shared module distribution model plugin wont support virtual destructor + virtual void Release() = 0; + //release resources associated with current instance of plugin, but do not release CoreInterface related resource set in pluginInit + virtual mfxStatus Close() = 0; + //communication protocol between particular version of plugin and application + virtual mfxStatus SetAuxParams(void* auxParam, int auxParamSize) = 0; +}; + +//common extension interface that codec plugins should expose additionally to MFXPlugin +struct MFXCodecPlugin : MFXPlugin +{ + virtual mfxStatus Init(mfxVideoParam *par) = 0; + virtual mfxStatus QueryIOSurf(mfxVideoParam *par, mfxFrameAllocRequest *in, mfxFrameAllocRequest *out) = 0; + virtual mfxStatus Query(mfxVideoParam *in, mfxVideoParam *out) =0; + virtual mfxStatus Reset(mfxVideoParam *par) = 0; + virtual mfxStatus GetVideoParam(mfxVideoParam *par) = 0; +}; + +//common extension interface that audio codec plugins should expose additionally to MFXPlugin +struct MFXAudioCodecPlugin : MFXPlugin +{ + virtual mfxStatus Init(mfxAudioParam *par) = 0; + virtual mfxStatus Query(mfxAudioParam *in, mfxAudioParam *out) =0; + virtual mfxStatus QueryIOSize(mfxAudioParam *par, mfxAudioAllocRequest *request) = 0; + virtual mfxStatus Reset(mfxAudioParam *par) = 0; + virtual mfxStatus GetAudioParam(mfxAudioParam *par) = 0; +}; + +//general purpose transform plugin interface, not a codec plugin +struct MFXGenericPlugin : MFXPlugin +{ + virtual mfxStatus Init(mfxVideoParam *par) = 0; + virtual mfxStatus QueryIOSurf(mfxVideoParam *par, mfxFrameAllocRequest *in, mfxFrameAllocRequest *out) = 0; + virtual mfxStatus Submit(const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task) = 0; +}; + +//decoder plugins may only support this interface +struct MFXDecoderPlugin : MFXCodecPlugin +{ + virtual mfxStatus DecodeHeader(mfxBitstream *bs, mfxVideoParam *par) = 0; + virtual mfxStatus GetPayload(mfxU64 *ts, mfxPayload *payload) = 0; + virtual mfxStatus DecodeFrameSubmit(mfxBitstream *bs, mfxFrameSurface1 *surface_work, mfxFrameSurface1 **surface_out, mfxThreadTask *task) = 0; +}; + +//audio decoder plugins may only support this interface +struct MFXAudioDecoderPlugin : MFXAudioCodecPlugin +{ + virtual mfxStatus DecodeHeader(mfxBitstream *bs, mfxAudioParam *par) = 0; +// virtual mfxStatus GetPayload(mfxU64 *ts, mfxPayload *payload) = 0; + virtual mfxStatus DecodeFrameSubmit(mfxBitstream *in, mfxAudioFrame *out, mfxThreadTask *task) = 0; +}; + +//encoder plugins may only support this interface +struct MFXEncoderPlugin : MFXCodecPlugin +{ + virtual mfxStatus EncodeFrameSubmit(mfxEncodeCtrl *ctrl, mfxFrameSurface1 *surface, mfxBitstream *bs, mfxThreadTask *task) = 0; +}; + +//audio encoder plugins may only support this interface +struct MFXAudioEncoderPlugin : MFXAudioCodecPlugin +{ + virtual mfxStatus EncodeFrameSubmit(mfxAudioFrame *aFrame, mfxBitstream *out, mfxThreadTask *task) = 0; +}; + +//vpp plugins may only support this interface +struct MFXVPPPlugin : MFXCodecPlugin +{ + virtual mfxStatus VPPFrameSubmit(mfxFrameSurface1 *surface_in, mfxFrameSurface1 *surface_out, mfxExtVppAuxData *aux, mfxThreadTask *task) = 0; + virtual mfxStatus VPPFrameSubmitEx(mfxFrameSurface1 *in, mfxFrameSurface1 *surface_work, mfxFrameSurface1 **surface_out, mfxThreadTask *task) = 0; +}; + +struct MFXEncPlugin : MFXCodecPlugin +{ + virtual mfxStatus EncFrameSubmit(mfxENCInput *in, mfxENCOutput *out, mfxThreadTask *task) = 0; +}; + + + + +class MFXCoreInterface +{ +protected: + mfxCoreInterface m_core; +public: + + MFXCoreInterface() + : m_core() { + } + MFXCoreInterface(const mfxCoreInterface & pCore) + : m_core(pCore) { + } + + MFXCoreInterface(const MFXCoreInterface & that) + : m_core(that.m_core) { + } + MFXCoreInterface &operator = (const MFXCoreInterface & that) + { + m_core = that.m_core; + return *this; + } + bool IsCoreSet() { + return m_core.pthis != 0; + } + mfxStatus GetCoreParam(mfxCoreParam *par) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.GetCoreParam(m_core.pthis, par); + } + mfxStatus GetHandle (mfxHandleType type, mfxHDL *handle) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.GetHandle(m_core.pthis, type, handle); + } + mfxStatus IncreaseReference (mfxFrameData *fd) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.IncreaseReference(m_core.pthis, fd); + } + mfxStatus DecreaseReference (mfxFrameData *fd) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.DecreaseReference(m_core.pthis, fd); + } + mfxStatus CopyFrame (mfxFrameSurface1 *dst, mfxFrameSurface1 *src) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.CopyFrame(m_core.pthis, dst, src); + } + mfxStatus CopyBuffer(mfxU8 *dst, mfxU32 size, mfxFrameSurface1 *src) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.CopyBuffer(m_core.pthis, dst, size, src); + } + mfxStatus MapOpaqueSurface(mfxU32 num, mfxU32 type, mfxFrameSurface1 **op_surf) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.MapOpaqueSurface(m_core.pthis, num, type, op_surf); + } + mfxStatus UnmapOpaqueSurface(mfxU32 num, mfxU32 type, mfxFrameSurface1 **op_surf) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.UnmapOpaqueSurface(m_core.pthis, num, type, op_surf); + } + mfxStatus GetRealSurface(mfxFrameSurface1 *op_surf, mfxFrameSurface1 **surf) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.GetRealSurface(m_core.pthis, op_surf, surf); + } + mfxStatus GetOpaqueSurface(mfxFrameSurface1 *surf, mfxFrameSurface1 **op_surf) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.GetOpaqueSurface(m_core.pthis, surf, op_surf); + } + mfxStatus CreateAccelerationDevice(mfxHandleType type, mfxHDL *handle) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.CreateAccelerationDevice(m_core.pthis, type, handle); + } + mfxFrameAllocator & FrameAllocator() { + return m_core.FrameAllocator; + } + mfxStatus GetFrameHandle(mfxFrameData *fd, mfxHDL *handle) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.GetFrameHandle(m_core.pthis, fd, handle); + } + mfxStatus QueryPlatform(mfxPlatform *platform) { + if (!IsCoreSet()) { + return MFX_ERR_NULL_PTR; + } + return m_core.QueryPlatform(m_core.pthis, platform); + } +} ; + +/* Class adapter between "C" structure mfxPlugin and C++ interface MFXPlugin */ + +namespace detail +{ + template + class MFXPluginAdapterBase + { + protected: + mfxPlugin m_mfxAPI; + public: + MFXPluginAdapterBase( T *plugin, mfxVideoCodecPlugin *pCodec = NULL) + : m_mfxAPI() + { + SetupCallbacks(plugin, pCodec); + } + + MFXPluginAdapterBase( T *plugin, mfxAudioCodecPlugin *pCodec) + : m_mfxAPI() + { + SetupCallbacks(plugin, pCodec); + } + + operator mfxPlugin () const { + return m_mfxAPI; + } + void SetupCallbacks(T *plugin) { + m_mfxAPI.pthis = plugin; + m_mfxAPI.PluginInit = _PluginInit; + m_mfxAPI.PluginClose = _PluginClose; + m_mfxAPI.GetPluginParam = _GetPluginParam; + m_mfxAPI.Submit = 0; + m_mfxAPI.Execute = _Execute; + m_mfxAPI.FreeResources = _FreeResources; + } + + void SetupCallbacks( T *plugin, mfxVideoCodecPlugin *pCodec) { + SetupCallbacks(plugin); + m_mfxAPI.Video = pCodec; + } + + void SetupCallbacks( T *plugin, mfxAudioCodecPlugin *pCodec) { + SetupCallbacks(plugin); + m_mfxAPI.Audio = pCodec; + } + private: + + static mfxStatus _PluginInit(mfxHDL pthis, mfxCoreInterface *core) { + return reinterpret_cast(pthis)->PluginInit(core); + } + static mfxStatus _PluginClose(mfxHDL pthis) { + return reinterpret_cast(pthis)->PluginClose(); + } + static mfxStatus _GetPluginParam(mfxHDL pthis, mfxPluginParam *par) { + return reinterpret_cast(pthis)->GetPluginParam(par); + } + static mfxStatus _Execute(mfxHDL pthis, mfxThreadTask task, mfxU32 thread_id, mfxU32 call_count) { + return reinterpret_cast(pthis)->Execute(task, thread_id, call_count); + } + static mfxStatus _FreeResources(mfxHDL pthis, mfxThreadTask task, mfxStatus sts) { + return reinterpret_cast(pthis)->FreeResources(task, sts); + } + }; + + template + class MFXCodecPluginAdapterBase : public MFXPluginAdapterBase + { + protected: + //stub to feed mediasdk plugin API + mfxVideoCodecPlugin m_codecPlg; + public: + MFXCodecPluginAdapterBase(T * pCodecPlg) + : MFXPluginAdapterBase(pCodecPlg, &m_codecPlg) + , m_codecPlg() + { + m_codecPlg.Query = _Query; + m_codecPlg.QueryIOSurf = _QueryIOSurf ; + m_codecPlg.Init = _Init; + m_codecPlg.Reset = _Reset; + m_codecPlg.Close = _Close; + m_codecPlg.GetVideoParam = _GetVideoParam; + } + MFXCodecPluginAdapterBase(const MFXCodecPluginAdapterBase & that) + : MFXPluginAdapterBase(reinterpret_cast(that.m_mfxAPI.pthis), &m_codecPlg) + , m_codecPlg() { + SetupCallbacks(); + } + MFXCodecPluginAdapterBase& operator = (const MFXCodecPluginAdapterBase & that) { + MFXPluginAdapterBase :: SetupCallbacks(reinterpret_cast(that.m_mfxAPI.pthis), &m_codecPlg); + SetupCallbacks(); + return *this; + } + + private: + void SetupCallbacks() { + m_codecPlg.Query = _Query; + m_codecPlg.QueryIOSurf = _QueryIOSurf ; + m_codecPlg.Init = _Init; + m_codecPlg.Reset = _Reset; + m_codecPlg.Close = _Close; + m_codecPlg.GetVideoParam = _GetVideoParam; + } + static mfxStatus _Query(mfxHDL pthis, mfxVideoParam *in, mfxVideoParam *out) { + return reinterpret_cast(pthis)->Query(in, out); + } + static mfxStatus _QueryIOSurf(mfxHDL pthis, mfxVideoParam *par, mfxFrameAllocRequest *in, mfxFrameAllocRequest *out){ + return reinterpret_cast(pthis)->QueryIOSurf(par, in, out); + } + static mfxStatus _Init(mfxHDL pthis, mfxVideoParam *par){ + return reinterpret_cast(pthis)->Init(par); + } + static mfxStatus _Reset(mfxHDL pthis, mfxVideoParam *par){ + return reinterpret_cast(pthis)->Reset(par); + } + static mfxStatus _Close(mfxHDL pthis) { + return reinterpret_cast(pthis)->Close(); + } + static mfxStatus _GetVideoParam(mfxHDL pthis, mfxVideoParam *par) { + return reinterpret_cast(pthis)->GetVideoParam(par); + } + }; + + template + class MFXAudioCodecPluginAdapterBase : public MFXPluginAdapterBase + { + protected: + //stub to feed mediasdk plugin API + mfxAudioCodecPlugin m_codecPlg; + public: + MFXAudioCodecPluginAdapterBase(T * pCodecPlg) + : MFXPluginAdapterBase(pCodecPlg, &m_codecPlg) + , m_codecPlg() + { + m_codecPlg.Query = _Query; + m_codecPlg.QueryIOSize = _QueryIOSize ; + m_codecPlg.Init = _Init; + m_codecPlg.Reset = _Reset; + m_codecPlg.Close = _Close; + m_codecPlg.GetAudioParam = _GetAudioParam; + } + MFXAudioCodecPluginAdapterBase(const MFXCodecPluginAdapterBase & that) + : MFXPluginAdapterBase(reinterpret_cast(that.m_mfxAPI.pthis), &m_codecPlg) + , m_codecPlg() { + SetupCallbacks(); + } + MFXAudioCodecPluginAdapterBase& operator = (const MFXAudioCodecPluginAdapterBase & that) { + MFXPluginAdapterBase :: SetupCallbacks(reinterpret_cast(that.m_mfxAPI.pthis), &m_codecPlg); + SetupCallbacks(); + return *this; + } + + private: + void SetupCallbacks() { + m_codecPlg.Query = _Query; + m_codecPlg.QueryIOSize = _QueryIOSize; + m_codecPlg.Init = _Init; + m_codecPlg.Reset = _Reset; + m_codecPlg.Close = _Close; + m_codecPlg.GetAudioParam = _GetAudioParam; + } + static mfxStatus _Query(mfxHDL pthis, mfxAudioParam *in, mfxAudioParam *out) { + return reinterpret_cast(pthis)->Query(in, out); + } + static mfxStatus _QueryIOSize(mfxHDL pthis, mfxAudioParam *par, mfxAudioAllocRequest *request){ + return reinterpret_cast(pthis)->QueryIOSize(par, request); + } + static mfxStatus _Init(mfxHDL pthis, mfxAudioParam *par){ + return reinterpret_cast(pthis)->Init(par); + } + static mfxStatus _Reset(mfxHDL pthis, mfxAudioParam *par){ + return reinterpret_cast(pthis)->Reset(par); + } + static mfxStatus _Close(mfxHDL pthis) { + return reinterpret_cast(pthis)->Close(); + } + static mfxStatus _GetAudioParam(mfxHDL pthis, mfxAudioParam *par) { + return reinterpret_cast(pthis)->GetAudioParam(par); + } + }; + + template + struct MFXPluginAdapterInternal{}; + template<> + class MFXPluginAdapterInternal : public MFXPluginAdapterBase + { + public: + MFXPluginAdapterInternal(MFXGenericPlugin *pPlugin) + : MFXPluginAdapterBase(pPlugin) + { + m_mfxAPI.Submit = _Submit; + } + MFXPluginAdapterInternal(const MFXPluginAdapterInternal & that ) + : MFXPluginAdapterBase(that) { + m_mfxAPI.Submit = that._Submit; + } + MFXPluginAdapterInternal& operator = (const MFXPluginAdapterInternal & that) { + MFXPluginAdapterBase::operator=(that); + m_mfxAPI.Submit = that._Submit; + return *this; + } + + private: + static mfxStatus _Submit(mfxHDL pthis, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task) { + return reinterpret_cast(pthis)->Submit(in, in_num, out, out_num, task); + } + }; + + template<> + class MFXPluginAdapterInternal : public MFXCodecPluginAdapterBase + { + public: + MFXPluginAdapterInternal(MFXDecoderPlugin *pPlugin) + : MFXCodecPluginAdapterBase(pPlugin) + { + SetupCallbacks(); + } + + MFXPluginAdapterInternal(const MFXPluginAdapterInternal & that) + : MFXCodecPluginAdapterBase(that) { + SetupCallbacks(); + } + + MFXPluginAdapterInternal& operator = (const MFXPluginAdapterInternal & that) { + MFXCodecPluginAdapterBase::operator=(that); + SetupCallbacks(); + return *this; + } + + private: + void SetupCallbacks() { + m_codecPlg.DecodeHeader = _DecodeHeader; + m_codecPlg.GetPayload = _GetPayload; + m_codecPlg.DecodeFrameSubmit = _DecodeFrameSubmit; + } + static mfxStatus _DecodeHeader(mfxHDL pthis, mfxBitstream *bs, mfxVideoParam *par) { + return reinterpret_cast(pthis)->DecodeHeader(bs, par); + } + static mfxStatus _GetPayload(mfxHDL pthis, mfxU64 *ts, mfxPayload *payload) { + return reinterpret_cast(pthis)->GetPayload(ts, payload); + } + static mfxStatus _DecodeFrameSubmit(mfxHDL pthis, mfxBitstream *bs, mfxFrameSurface1 *surface_work, mfxFrameSurface1 **surface_out, mfxThreadTask *task) { + return reinterpret_cast(pthis)->DecodeFrameSubmit(bs, surface_work, surface_out, task); + } + }; + + template<> + class MFXPluginAdapterInternal : public MFXAudioCodecPluginAdapterBase + { + public: + MFXPluginAdapterInternal(MFXAudioDecoderPlugin *pPlugin) + : MFXAudioCodecPluginAdapterBase(pPlugin) + { + SetupCallbacks(); + } + + MFXPluginAdapterInternal(const MFXPluginAdapterInternal & that) + : MFXAudioCodecPluginAdapterBase(that) { + SetupCallbacks(); + } + + MFXPluginAdapterInternal& operator = (const MFXPluginAdapterInternal & that) { + MFXAudioCodecPluginAdapterBase::operator=(that); + SetupCallbacks(); + return *this; + } + + private: + void SetupCallbacks() { + m_codecPlg.DecodeHeader = _DecodeHeader; +// m_codecPlg.GetPayload = _GetPayload; + m_codecPlg.DecodeFrameSubmit = _DecodeFrameSubmit; + } + static mfxStatus _DecodeHeader(mfxHDL pthis, mfxBitstream *bs, mfxAudioParam *par) { + return reinterpret_cast(pthis)->DecodeHeader(bs, par); + } +// static mfxStatus _GetPayload(mfxHDL pthis, mfxU64 *ts, mfxPayload *payload) { + // return reinterpret_cast(pthis)->GetPayload(ts, payload); + // } + static mfxStatus _DecodeFrameSubmit(mfxHDL pthis, mfxBitstream *in, mfxAudioFrame *out, mfxThreadTask *task) { + return reinterpret_cast(pthis)->DecodeFrameSubmit(in, out, task); + } + }; + + template<> + class MFXPluginAdapterInternal : public MFXCodecPluginAdapterBase + { + public: + MFXPluginAdapterInternal(MFXEncoderPlugin *pPlugin) + : MFXCodecPluginAdapterBase(pPlugin) + { + m_codecPlg.EncodeFrameSubmit = _EncodeFrameSubmit; + } + MFXPluginAdapterInternal(const MFXPluginAdapterInternal & that) + : MFXCodecPluginAdapterBase(that) { + m_codecPlg.EncodeFrameSubmit = _EncodeFrameSubmit; + } + + MFXPluginAdapterInternal& operator = (const MFXPluginAdapterInternal & that) { + MFXCodecPluginAdapterBase::operator = (that); + m_codecPlg.EncodeFrameSubmit = _EncodeFrameSubmit; + return *this; + } + + private: + static mfxStatus _EncodeFrameSubmit(mfxHDL pthis, mfxEncodeCtrl *ctrl, mfxFrameSurface1 *surface, mfxBitstream *bs, mfxThreadTask *task) { + return reinterpret_cast(pthis)->EncodeFrameSubmit(ctrl, surface, bs, task); + } + }; + + template<> + class MFXPluginAdapterInternal : public MFXAudioCodecPluginAdapterBase + { + public: + MFXPluginAdapterInternal(MFXAudioEncoderPlugin *pPlugin) + : MFXAudioCodecPluginAdapterBase(pPlugin) + { + SetupCallbacks(); + } + + MFXPluginAdapterInternal(const MFXPluginAdapterInternal & that) + : MFXAudioCodecPluginAdapterBase(that) { + SetupCallbacks(); + } + + MFXPluginAdapterInternal& operator = (const MFXPluginAdapterInternal & that) { + MFXAudioCodecPluginAdapterBase::operator=(that); + SetupCallbacks(); + return *this; + } + + private: + void SetupCallbacks() { + m_codecPlg.EncodeFrameSubmit = _EncodeFrameSubmit; + } + static mfxStatus _EncodeFrameSubmit(mfxHDL pthis, mfxAudioFrame *aFrame, mfxBitstream *out, mfxThreadTask *task) { + return reinterpret_cast(pthis)->EncodeFrameSubmit(aFrame, out, task); + } + }; + + template<> + class MFXPluginAdapterInternal : public MFXCodecPluginAdapterBase + { + public: + MFXPluginAdapterInternal(MFXEncPlugin *pPlugin) + : MFXCodecPluginAdapterBase(pPlugin) + { + m_codecPlg.ENCFrameSubmit = _ENCFrameSubmit; + } + MFXPluginAdapterInternal(const MFXPluginAdapterInternal & that) + : MFXCodecPluginAdapterBase(that) { + m_codecPlg.ENCFrameSubmit = _ENCFrameSubmit; + } + + MFXPluginAdapterInternal& operator = (const MFXPluginAdapterInternal & that) { + MFXCodecPluginAdapterBase::operator = (that); + m_codecPlg.ENCFrameSubmit = _ENCFrameSubmit; + return *this; + } + + private: + static mfxStatus _ENCFrameSubmit(mfxHDL pthis,mfxENCInput *in, mfxENCOutput *out, mfxThreadTask *task) { + return reinterpret_cast(pthis)->EncFrameSubmit(in, out, task); + } + }; + + + template<> + class MFXPluginAdapterInternal : public MFXCodecPluginAdapterBase + { + public: + MFXPluginAdapterInternal(MFXVPPPlugin *pPlugin) + : MFXCodecPluginAdapterBase(pPlugin) + { + SetupCallbacks(); + } + MFXPluginAdapterInternal(const MFXPluginAdapterInternal & that) + : MFXCodecPluginAdapterBase(that) { + SetupCallbacks(); + } + + MFXPluginAdapterInternal& operator = (const MFXPluginAdapterInternal & that) { + MFXCodecPluginAdapterBase::operator = (that); + SetupCallbacks(); + return *this; + } + + private: + void SetupCallbacks() { + m_codecPlg.VPPFrameSubmit = _VPPFrameSubmit; + m_codecPlg.VPPFrameSubmitEx = _VPPFrameSubmitEx; + } + static mfxStatus _VPPFrameSubmit(mfxHDL pthis, mfxFrameSurface1 *surface_in, mfxFrameSurface1 *surface_out, mfxExtVppAuxData *aux, mfxThreadTask *task) { + return reinterpret_cast(pthis)->VPPFrameSubmit(surface_in, surface_out, aux, task); + } + static mfxStatus _VPPFrameSubmitEx(mfxHDL pthis, mfxFrameSurface1 *surface_in, mfxFrameSurface1 *surface_work, mfxFrameSurface1 **surface_out, mfxThreadTask *task) { + return reinterpret_cast(pthis)->VPPFrameSubmitEx(surface_in, surface_work, surface_out, task); + } + }; +} + +/* adapter for particular plugin type*/ +template +class MFXPluginAdapter +{ +public: + detail::MFXPluginAdapterInternal m_Adapter; + + operator mfxPlugin () const { + return m_Adapter.operator mfxPlugin(); + } + + MFXPluginAdapter(T* pPlugin = NULL) + : m_Adapter(pPlugin) + { + } +}; + +template +inline MFXPluginAdapter make_mfx_plugin_adapter(T* pPlugin) { + + MFXPluginAdapter adapt(pPlugin); + return adapt; +} + +#endif // __MFXPLUGINPLUSPLUS_H diff --git a/vshampor/deshuffler/msdk_api/include/mfxplugin.h b/vshampor/deshuffler/msdk_api/include/mfxplugin.h new file mode 100644 index 0000000..7f84aa0 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxplugin.h @@ -0,0 +1,203 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXPLUGIN_H__ +#define __MFXPLUGIN_H__ +#include "mfxvideo.h" +#include "mfxaudio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +typedef struct { + mfxU8 Data[16]; +} mfxPluginUID; + +static const mfxPluginUID MFX_PLUGINID_HEVCD_SW = {{0x15, 0xdd, 0x93, 0x68, 0x25, 0xad, 0x47, 0x5e, 0xa3, 0x4e, 0x35, 0xf3, 0xf5, 0x42, 0x17, 0xa6}}; +static const mfxPluginUID MFX_PLUGINID_HEVCD_HW = {{0x33, 0xa6, 0x1c, 0x0b, 0x4c, 0x27, 0x45, 0x4c, 0xa8, 0xd8, 0x5d, 0xde, 0x75, 0x7c, 0x6f, 0x8e}}; +static const mfxPluginUID MFX_PLUGINID_HEVCE_SW = {{0x2f, 0xca, 0x99, 0x74, 0x9f, 0xdb, 0x49, 0xae, 0xb1, 0x21, 0xa5, 0xb6, 0x3e, 0xf5, 0x68, 0xf7}}; +static const mfxPluginUID MFX_PLUGINID_HEVCE_GACC = {{0xe5, 0x40, 0x0a, 0x06, 0xc7, 0x4d, 0x41, 0xf5, 0xb1, 0x2d, 0x43, 0x0b, 0xba, 0xa2, 0x3d, 0x0b}}; +static const mfxPluginUID MFX_PLUGINID_HEVCE_DP_GACC = {{0x2b, 0xad, 0x6f, 0x9d, 0x77, 0x54, 0x41, 0x2d, 0xbf, 0x63, 0x03, 0xed, 0x4b, 0xb5, 0x09, 0x68}}; +static const mfxPluginUID MFX_PLUGINID_HEVCE_HW = {{0x6f, 0xad, 0xc7, 0x91, 0xa0, 0xc2, 0xeb, 0x47, 0x9a, 0xb6, 0xdc, 0xd5, 0xea, 0x9d, 0xa3, 0x47}}; +static const mfxPluginUID MFX_PLUGINID_VP8D_HW = {{0xf6, 0x22, 0x39, 0x4d, 0x8d, 0x87, 0x45, 0x2f, 0x87, 0x8c, 0x51, 0xf2, 0xfc, 0x9b, 0x41, 0x31}}; +static const mfxPluginUID MFX_PLUGINID_VP8E_HW = {{0xbf, 0xfc, 0x51, 0x8c, 0xde, 0x13, 0x4d, 0xf9, 0x8a, 0x96, 0xf4, 0xcf, 0x81, 0x6c, 0x0f, 0xac}}; +static const mfxPluginUID MFX_PLUGINID_VP9E_HW = {{0xce, 0x44, 0xef, 0x6f, 0x1a, 0x6d, 0x22, 0x46, 0xb4, 0x12, 0xbb, 0x38, 0xd6, 0xe4, 0x51, 0x82}}; +static const mfxPluginUID MFX_PLUGINID_VP9D_HW = {{0xa9, 0x22, 0x39, 0x4d, 0x8d, 0x87, 0x45, 0x2f, 0x87, 0x8c, 0x51, 0xf2, 0xfc, 0x9b, 0x41, 0x31}}; +static const mfxPluginUID MFX_PLUGINID_CAMERA_HW = {{0x54, 0x54, 0x26, 0x16, 0x24, 0x33, 0x41, 0xe6, 0x93, 0xae, 0x89, 0x99, 0x42, 0xce, 0x73, 0x55}}; +static const mfxPluginUID MFX_PLUGINID_CAPTURE_HW = {{0x22, 0xd6, 0x2c, 0x07, 0xe6, 0x72, 0x40, 0x8f, 0xbb, 0x4c, 0xc2, 0x0e, 0xd7, 0xa0, 0x53, 0xe4}}; +static const mfxPluginUID MFX_PLUGINID_ITELECINE_HW = {{0xe7, 0x44, 0x75, 0x3a, 0xcd, 0x74, 0x40, 0x2e, 0x89, 0xa2, 0xee, 0x06, 0x35, 0x49, 0x61, 0x79}}; +static const mfxPluginUID MFX_PLUGINID_H264LA_HW = {{0x58, 0x8f, 0x11, 0x85, 0xd4, 0x7b, 0x42, 0x96, 0x8d, 0xea, 0x37, 0x7b, 0xb5, 0xd0, 0xdc, 0xb4}}; +static const mfxPluginUID MFX_PLUGINID_AACD = {{0xe9, 0x34, 0x67, 0x25, 0xac, 0x2f, 0x4c, 0x93, 0xaa, 0x58, 0x5c, 0x11, 0xc7, 0x08, 0x7c, 0xf4}}; +static const mfxPluginUID MFX_PLUGINID_AACE = {{0xb2, 0xa2, 0xa0, 0x5a, 0x4e, 0xac, 0x46, 0xbf, 0xa9, 0xde, 0x7e, 0x80, 0xc9, 0x8d, 0x2e, 0x18}}; +static const mfxPluginUID MFX_PLUGINID_HEVCE_FEI_HW = {{0x87, 0xe0, 0xe8, 0x02, 0x07, 0x37, 0x52, 0x40, 0x85, 0x25, 0x15, 0xcf, 0x4a, 0x5e, 0xdd, 0xe6}}; +#if (MFX_VERSION >= 1027) +static const mfxPluginUID MFX_PLUGINID_HEVC_FEI_ENCODE = {{0x54, 0x18, 0xa7, 0x06, 0x66, 0xf9, 0x4d, 0x5c, 0xb4, 0xf7, 0xb1, 0xca, 0xee, 0x86, 0x33, 0x9b}}; +#endif + + +typedef enum { + MFX_PLUGINTYPE_VIDEO_GENERAL = 0, + MFX_PLUGINTYPE_VIDEO_DECODE = 1, + MFX_PLUGINTYPE_VIDEO_ENCODE = 2, + MFX_PLUGINTYPE_VIDEO_VPP = 3, + MFX_PLUGINTYPE_VIDEO_ENC = 4, + MFX_PLUGINTYPE_AUDIO_DECODE = 5, + MFX_PLUGINTYPE_AUDIO_ENCODE = 6 +} mfxPluginType; + +typedef enum { + MFX_THREADPOLICY_SERIAL = 0, + MFX_THREADPOLICY_PARALLEL = 1 +} mfxThreadPolicy; + +typedef struct mfxPluginParam { + mfxU32 reserved[6]; + mfxU16 reserved1; + mfxU16 PluginVersion; + mfxVersion APIVersion; + mfxPluginUID PluginUID; + mfxU32 Type; + mfxU32 CodecId; + mfxThreadPolicy ThreadPolicy; + mfxU32 MaxThreadNum; +} mfxPluginParam; + +typedef struct mfxCoreParam{ + mfxU32 reserved[13]; + mfxIMPL Impl; + mfxVersion Version; + mfxU32 NumWorkingThread; +} mfxCoreParam; + +typedef struct mfxCoreInterface { + mfxHDL pthis; + + mfxHDL reserved1[2]; + mfxFrameAllocator FrameAllocator; + mfxBufferAllocator reserved3; + + mfxStatus (MFX_CDECL *GetCoreParam)(mfxHDL pthis, mfxCoreParam *par); + mfxStatus (MFX_CDECL *GetHandle) (mfxHDL pthis, mfxHandleType type, mfxHDL *handle); + mfxStatus (MFX_CDECL *IncreaseReference) (mfxHDL pthis, mfxFrameData *fd); + mfxStatus (MFX_CDECL *DecreaseReference) (mfxHDL pthis, mfxFrameData *fd); + mfxStatus (MFX_CDECL *CopyFrame) (mfxHDL pthis, mfxFrameSurface1 *dst, mfxFrameSurface1 *src); + mfxStatus (MFX_CDECL *CopyBuffer)(mfxHDL pthis, mfxU8 *dst, mfxU32 size, mfxFrameSurface1 *src); + + mfxStatus (MFX_CDECL *MapOpaqueSurface)(mfxHDL pthis, mfxU32 num, mfxU32 type, mfxFrameSurface1 **op_surf); + mfxStatus (MFX_CDECL *UnmapOpaqueSurface)(mfxHDL pthis, mfxU32 num, mfxU32 type, mfxFrameSurface1 **op_surf); + + mfxStatus (MFX_CDECL *GetRealSurface)(mfxHDL pthis, mfxFrameSurface1 *op_surf, mfxFrameSurface1 **surf); + mfxStatus (MFX_CDECL *GetOpaqueSurface)(mfxHDL pthis, mfxFrameSurface1 *surf, mfxFrameSurface1 **op_surf); + + mfxStatus (MFX_CDECL *CreateAccelerationDevice)(mfxHDL pthis, mfxHandleType type, mfxHDL *handle); + mfxStatus (MFX_CDECL *GetFrameHandle) (mfxHDL pthis, mfxFrameData *fd, mfxHDL *handle); + mfxStatus (MFX_CDECL *QueryPlatform) (mfxHDL pthis, mfxPlatform *platform); + + mfxHDL reserved4[1]; +} mfxCoreInterface; + +/* video codec plugin extension */ +typedef struct _mfxENCInput mfxENCInput; +typedef struct _mfxENCOutput mfxENCOutput; +typedef struct mfxVideoCodecPlugin{ + mfxStatus (MFX_CDECL *Query)(mfxHDL pthis, mfxVideoParam *in, mfxVideoParam *out); + mfxStatus (MFX_CDECL *QueryIOSurf)(mfxHDL pthis, mfxVideoParam *par, mfxFrameAllocRequest *in, mfxFrameAllocRequest *out); + mfxStatus (MFX_CDECL *Init)(mfxHDL pthis, mfxVideoParam *par); + mfxStatus (MFX_CDECL *Reset)(mfxHDL pthis, mfxVideoParam *par); + mfxStatus (MFX_CDECL *Close)(mfxHDL pthis); + mfxStatus (MFX_CDECL *GetVideoParam)(mfxHDL pthis, mfxVideoParam *par); + + mfxStatus (MFX_CDECL *EncodeFrameSubmit)(mfxHDL pthis, mfxEncodeCtrl *ctrl, mfxFrameSurface1 *surface, mfxBitstream *bs, mfxThreadTask *task); + + mfxStatus (MFX_CDECL *DecodeHeader)(mfxHDL pthis, mfxBitstream *bs, mfxVideoParam *par); + mfxStatus (MFX_CDECL *GetPayload)(mfxHDL pthis, mfxU64 *ts, mfxPayload *payload); + mfxStatus (MFX_CDECL *DecodeFrameSubmit)(mfxHDL pthis, mfxBitstream *bs, mfxFrameSurface1 *surface_work, mfxFrameSurface1 **surface_out, mfxThreadTask *task); + + mfxStatus (MFX_CDECL *VPPFrameSubmit)(mfxHDL pthis, mfxFrameSurface1 *in, mfxFrameSurface1 *out, mfxExtVppAuxData *aux, mfxThreadTask *task); + mfxStatus (MFX_CDECL *VPPFrameSubmitEx)(mfxHDL pthis, mfxFrameSurface1 *in, mfxFrameSurface1 *surface_work, mfxFrameSurface1 **surface_out, mfxThreadTask *task); + + mfxStatus (MFX_CDECL *ENCFrameSubmit)(mfxHDL pthis, mfxENCInput *in, mfxENCOutput *out, mfxThreadTask *task); + + mfxHDL reserved1[3]; + mfxU32 reserved2[8]; +} mfxVideoCodecPlugin; + +typedef struct mfxAudioCodecPlugin{ + mfxStatus (MFX_CDECL *Query)(mfxHDL pthis, mfxAudioParam *in, mfxAudioParam *out); + mfxStatus (MFX_CDECL *QueryIOSize)(mfxHDL pthis, mfxAudioParam *par, mfxAudioAllocRequest *request); + mfxStatus (MFX_CDECL *Init)(mfxHDL pthis, mfxAudioParam *par); + mfxStatus (MFX_CDECL *Reset)(mfxHDL pthis, mfxAudioParam *par); + mfxStatus (MFX_CDECL *Close)(mfxHDL pthis); + mfxStatus (MFX_CDECL *GetAudioParam)(mfxHDL pthis, mfxAudioParam *par); + + mfxStatus (MFX_CDECL *EncodeFrameSubmit)(mfxHDL pthis, mfxAudioFrame *aFrame, mfxBitstream *out, mfxThreadTask *task); + + mfxStatus (MFX_CDECL *DecodeHeader)(mfxHDL pthis, mfxBitstream *bs, mfxAudioParam *par); +// mfxStatus (MFX_CDECL *GetPayload)(mfxHDL pthis, mfxU64 *ts, mfxPayload *payload); + mfxStatus (MFX_CDECL *DecodeFrameSubmit)(mfxHDL pthis, mfxBitstream *in, mfxAudioFrame *out, mfxThreadTask *task); + + mfxHDL reserved1[6]; + mfxU32 reserved2[8]; +} mfxAudioCodecPlugin; + +typedef struct mfxPlugin{ + mfxHDL pthis; + + mfxStatus (MFX_CDECL *PluginInit) (mfxHDL pthis, mfxCoreInterface *core); + mfxStatus (MFX_CDECL *PluginClose) (mfxHDL pthis); + + mfxStatus (MFX_CDECL *GetPluginParam)(mfxHDL pthis, mfxPluginParam *par); + + mfxStatus (MFX_CDECL *Submit)(mfxHDL pthis, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task); + mfxStatus (MFX_CDECL *Execute)(mfxHDL pthis, mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a); + mfxStatus (MFX_CDECL *FreeResources)(mfxHDL pthis, mfxThreadTask task, mfxStatus sts); + + union { + mfxVideoCodecPlugin *Video; + mfxAudioCodecPlugin *Audio; + }; + + mfxHDL reserved[8]; +} mfxPlugin; + + + +mfxStatus MFX_CDECL MFXVideoUSER_Register(mfxSession session, mfxU32 type, const mfxPlugin *par); +mfxStatus MFX_CDECL MFXVideoUSER_Unregister(mfxSession session, mfxU32 type); +mfxStatus MFX_CDECL MFXVideoUSER_GetPlugin(mfxSession session, mfxU32 type, mfxPlugin *par); +mfxStatus MFX_CDECL MFXVideoUSER_ProcessFrameAsync(mfxSession session, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxSyncPoint *syncp); + +mfxStatus MFX_CDECL MFXVideoUSER_Load(mfxSession session, const mfxPluginUID *uid, mfxU32 version); +mfxStatus MFX_CDECL MFXVideoUSER_LoadByPath(mfxSession session, const mfxPluginUID *uid, mfxU32 version, const mfxChar *path, mfxU32 len); +mfxStatus MFX_CDECL MFXVideoUSER_UnLoad(mfxSession session, const mfxPluginUID *uid); + +mfxStatus MFX_CDECL MFXAudioUSER_Register(mfxSession session, mfxU32 type, const mfxPlugin *par); +mfxStatus MFX_CDECL MFXAudioUSER_Unregister(mfxSession session, mfxU32 type); +mfxStatus MFX_CDECL MFXAudioUSER_ProcessFrameAsync(mfxSession session, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxSyncPoint *syncp); + +mfxStatus MFX_CDECL MFXAudioUSER_Load(mfxSession session, const mfxPluginUID *uid, mfxU32 version); +mfxStatus MFX_CDECL MFXAudioUSER_UnLoad(mfxSession session, const mfxPluginUID *uid); + +#ifdef __cplusplus +} // extern "C" +#endif /* __cplusplus */ + +#endif /* __MFXPLUGIN_H__ */ diff --git a/vshampor/deshuffler/msdk_api/include/mfxsc.h b/vshampor/deshuffler/msdk_api/include/mfxsc.h new file mode 100644 index 0000000..a4f54c9 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxsc.h @@ -0,0 +1,52 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXSC_H__ +#define __MFXSC_H__ +#include "mfxdefs.h" +#include "mfxvstructures.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* Extended Buffer Ids */ +enum +{ + MFX_EXTBUFF_SCREEN_CAPTURE_PARAM = MFX_MAKEFOURCC('S','C','P','A') +}; + +typedef struct +{ + mfxExtBuffer Header; + + mfxU32 DisplayIndex; + mfxU16 EnableDirtyRect; + mfxU16 EnableCursorCapture; + mfxU16 reserved[24]; +} mfxExtScreenCaptureParam; + +#ifdef __cplusplus +} // extern "C" +#endif /* __cplusplus */ + + +#endif + diff --git a/vshampor/deshuffler/msdk_api/include/mfxscd.h b/vshampor/deshuffler/msdk_api/include/mfxscd.h new file mode 100644 index 0000000..21d453c --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxscd.h @@ -0,0 +1,59 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXSCD_H__ +#define __MFXSCD_H__ + +#include "mfxenc.h" +#include "mfxplugin.h" + +#define MFX_ENC_SCD_PLUGIN_VERSION 1 + +#ifdef __cplusplus +extern "C" { +#endif + +static const mfxPluginUID MFX_PLUGINID_ENC_SCD = {{ 0xdf, 0xc2, 0x15, 0xb3, 0xe3, 0xd3, 0x90, 0x4d, 0x7f, 0xa5, 0x04, 0x12, 0x7e, 0xf5, 0x64, 0xd5 }}; + +/* SCD Extended Buffer Ids */ +enum { + MFX_EXTBUFF_SCD = MFX_MAKEFOURCC('S','C','D',' ') +}; + +/* SceneType */ +enum { + MFX_SCD_SCENE_SAME = 0x00, + MFX_SCD_SCENE_NEW_FIELD_1 = 0x01, + MFX_SCD_SCENE_NEW_FIELD_2 = 0x02, + MFX_SCD_SCENE_NEW_PICTURE = MFX_SCD_SCENE_NEW_FIELD_1 | MFX_SCD_SCENE_NEW_FIELD_2 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 SceneType; + mfxU16 reserved[27]; +} mfxExtSCD; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif + diff --git a/vshampor/deshuffler/msdk_api/include/mfxsession.h b/vshampor/deshuffler/msdk_api/include/mfxsession.h new file mode 100644 index 0000000..60cc6d6 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxsession.h @@ -0,0 +1,50 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXSESSION_H__ +#define __MFXSESSION_H__ +#include "mfxcommon.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* Global Functions */ +typedef struct _mfxSession *mfxSession; +mfxStatus MFX_CDECL MFXInit(mfxIMPL impl, mfxVersion *ver, mfxSession *session); +mfxStatus MFX_CDECL MFXInitEx(mfxInitParam par, mfxSession *session); +mfxStatus MFX_CDECL MFXClose(mfxSession session); + +mfxStatus MFX_CDECL MFXQueryIMPL(mfxSession session, mfxIMPL *impl); +mfxStatus MFX_CDECL MFXQueryVersion(mfxSession session, mfxVersion *version); + +mfxStatus MFX_CDECL MFXJoinSession(mfxSession session, mfxSession child); +mfxStatus MFX_CDECL MFXDisjoinSession(mfxSession session); +mfxStatus MFX_CDECL MFXCloneSession(mfxSession session, mfxSession *clone); +mfxStatus MFX_CDECL MFXSetPriority(mfxSession session, mfxPriority priority); +mfxStatus MFX_CDECL MFXGetPriority(mfxSession session, mfxPriority *priority); +mfxStatus MFX_CDECL MFXDoWork(mfxSession session); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/vshampor/deshuffler/msdk_api/include/mfxstructures.h b/vshampor/deshuffler/msdk_api/include/mfxstructures.h new file mode 100644 index 0000000..346eeab --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxstructures.h @@ -0,0 +1,2131 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXSTRUCTURES_H__ +#define __MFXSTRUCTURES_H__ +#include "mfxcommon.h" + +#if !defined (__GNUC__) +#pragma warning(disable: 4201) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Frame ID for SVC and MVC */ +typedef struct { + mfxU16 TemporalId; + mfxU16 PriorityId; + union { + struct { + mfxU16 DependencyId; + mfxU16 QualityId; + }; + struct { + mfxU16 ViewId; + }; + }; +} mfxFrameId; + +#pragma pack(push, 4) +/* Frame Info */ +typedef struct { + mfxU32 reserved[4]; + mfxU16 reserved4; + mfxU16 BitDepthLuma; + mfxU16 BitDepthChroma; + mfxU16 Shift; + + mfxFrameId FrameId; + + mfxU32 FourCC; + union { + struct { /* Frame parameters */ + mfxU16 Width; + mfxU16 Height; + + mfxU16 CropX; + mfxU16 CropY; + mfxU16 CropW; + mfxU16 CropH; + }; + struct { /* Buffer parameters (for plain formats like P8) */ + mfxU64 BufferSize; + mfxU32 reserved5; + }; + }; + + mfxU32 FrameRateExtN; + mfxU32 FrameRateExtD; + mfxU16 reserved3; + + mfxU16 AspectRatioW; + mfxU16 AspectRatioH; + + mfxU16 PicStruct; + mfxU16 ChromaFormat; + mfxU16 reserved2; +} mfxFrameInfo; +#pragma pack(pop) + +/* FourCC */ +enum { + MFX_FOURCC_NV12 = MFX_MAKEFOURCC('N','V','1','2'), /* Native Format */ + MFX_FOURCC_YV12 = MFX_MAKEFOURCC('Y','V','1','2'), + MFX_FOURCC_NV16 = MFX_MAKEFOURCC('N','V','1','6'), + MFX_FOURCC_YUY2 = MFX_MAKEFOURCC('Y','U','Y','2'), +#if (MFX_VERSION >= MFX_VERSION_NEXT) + MFX_FOURCC_RGB565 = MFX_MAKEFOURCC('R','G','B','2'), + MFX_FOURCC_RGBP = MFX_MAKEFOURCC('R','G','B','P'), +#endif + MFX_FOURCC_RGB3 = MFX_MAKEFOURCC('R','G','B','3'), /* deprecated */ + MFX_FOURCC_RGB4 = MFX_MAKEFOURCC('R','G','B','4'), /* ARGB in that order, A channel is 8 MSBs */ + MFX_FOURCC_P8 = 41, /* D3DFMT_P8 */ + MFX_FOURCC_P8_TEXTURE = MFX_MAKEFOURCC('P','8','M','B'), + MFX_FOURCC_P010 = MFX_MAKEFOURCC('P','0','1','0'), +#if (MFX_VERSION >= MFX_VERSION_NEXT) + MFX_FOURCC_P016 = MFX_MAKEFOURCC('P','0','1','6'), +#endif + MFX_FOURCC_P210 = MFX_MAKEFOURCC('P','2','1','0'), + MFX_FOURCC_BGR4 = MFX_MAKEFOURCC('B','G','R','4'), /* ABGR in that order, A channel is 8 MSBs */ + MFX_FOURCC_A2RGB10 = MFX_MAKEFOURCC('R','G','1','0'), /* ARGB in that order, A channel is two MSBs */ + MFX_FOURCC_ARGB16 = MFX_MAKEFOURCC('R','G','1','6'), /* ARGB in that order, 64 bits, A channel is 16 MSBs */ + MFX_FOURCC_ABGR16 = MFX_MAKEFOURCC('B','G','1','6'), /* ABGR in that order, 64 bits, A channel is 16 MSBs */ + MFX_FOURCC_R16 = MFX_MAKEFOURCC('R','1','6','U'), + MFX_FOURCC_AYUV = MFX_MAKEFOURCC('A','Y','U','V'), /* YUV 4:4:4, AYUV in that order, A channel is 8 MSBs */ + MFX_FOURCC_AYUV_RGB4 = MFX_MAKEFOURCC('A','V','U','Y'), /* ARGB in that order, A channel is 8 MSBs stored in AYUV surface*/ + MFX_FOURCC_UYVY = MFX_MAKEFOURCC('U','Y','V','Y'), +#if (MFX_VERSION >= 1027) + MFX_FOURCC_Y210 = MFX_MAKEFOURCC('Y','2','1','0'), + MFX_FOURCC_Y410 = MFX_MAKEFOURCC('Y','4','1','0'), +#endif +#if (MFX_VERSION >= MFX_VERSION_NEXT) + MFX_FOURCC_Y216 = MFX_MAKEFOURCC('Y','2','1','6'), + MFX_FOURCC_Y416 = MFX_MAKEFOURCC('Y','4','1','6'), +#endif +}; + +/* PicStruct */ +enum { + MFX_PICSTRUCT_UNKNOWN =0x00, + MFX_PICSTRUCT_PROGRESSIVE =0x01, + MFX_PICSTRUCT_FIELD_TFF =0x02, + MFX_PICSTRUCT_FIELD_BFF =0x04, + + MFX_PICSTRUCT_FIELD_REPEATED=0x10, /* first field repeated, pic_struct=5 or 6 in H.264 */ + MFX_PICSTRUCT_FRAME_DOUBLING=0x20, /* pic_struct=7 in H.264 */ + MFX_PICSTRUCT_FRAME_TRIPLING=0x40, /* pic_struct=8 in H.264 */ + + MFX_PICSTRUCT_FIELD_SINGLE =0x100, + MFX_PICSTRUCT_FIELD_TOP =MFX_PICSTRUCT_FIELD_SINGLE | MFX_PICSTRUCT_FIELD_TFF, + MFX_PICSTRUCT_FIELD_BOTTOM =MFX_PICSTRUCT_FIELD_SINGLE | MFX_PICSTRUCT_FIELD_BFF, + MFX_PICSTRUCT_FIELD_PAIRED_PREV =0x200, + MFX_PICSTRUCT_FIELD_PAIRED_NEXT =0x400, +}; + +/* ColorFormat */ +enum { + MFX_CHROMAFORMAT_MONOCHROME =0, + MFX_CHROMAFORMAT_YUV420 =1, + MFX_CHROMAFORMAT_YUV422 =2, + MFX_CHROMAFORMAT_YUV444 =3, + MFX_CHROMAFORMAT_YUV400 = MFX_CHROMAFORMAT_MONOCHROME, + MFX_CHROMAFORMAT_YUV411 = 4, + MFX_CHROMAFORMAT_YUV422H = MFX_CHROMAFORMAT_YUV422, + MFX_CHROMAFORMAT_YUV422V = 5, + MFX_CHROMAFORMAT_RESERVED1 = 6 +}; + +enum { + MFX_TIMESTAMP_UNKNOWN = -1 +}; + +enum { + MFX_FRAMEORDER_UNKNOWN = -1 +}; + +/* DataFlag in mfxFrameData */ +enum { + MFX_FRAMEDATA_ORIGINAL_TIMESTAMP = 0x0001 +}; + +/* Corrupted in mfxFrameData */ +enum { + MFX_CORRUPTION_MINOR = 0x0001, + MFX_CORRUPTION_MAJOR = 0x0002, + MFX_CORRUPTION_ABSENT_TOP_FIELD = 0x0004, + MFX_CORRUPTION_ABSENT_BOTTOM_FIELD = 0x0008, + MFX_CORRUPTION_REFERENCE_FRAME = 0x0010, + MFX_CORRUPTION_REFERENCE_LIST = 0x0020 +}; + +#if (MFX_VERSION >= 1027) +#pragma pack(push, 4) +typedef struct +{ + mfxU32 U : 10; + mfxU32 Y : 10; + mfxU32 V : 10; + mfxU32 A : 2; +} mfxY410; +#pragma pack(pop) +#endif + +#if (MFX_VERSION >= 1025) +#pragma pack(push, 4) +typedef struct +{ + mfxU32 B : 10; + mfxU32 G : 10; + mfxU32 R : 10; + mfxU32 A : 2; +} mfxA2RGB10; +#pragma pack(pop) + +#endif + +/* Frame Data Info */ +typedef struct { + union { + mfxExtBuffer **ExtParam; + mfxU64 reserved2; + }; + mfxU16 NumExtParam; + + mfxU16 reserved[9]; + mfxU16 MemType; + mfxU16 PitchHigh; + + mfxU64 TimeStamp; + mfxU32 FrameOrder; + mfxU16 Locked; + union{ + mfxU16 Pitch; + mfxU16 PitchLow; + }; + + /* color planes */ + union { + mfxU8 *Y; + mfxU16 *Y16; + mfxU8 *R; + }; + union { + mfxU8 *UV; /* for UV merged formats */ + mfxU8 *VU; /* for VU merged formats */ + mfxU8 *CbCr; /* for CbCr merged formats */ + mfxU8 *CrCb; /* for CrCb merged formats */ + mfxU8 *Cb; + mfxU8 *U; + mfxU16 *U16; + mfxU8 *G; +#if (MFX_VERSION >= 1027) + mfxY410 *Y410; /* for.U format (merged AVYU) */ +#endif + }; + union { + mfxU8 *Cr; + mfxU8 *V; + mfxU16 *V16; + mfxU8 *B; +#if (MFX_VERSION >= 1025) + mfxA2RGB10 *A2RGB10; /* for A2RGB10 format (merged ARGB) */ +#endif + }; + mfxU8 *A; + mfxMemId MemId; + + /* Additional Flags */ + mfxU16 Corrupted; + mfxU16 DataFlag; +} mfxFrameData; + +/* Frame Surface */ +typedef struct { + mfxU32 reserved[4]; + mfxFrameInfo Info; + mfxFrameData Data; +} mfxFrameSurface1; + +enum { + MFX_TIMESTAMPCALC_UNKNOWN = 0, + MFX_TIMESTAMPCALC_TELECINE = 1, +}; + +/* Transcoding Info */ +typedef struct { + mfxU32 reserved[7]; + + mfxU16 LowPower; + mfxU16 BRCParamMultiplier; + + mfxFrameInfo FrameInfo; + mfxU32 CodecId; + mfxU16 CodecProfile; + mfxU16 CodecLevel; + mfxU16 NumThread; + + union { + struct { /* Encoding Options */ + mfxU16 TargetUsage; + + mfxU16 GopPicSize; + mfxU16 GopRefDist; + mfxU16 GopOptFlag; + mfxU16 IdrInterval; + + mfxU16 RateControlMethod; + union { + mfxU16 InitialDelayInKB; + mfxU16 QPI; + mfxU16 Accuracy; + }; + mfxU16 BufferSizeInKB; + union { + mfxU16 TargetKbps; + mfxU16 QPP; + mfxU16 ICQQuality; + }; + union { + mfxU16 MaxKbps; + mfxU16 QPB; + mfxU16 Convergence; + }; + + mfxU16 NumSlice; + mfxU16 NumRefFrame; + mfxU16 EncodedOrder; + }; + struct { /* Decoding Options */ + mfxU16 DecodedOrder; + mfxU16 ExtendedPicStruct; + mfxU16 TimeStampCalc; + mfxU16 SliceGroupsPresent; + mfxU16 MaxDecFrameBuffering; + mfxU16 EnableReallocRequest; + mfxU16 reserved2[7]; + }; + struct { /* JPEG Decoding Options */ + mfxU16 JPEGChromaFormat; + mfxU16 Rotation; + mfxU16 JPEGColorFormat; + mfxU16 InterleavedDec; + mfxU8 SamplingFactorH[4]; + mfxU8 SamplingFactorV[4]; + mfxU16 reserved3[5]; + }; + struct { /* JPEG Encoding Options */ + mfxU16 Interleaved; + mfxU16 Quality; + mfxU16 RestartInterval; + mfxU16 reserved5[10]; + }; + }; +} mfxInfoMFX; + +typedef struct { + mfxU32 reserved[8]; + mfxFrameInfo In; + mfxFrameInfo Out; +} mfxInfoVPP; + +typedef struct { + mfxU32 AllocId; + mfxU32 reserved[2]; + mfxU16 reserved3; + mfxU16 AsyncDepth; + + union { + mfxInfoMFX mfx; + mfxInfoVPP vpp; + }; + mfxU16 Protected; + mfxU16 IOPattern; + mfxExtBuffer** ExtParam; + mfxU16 NumExtParam; + mfxU16 reserved2; +} mfxVideoParam; + +/* IOPattern */ +enum { + MFX_IOPATTERN_IN_VIDEO_MEMORY = 0x01, + MFX_IOPATTERN_IN_SYSTEM_MEMORY = 0x02, + MFX_IOPATTERN_IN_OPAQUE_MEMORY = 0x04, + MFX_IOPATTERN_OUT_VIDEO_MEMORY = 0x10, + MFX_IOPATTERN_OUT_SYSTEM_MEMORY = 0x20, + MFX_IOPATTERN_OUT_OPAQUE_MEMORY = 0x40 +}; + +/* CodecId */ +enum { + MFX_CODEC_AVC =MFX_MAKEFOURCC('A','V','C',' '), + MFX_CODEC_HEVC =MFX_MAKEFOURCC('H','E','V','C'), + MFX_CODEC_MPEG2 =MFX_MAKEFOURCC('M','P','G','2'), + MFX_CODEC_VC1 =MFX_MAKEFOURCC('V','C','1',' '), + MFX_CODEC_CAPTURE =MFX_MAKEFOURCC('C','A','P','T'), + MFX_CODEC_VP9 =MFX_MAKEFOURCC('V','P','9',' '), + MFX_CODEC_AV1 =MFX_MAKEFOURCC('A','V','1',' ') +}; + +/* CodecProfile, CodecLevel */ +enum { + MFX_PROFILE_UNKNOWN =0, + MFX_LEVEL_UNKNOWN =0, + + /* AVC Profiles & Levels */ + MFX_PROFILE_AVC_CONSTRAINT_SET0 = (0x100 << 0), + MFX_PROFILE_AVC_CONSTRAINT_SET1 = (0x100 << 1), + MFX_PROFILE_AVC_CONSTRAINT_SET2 = (0x100 << 2), + MFX_PROFILE_AVC_CONSTRAINT_SET3 = (0x100 << 3), + MFX_PROFILE_AVC_CONSTRAINT_SET4 = (0x100 << 4), + MFX_PROFILE_AVC_CONSTRAINT_SET5 = (0x100 << 5), + + MFX_PROFILE_AVC_BASELINE =66, + MFX_PROFILE_AVC_MAIN =77, + MFX_PROFILE_AVC_EXTENDED =88, + MFX_PROFILE_AVC_HIGH =100, + MFX_PROFILE_AVC_HIGH_422 =122, + MFX_PROFILE_AVC_CONSTRAINED_BASELINE =MFX_PROFILE_AVC_BASELINE + MFX_PROFILE_AVC_CONSTRAINT_SET1, + MFX_PROFILE_AVC_CONSTRAINED_HIGH =MFX_PROFILE_AVC_HIGH + MFX_PROFILE_AVC_CONSTRAINT_SET4 + + MFX_PROFILE_AVC_CONSTRAINT_SET5, + MFX_PROFILE_AVC_PROGRESSIVE_HIGH =MFX_PROFILE_AVC_HIGH + MFX_PROFILE_AVC_CONSTRAINT_SET4, + + MFX_LEVEL_AVC_1 =10, + MFX_LEVEL_AVC_1b =9, + MFX_LEVEL_AVC_11 =11, + MFX_LEVEL_AVC_12 =12, + MFX_LEVEL_AVC_13 =13, + MFX_LEVEL_AVC_2 =20, + MFX_LEVEL_AVC_21 =21, + MFX_LEVEL_AVC_22 =22, + MFX_LEVEL_AVC_3 =30, + MFX_LEVEL_AVC_31 =31, + MFX_LEVEL_AVC_32 =32, + MFX_LEVEL_AVC_4 =40, + MFX_LEVEL_AVC_41 =41, + MFX_LEVEL_AVC_42 =42, + MFX_LEVEL_AVC_5 =50, + MFX_LEVEL_AVC_51 =51, + MFX_LEVEL_AVC_52 =52, + + /* MPEG-2 Profiles & Levels */ + MFX_PROFILE_MPEG2_SIMPLE =0x50, + MFX_PROFILE_MPEG2_MAIN =0x40, + MFX_PROFILE_MPEG2_HIGH =0x10, + + MFX_LEVEL_MPEG2_LOW =0xA, + MFX_LEVEL_MPEG2_MAIN =0x8, + MFX_LEVEL_MPEG2_HIGH =0x4, + MFX_LEVEL_MPEG2_HIGH1440 =0x6, + + /* VC1 Profiles & Levels */ + MFX_PROFILE_VC1_SIMPLE =(0+1), + MFX_PROFILE_VC1_MAIN =(4+1), + MFX_PROFILE_VC1_ADVANCED =(12+1), + + /* VC1 levels for simple & main profiles */ + MFX_LEVEL_VC1_LOW =(0+1), + MFX_LEVEL_VC1_MEDIAN =(2+1), + MFX_LEVEL_VC1_HIGH =(4+1), + + /* VC1 levels for the advanced profile */ + MFX_LEVEL_VC1_0 =(0x00+1), + MFX_LEVEL_VC1_1 =(0x01+1), + MFX_LEVEL_VC1_2 =(0x02+1), + MFX_LEVEL_VC1_3 =(0x03+1), + MFX_LEVEL_VC1_4 =(0x04+1), + + /* HEVC Profiles & Levels & Tiers */ + MFX_PROFILE_HEVC_MAIN =1, + MFX_PROFILE_HEVC_MAIN10 =2, + MFX_PROFILE_HEVC_MAINSP =3, + MFX_PROFILE_HEVC_REXT =4, + + MFX_LEVEL_HEVC_1 = 10, + MFX_LEVEL_HEVC_2 = 20, + MFX_LEVEL_HEVC_21 = 21, + MFX_LEVEL_HEVC_3 = 30, + MFX_LEVEL_HEVC_31 = 31, + MFX_LEVEL_HEVC_4 = 40, + MFX_LEVEL_HEVC_41 = 41, + MFX_LEVEL_HEVC_5 = 50, + MFX_LEVEL_HEVC_51 = 51, + MFX_LEVEL_HEVC_52 = 52, + MFX_LEVEL_HEVC_6 = 60, + MFX_LEVEL_HEVC_61 = 61, + MFX_LEVEL_HEVC_62 = 62, + + MFX_TIER_HEVC_MAIN = 0, + MFX_TIER_HEVC_HIGH = 0x100, + + /* VP9 Profiles */ + MFX_PROFILE_VP9_0 = 1, + MFX_PROFILE_VP9_1 = 2, + MFX_PROFILE_VP9_2 = 3, + MFX_PROFILE_VP9_3 = 4, + +}; + +/* GopOptFlag */ +enum { + MFX_GOP_CLOSED =1, + MFX_GOP_STRICT =2 +}; + +/* TargetUsages: from 1 to 7 inclusive */ +enum { + MFX_TARGETUSAGE_1 =1, + MFX_TARGETUSAGE_2 =2, + MFX_TARGETUSAGE_3 =3, + MFX_TARGETUSAGE_4 =4, + MFX_TARGETUSAGE_5 =5, + MFX_TARGETUSAGE_6 =6, + MFX_TARGETUSAGE_7 =7, + + MFX_TARGETUSAGE_UNKNOWN =0, + MFX_TARGETUSAGE_BEST_QUALITY =MFX_TARGETUSAGE_1, + MFX_TARGETUSAGE_BALANCED =MFX_TARGETUSAGE_4, + MFX_TARGETUSAGE_BEST_SPEED =MFX_TARGETUSAGE_7 +}; + +/* RateControlMethod */ +enum { + MFX_RATECONTROL_CBR =1, + MFX_RATECONTROL_VBR =2, + MFX_RATECONTROL_CQP =3, + MFX_RATECONTROL_AVBR =4, + MFX_RATECONTROL_RESERVED1 =5, + MFX_RATECONTROL_RESERVED2 =6, + MFX_RATECONTROL_RESERVED3 =100, + MFX_RATECONTROL_RESERVED4 =7, + MFX_RATECONTROL_LA =8, + MFX_RATECONTROL_ICQ =9, + MFX_RATECONTROL_VCM =10, + MFX_RATECONTROL_LA_ICQ =11, + MFX_RATECONTROL_LA_EXT =12, + MFX_RATECONTROL_LA_HRD =13, + MFX_RATECONTROL_QVBR =14, +}; + +/* Trellis control*/ +enum { + MFX_TRELLIS_UNKNOWN =0, + MFX_TRELLIS_OFF =0x01, + MFX_TRELLIS_I =0x02, + MFX_TRELLIS_P =0x04, + MFX_TRELLIS_B =0x08 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 reserved1; + mfxU16 RateDistortionOpt; /* tri-state option */ + mfxU16 MECostType; + mfxU16 MESearchType; + mfxI16Pair MVSearchWindow; + mfxU16 EndOfSequence; /* tri-state option */ + mfxU16 FramePicture; /* tri-state option */ + + mfxU16 CAVLC; /* tri-state option */ + mfxU16 reserved2[2]; + mfxU16 RecoveryPointSEI; /* tri-state option */ + mfxU16 ViewOutput; /* tri-state option */ + mfxU16 NalHrdConformance; /* tri-state option */ + mfxU16 SingleSeiNalUnit; /* tri-state option */ + mfxU16 VuiVclHrdParameters; /* tri-state option */ + + mfxU16 RefPicListReordering; /* tri-state option */ + mfxU16 ResetRefList; /* tri-state option */ + mfxU16 RefPicMarkRep; /* tri-state option */ + mfxU16 FieldOutput; /* tri-state option */ + + mfxU16 IntraPredBlockSize; + mfxU16 InterPredBlockSize; + mfxU16 MVPrecision; + mfxU16 MaxDecFrameBuffering; + + mfxU16 AUDelimiter; /* tri-state option */ + mfxU16 EndOfStream; /* tri-state option */ + mfxU16 PicTimingSEI; /* tri-state option */ + mfxU16 VuiNalHrdParameters; /* tri-state option */ +} mfxExtCodingOption; + +enum { + MFX_B_REF_UNKNOWN = 0, + MFX_B_REF_OFF = 1, + MFX_B_REF_PYRAMID = 2 +}; + +enum { + MFX_LOOKAHEAD_DS_UNKNOWN = 0, + MFX_LOOKAHEAD_DS_OFF = 1, + MFX_LOOKAHEAD_DS_2x = 2, + MFX_LOOKAHEAD_DS_4x = 3 +}; + +enum { + MFX_BPSEI_DEFAULT = 0x00, + MFX_BPSEI_IFRAME = 0x01 +}; + +enum { + MFX_SKIPFRAME_NO_SKIP = 0, + MFX_SKIPFRAME_INSERT_DUMMY = 1, + MFX_SKIPFRAME_INSERT_NOTHING = 2, + MFX_SKIPFRAME_BRC_ONLY = 3, +}; + +/* Intra refresh types */ +enum { + MFX_REFRESH_NO = 0, + MFX_REFRESH_VERTICAL = 1, + MFX_REFRESH_HORIZONTAL = 2, + MFX_REFRESH_SLICE = 3 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 IntRefType; + mfxU16 IntRefCycleSize; + mfxI16 IntRefQPDelta; + + mfxU32 MaxFrameSize; + mfxU32 MaxSliceSize; + + mfxU16 BitrateLimit; /* tri-state option */ + mfxU16 MBBRC; /* tri-state option */ + mfxU16 ExtBRC; /* tri-state option */ + mfxU16 LookAheadDepth; + mfxU16 Trellis; + mfxU16 RepeatPPS; /* tri-state option */ + mfxU16 BRefType; + mfxU16 AdaptiveI; /* tri-state option */ + mfxU16 AdaptiveB; /* tri-state option */ + mfxU16 LookAheadDS; + mfxU16 NumMbPerSlice; + mfxU16 SkipFrame; + mfxU8 MinQPI; /* 1..51, 0 = default */ + mfxU8 MaxQPI; /* 1..51, 0 = default */ + mfxU8 MinQPP; /* 1..51, 0 = default */ + mfxU8 MaxQPP; /* 1..51, 0 = default */ + mfxU8 MinQPB; /* 1..51, 0 = default */ + mfxU8 MaxQPB; /* 1..51, 0 = default */ + mfxU16 FixedFrameRate; /* tri-state option */ + mfxU16 DisableDeblockingIdc; + mfxU16 DisableVUI; + mfxU16 BufferingPeriodSEI; + mfxU16 EnableMAD; /* tri-state option */ + mfxU16 UseRawRef; /* tri-state option */ +} mfxExtCodingOption2; + +/* WeightedPred */ +enum { + MFX_WEIGHTED_PRED_UNKNOWN = 0, + MFX_WEIGHTED_PRED_DEFAULT = 1, + MFX_WEIGHTED_PRED_EXPLICIT = 2, + MFX_WEIGHTED_PRED_IMPLICIT = 3 +}; + +/* ScenarioInfo */ +enum { + MFX_SCENARIO_UNKNOWN = 0, + MFX_SCENARIO_DISPLAY_REMOTING = 1, + MFX_SCENARIO_VIDEO_CONFERENCE = 2, + MFX_SCENARIO_ARCHIVE = 3, + MFX_SCENARIO_LIVE_STREAMING = 4, + MFX_SCENARIO_CAMERA_CAPTURE = 5 +}; + +/* ContentInfo */ +enum { + MFX_CONTENT_UNKNOWN = 0, + MFX_CONTENT_FULL_SCREEN_VIDEO = 1, + MFX_CONTENT_NON_VIDEO_SCREEN = 2 +}; + +/* PRefType */ +enum { + MFX_P_REF_DEFAULT = 0, + MFX_P_REF_SIMPLE = 1, + MFX_P_REF_PYRAMID = 2 +}; + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + +/* QuantScaleType */ +enum { + MFX_MPEG2_QUANT_SCALE_TYPE_DEFAULT = 0, + MFX_MPEG2_QUANT_SCALE_TYPE_LINEAR = 1, /* q_scale_type = 0 */ + MFX_MPEG2_QUANT_SCALE_TYPE_NONLINEAR = 2 /* q_scale_type = 1 */ +}; + +/* IntraVLCFormat */ +enum { + MFX_MPEG2_INTRA_VLC_FORMAT_DEFAULT = 0, + MFX_MPEG2_INTRA_VLC_FORMAT_B14 = 1, /* use table B.14 */ + MFX_MPEG2_INTRA_VLC_FORMAT_B15 = 2 /* use table B.15 */ +}; + +/* ScanType */ +enum { + MFX_MPEG2_SCAN_TYPE_DEFAULT = 0, + MFX_MPEG2_SCAN_TYPE_ZIGZAG = 1, /* alternate_scan = 0 */ + MFX_MPEG2_SCAN_TYPE_ALTERNATE = 2 /* alternate_scan = 1 */ +}; + +#endif + +typedef struct { + mfxExtBuffer Header; + + mfxU16 NumSliceI; + mfxU16 NumSliceP; + mfxU16 NumSliceB; + + mfxU16 WinBRCMaxAvgKbps; + mfxU16 WinBRCSize; + + mfxU16 QVBRQuality; + mfxU16 EnableMBQP; + mfxU16 IntRefCycleDist; + mfxU16 DirectBiasAdjustment; /* tri-state option */ + mfxU16 GlobalMotionBiasAdjustment; /* tri-state option */ + mfxU16 MVCostScalingFactor; + mfxU16 MBDisableSkipMap; /* tri-state option */ + + mfxU16 WeightedPred; + mfxU16 WeightedBiPred; + + mfxU16 AspectRatioInfoPresent; /* tri-state option */ + mfxU16 OverscanInfoPresent; /* tri-state option */ + mfxU16 OverscanAppropriate; /* tri-state option */ + mfxU16 TimingInfoPresent; /* tri-state option */ + mfxU16 BitstreamRestriction; /* tri-state option */ + mfxU16 LowDelayHrd; /* tri-state option */ + mfxU16 MotionVectorsOverPicBoundaries; /* tri-state option */ +#if (MFX_VERSION >= MFX_VERSION_NEXT) + mfxU16 Log2MaxMvLengthHorizontal; /* 0..16 */ + mfxU16 Log2MaxMvLengthVertical; /* 0..16 */ +#else + mfxU16 reserved1[2]; +#endif + + mfxU16 ScenarioInfo; + mfxU16 ContentInfo; + + mfxU16 PRefType; + mfxU16 FadeDetection; /* tri-state option */ +#if (MFX_VERSION >= MFX_VERSION_NEXT) + mfxI16 DeblockingAlphaTcOffset; /* -12..12 (slice_alpha_c0_offset_div2 << 1) */ + mfxI16 DeblockingBetaOffset; /* -12..12 (slice_beta_offset_div2 << 1) */ +#else + mfxU16 reserved2[2]; +#endif + mfxU16 GPB; /* tri-state option */ + + mfxU32 MaxFrameSizeI; + mfxU32 MaxFrameSizeP; + mfxU32 reserved3[3]; + + mfxU16 EnableQPOffset; /* tri-state option */ + mfxI16 QPOffset[8]; /* FrameQP = QPX + QPOffset[pyramid_layer]; QPX = QPB for B-pyramid, QPP for P-pyramid */ + + mfxU16 NumRefActiveP[8]; + mfxU16 NumRefActiveBL0[8]; + mfxU16 NumRefActiveBL1[8]; + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + mfxU16 ConstrainedIntraPredFlag; /* tri-state option */ +#else + mfxU16 reserved6; +#endif +#if (MFX_VERSION >= 1026) + mfxU16 TransformSkip; /* tri-state option; HEVC transform_skip_enabled_flag */ +#else + mfxU16 reserved7; +#endif +#if (MFX_VERSION >= 1027) + mfxU16 TargetChromaFormatPlus1; /* Minus 1 specifies target encoding chroma format (see ColorFormat enum). May differ from input one. */ + mfxU16 TargetBitDepthLuma; /* Target encoding bit depth for luma samples. May differ from input one. */ + mfxU16 TargetBitDepthChroma; /* Target encoding bit depth for chroma samples. May differ from input one. */ +#else + mfxU16 reserved4[3]; +#endif + mfxU16 BRCPanicMode; /* tri-state option */ + + mfxU16 LowDelayBRC; /* tri-state option */ + mfxU16 EnableMBForceIntra; /* tri-state option */ + mfxU16 AdaptiveMaxFrameSize; /* tri-state option */ + + mfxU16 RepartitionCheckEnable; /* tri-state option */ +#if (MFX_VERSION >= MFX_VERSION_NEXT) + mfxU16 QuantScaleType; /* For MPEG2 specifies mapping between quantiser_scale_code and quantiser_scale (see QuantScaleType enum) */ + mfxU16 IntraVLCFormat; /* For MPEG2 specifies which table shall be used for coding of DCT coefficients of intra macroblocks (see IntraVLCFormat enum) */ + mfxU16 ScanType; /* For MPEG2 specifies transform coefficients scan pattern (see ScanType enum) */ +#else + mfxU16 reserved5[3]; +#endif +#if (MFX_VERSION >= 1025) + mfxU16 EncodedUnitsInfo; /* tri-state option */ + mfxU16 EnableNalUnitType; /* tri-state option */ +#else + mfxU16 reserved8[2]; +#endif +#if (MFX_VERSION >= 1026) + mfxU16 ExtBrcAdaptiveLTR; /* tri-state option for ExtBRC */ +#else + mfxU16 reserved9; +#endif + mfxU16 reserved[163]; +} mfxExtCodingOption3; + +/* IntraPredBlockSize/InterPredBlockSize */ +enum { + MFX_BLOCKSIZE_UNKNOWN = 0, + MFX_BLOCKSIZE_MIN_16X16 = 1, /* 16x16 */ + MFX_BLOCKSIZE_MIN_8X8 = 2, /* 16x16, 8x8 */ + MFX_BLOCKSIZE_MIN_4X4 = 3 /* 16x16, 8x8, 4x4 */ +}; + +/* MVPrecision */ +enum { + MFX_MVPRECISION_UNKNOWN = 0, + MFX_MVPRECISION_INTEGER = (1 << 0), + MFX_MVPRECISION_HALFPEL = (1 << 1), + MFX_MVPRECISION_QUARTERPEL = (1 << 2) +}; + +enum { + MFX_CODINGOPTION_UNKNOWN =0, + MFX_CODINGOPTION_ON =0x10, + MFX_CODINGOPTION_OFF =0x20, + MFX_CODINGOPTION_ADAPTIVE =0x30 +}; + +/* Data Flag for mfxBitstream*/ +enum { + MFX_BITSTREAM_COMPLETE_FRAME = 0x0001, /* the bitstream contains a complete frame or field pair of data */ + MFX_BITSTREAM_EOS = 0x0002 +}; +/* Extended Buffer Ids */ +enum { + MFX_EXTBUFF_CODING_OPTION = MFX_MAKEFOURCC('C','D','O','P'), + MFX_EXTBUFF_CODING_OPTION_SPSPPS = MFX_MAKEFOURCC('C','O','S','P'), + MFX_EXTBUFF_VPP_DONOTUSE = MFX_MAKEFOURCC('N','U','S','E'), + MFX_EXTBUFF_VPP_AUXDATA = MFX_MAKEFOURCC('A','U','X','D'), + MFX_EXTBUFF_VPP_DENOISE = MFX_MAKEFOURCC('D','N','I','S'), + MFX_EXTBUFF_VPP_SCENE_ANALYSIS = MFX_MAKEFOURCC('S','C','L','Y'), + MFX_EXTBUFF_VPP_SCENE_CHANGE = MFX_EXTBUFF_VPP_SCENE_ANALYSIS, + MFX_EXTBUFF_VPP_PROCAMP = MFX_MAKEFOURCC('P','A','M','P'), + MFX_EXTBUFF_VPP_DETAIL = MFX_MAKEFOURCC('D','E','T',' '), + MFX_EXTBUFF_VIDEO_SIGNAL_INFO = MFX_MAKEFOURCC('V','S','I','N'), + MFX_EXTBUFF_VPP_DOUSE = MFX_MAKEFOURCC('D','U','S','E'), + MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION = MFX_MAKEFOURCC('O','P','Q','S'), + MFX_EXTBUFF_AVC_REFLIST_CTRL = MFX_MAKEFOURCC('R','L','S','T'), + MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION = MFX_MAKEFOURCC('F','R','C',' '), + MFX_EXTBUFF_PICTURE_TIMING_SEI = MFX_MAKEFOURCC('P','T','S','E'), + MFX_EXTBUFF_AVC_TEMPORAL_LAYERS = MFX_MAKEFOURCC('A','T','M','L'), + MFX_EXTBUFF_CODING_OPTION2 = MFX_MAKEFOURCC('C','D','O','2'), + MFX_EXTBUFF_VPP_IMAGE_STABILIZATION = MFX_MAKEFOURCC('I','S','T','B'), + MFX_EXTBUFF_VPP_PICSTRUCT_DETECTION = MFX_MAKEFOURCC('I','D','E','T'), + MFX_EXTBUFF_ENCODER_CAPABILITY = MFX_MAKEFOURCC('E','N','C','P'), + MFX_EXTBUFF_ENCODER_RESET_OPTION = MFX_MAKEFOURCC('E','N','R','O'), + MFX_EXTBUFF_ENCODED_FRAME_INFO = MFX_MAKEFOURCC('E','N','F','I'), + MFX_EXTBUFF_VPP_COMPOSITE = MFX_MAKEFOURCC('V','C','M','P'), + MFX_EXTBUFF_VPP_VIDEO_SIGNAL_INFO = MFX_MAKEFOURCC('V','V','S','I'), + MFX_EXTBUFF_ENCODER_ROI = MFX_MAKEFOURCC('E','R','O','I'), + MFX_EXTBUFF_VPP_DEINTERLACING = MFX_MAKEFOURCC('V','P','D','I'), + MFX_EXTBUFF_AVC_REFLISTS = MFX_MAKEFOURCC('R','L','T','S'), + MFX_EXTBUFF_DEC_VIDEO_PROCESSING = MFX_MAKEFOURCC('D','E','C','V'), + MFX_EXTBUFF_VPP_FIELD_PROCESSING = MFX_MAKEFOURCC('F','P','R','O'), + MFX_EXTBUFF_CODING_OPTION3 = MFX_MAKEFOURCC('C','D','O','3'), + MFX_EXTBUFF_CHROMA_LOC_INFO = MFX_MAKEFOURCC('C','L','I','N'), + MFX_EXTBUFF_MBQP = MFX_MAKEFOURCC('M','B','Q','P'), + MFX_EXTBUFF_MB_FORCE_INTRA = MFX_MAKEFOURCC('M','B','F','I'), + MFX_EXTBUFF_HEVC_TILES = MFX_MAKEFOURCC('2','6','5','T'), + MFX_EXTBUFF_MB_DISABLE_SKIP_MAP = MFX_MAKEFOURCC('M','D','S','M'), + MFX_EXTBUFF_HEVC_PARAM = MFX_MAKEFOURCC('2','6','5','P'), + MFX_EXTBUFF_DECODED_FRAME_INFO = MFX_MAKEFOURCC('D','E','F','I'), + MFX_EXTBUFF_TIME_CODE = MFX_MAKEFOURCC('T','M','C','D'), + MFX_EXTBUFF_HEVC_REGION = MFX_MAKEFOURCC('2','6','5','R'), + MFX_EXTBUFF_PRED_WEIGHT_TABLE = MFX_MAKEFOURCC('E','P','W','T'), + MFX_EXTBUFF_DIRTY_RECTANGLES = MFX_MAKEFOURCC('D','R','O','I'), + MFX_EXTBUFF_MOVING_RECTANGLES = MFX_MAKEFOURCC('M','R','O','I'), + MFX_EXTBUFF_CODING_OPTION_VPS = MFX_MAKEFOURCC('C','O','V','P'), + MFX_EXTBUFF_VPP_ROTATION = MFX_MAKEFOURCC('R','O','T',' '), + MFX_EXTBUFF_ENCODED_SLICES_INFO = MFX_MAKEFOURCC('E','N','S','I'), + MFX_EXTBUFF_VPP_SCALING = MFX_MAKEFOURCC('V','S','C','L'), + MFX_EXTBUFF_HEVC_REFLIST_CTRL = MFX_EXTBUFF_AVC_REFLIST_CTRL, + MFX_EXTBUFF_HEVC_REFLISTS = MFX_EXTBUFF_AVC_REFLISTS, + MFX_EXTBUFF_HEVC_TEMPORAL_LAYERS = MFX_EXTBUFF_AVC_TEMPORAL_LAYERS, + MFX_EXTBUFF_VPP_MIRRORING = MFX_MAKEFOURCC('M','I','R','R'), + MFX_EXTBUFF_MV_OVER_PIC_BOUNDARIES = MFX_MAKEFOURCC('M','V','P','B'), + MFX_EXTBUFF_VPP_COLORFILL = MFX_MAKEFOURCC('V','C','L','F'), +#if (MFX_VERSION >= 1025) + MFX_EXTBUFF_DECODE_ERROR_REPORT = MFX_MAKEFOURCC('D', 'E', 'R', 'R'), + MFX_EXTBUFF_VPP_COLOR_CONVERSION = MFX_MAKEFOURCC('V', 'C', 'S', 'C'), + MFX_EXTBUFF_CONTENT_LIGHT_LEVEL_INFO = MFX_MAKEFOURCC('L', 'L', 'I', 'S'), + MFX_EXTBUFF_MASTERING_DISPLAY_COLOUR_VOLUME = MFX_MAKEFOURCC('D', 'C', 'V', 'S'), + MFX_EXTBUFF_MULTI_FRAME_PARAM = MFX_MAKEFOURCC('M', 'F', 'R', 'P'), + MFX_EXTBUFF_MULTI_FRAME_CONTROL = MFX_MAKEFOURCC('M', 'F', 'R', 'C'), + MFX_EXTBUFF_ENCODED_UNITS_INFO = MFX_MAKEFOURCC('E', 'N', 'U', 'I'), +#endif +#if (MFX_VERSION >= 1026) + MFX_EXTBUFF_VPP_MCTF = MFX_MAKEFOURCC('M', 'C', 'T', 'F'), + MFX_EXTBUFF_VP9_SEGMENTATION = MFX_MAKEFOURCC('9', 'S', 'E', 'G'), + MFX_EXTBUFF_VP9_TEMPORAL_LAYERS = MFX_MAKEFOURCC('9', 'T', 'M', 'L'), + MFX_EXTBUFF_VP9_PARAM = MFX_MAKEFOURCC('9', 'P', 'A', 'R'), +#endif +#if (MFX_VERSION >= 1027) + MFX_EXTBUFF_AVC_ROUNDING_OFFSET = MFX_MAKEFOURCC('R','N','D','O'), +#endif +#if (MFX_VERSION >= MFX_VERSION_NEXT) + MFX_EXTBUFF_DPB = MFX_MAKEFOURCC('E','D','P','B'), + MFX_EXTBUFF_TEMPORAL_LAYERS = MFX_MAKEFOURCC('T','M','P','L'), + MFX_EXTBUFF_AVC_SCALING_MATRIX = MFX_MAKEFOURCC('A','V','S','M'), + MFX_EXTBUFF_MPEG2_QUANT_MATRIX = MFX_MAKEFOURCC('M','2','Q','M'), + MFX_EXTBUFF_TASK_DEPENDENCY = MFX_MAKEFOURCC('S','Y','N','C'), +#endif +}; + +/* VPP Conf: Do not use certain algorithms */ +typedef struct { + mfxExtBuffer Header; + mfxU32 NumAlg; + mfxU32* AlgList; +} mfxExtVPPDoNotUse; + +typedef struct { + mfxExtBuffer Header; + mfxU16 DenoiseFactor; +} mfxExtVPPDenoise; + +typedef struct { + mfxExtBuffer Header; + mfxU16 DetailFactor; +} mfxExtVPPDetail; + +typedef struct { + mfxExtBuffer Header; + mfxF64 Brightness; + mfxF64 Contrast; + mfxF64 Hue; + mfxF64 Saturation; +} mfxExtVPPProcAmp; + +/* statistics collected for decode, encode and vpp */ +typedef struct { + mfxU32 reserved[16]; + mfxU32 NumFrame; + mfxU64 NumBit; + mfxU32 NumCachedFrame; +} mfxEncodeStat; + +typedef struct { + mfxU32 reserved[16]; + mfxU32 NumFrame; + mfxU32 NumSkippedFrame; + mfxU32 NumError; + mfxU32 NumCachedFrame; +} mfxDecodeStat; + +typedef struct { + mfxU32 reserved[16]; + mfxU32 NumFrame; + mfxU32 NumCachedFrame; +} mfxVPPStat; + +typedef struct { + mfxExtBuffer Header; + + union{ + struct{ + mfxU32 SpatialComplexity; + mfxU32 TemporalComplexity; + }; + struct{ + mfxU16 PicStruct; + mfxU16 reserved[3]; + }; + }; + mfxU16 SceneChangeRate; + mfxU16 RepeatedFrame; +} mfxExtVppAuxData; + +/* CtrlFlags */ +enum { + MFX_PAYLOAD_CTRL_SUFFIX = 0x00000001 /* HEVC suffix SEI */ +}; + +typedef struct { + mfxU32 CtrlFlags; + mfxU32 reserved[3]; + mfxU8 *Data; /* buffer pointer */ + mfxU32 NumBit; /* number of bits */ + mfxU16 Type; /* SEI message type in H.264 or user data start_code in MPEG-2 */ + mfxU16 BufSize; /* payload buffer size in bytes */ +} mfxPayload; + +typedef struct { + mfxExtBuffer Header; +#if (MFX_VERSION >= 1025) + mfxU32 reserved[4]; + mfxU16 reserved1; + mfxU16 MfxNalUnitType; +#else + mfxU32 reserved[5]; +#endif + mfxU16 SkipFrame; + + mfxU16 QP; /* per frame QP */ + + mfxU16 FrameType; + mfxU16 NumExtParam; + mfxU16 NumPayload; /* MPEG-2 user data or H.264 SEI message(s) */ + mfxU16 reserved2; + + mfxExtBuffer **ExtParam; + mfxPayload **Payload; /* for field pair, first field uses even payloads and second field uses odd payloads */ +} mfxEncodeCtrl; + +/* Buffer Memory Types */ +enum { + /* Buffer types */ + MFX_MEMTYPE_PERSISTENT_MEMORY =0x0002 +}; + +/* Frame Memory Types */ +#define MFX_MEMTYPE_BASE(x) (0x90ff & (x)) + +enum { + MFX_MEMTYPE_DXVA2_DECODER_TARGET =0x0010, + MFX_MEMTYPE_DXVA2_PROCESSOR_TARGET =0x0020, + MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET = MFX_MEMTYPE_DXVA2_DECODER_TARGET, + MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET = MFX_MEMTYPE_DXVA2_PROCESSOR_TARGET, + MFX_MEMTYPE_SYSTEM_MEMORY =0x0040, + MFX_MEMTYPE_RESERVED1 =0x0080, + + MFX_MEMTYPE_FROM_ENCODE = 0x0100, + MFX_MEMTYPE_FROM_DECODE = 0x0200, + MFX_MEMTYPE_FROM_VPPIN = 0x0400, + MFX_MEMTYPE_FROM_VPPOUT = 0x0800, + MFX_MEMTYPE_FROM_ENC = 0x2000, + MFX_MEMTYPE_FROM_PAK = 0x4000, //reserved + + MFX_MEMTYPE_INTERNAL_FRAME = 0x0001, + MFX_MEMTYPE_EXTERNAL_FRAME = 0x0002, + MFX_MEMTYPE_OPAQUE_FRAME = 0x0004, + MFX_MEMTYPE_EXPORT_FRAME = 0x0008, + MFX_MEMTYPE_SHARED_RESOURCE = MFX_MEMTYPE_EXPORT_FRAME, +#if (MFX_VERSION >= 1025) + MFX_MEMTYPE_VIDEO_MEMORY_ENCODER_TARGET = 0x1000 +#else + MFX_MEMTYPE_RESERVED2 = 0x1000 +#endif +}; + +typedef struct { + union { + mfxU32 AllocId; + mfxU32 reserved[1]; + }; + mfxU32 reserved3[3]; + mfxFrameInfo Info; + mfxU16 Type; /* decoder or processor render targets */ + mfxU16 NumFrameMin; + mfxU16 NumFrameSuggested; + mfxU16 reserved2; +} mfxFrameAllocRequest; + +typedef struct { + mfxU32 AllocId; + mfxU32 reserved[3]; + mfxMemId *mids; /* the array allocated by application */ + mfxU16 NumFrameActual; +#if (MFX_VERSION >= MFX_VERSION_NEXT) + mfxU16 MemType; +#else + mfxU16 reserved2; +#endif +} mfxFrameAllocResponse; + +/* FrameType */ +enum { + MFX_FRAMETYPE_UNKNOWN =0x0000, + + MFX_FRAMETYPE_I =0x0001, + MFX_FRAMETYPE_P =0x0002, + MFX_FRAMETYPE_B =0x0004, + MFX_FRAMETYPE_S =0x0008, + + MFX_FRAMETYPE_REF =0x0040, + MFX_FRAMETYPE_IDR =0x0080, + + MFX_FRAMETYPE_xI =0x0100, + MFX_FRAMETYPE_xP =0x0200, + MFX_FRAMETYPE_xB =0x0400, + MFX_FRAMETYPE_xS =0x0800, + + MFX_FRAMETYPE_xREF =0x4000, + MFX_FRAMETYPE_xIDR =0x8000 +}; + +#if (MFX_VERSION >= 1025) +enum { + MFX_HEVC_NALU_TYPE_UNKNOWN = 0, + MFX_HEVC_NALU_TYPE_TRAIL_N = ( 0+1), + MFX_HEVC_NALU_TYPE_TRAIL_R = ( 1+1), + MFX_HEVC_NALU_TYPE_RADL_N = ( 6+1), + MFX_HEVC_NALU_TYPE_RADL_R = ( 7+1), + MFX_HEVC_NALU_TYPE_RASL_N = ( 8+1), + MFX_HEVC_NALU_TYPE_RASL_R = ( 9+1), + MFX_HEVC_NALU_TYPE_IDR_W_RADL = (19+1), + MFX_HEVC_NALU_TYPE_IDR_N_LP = (20+1), + MFX_HEVC_NALU_TYPE_CRA_NUT = (21+1) +}; +#endif + +typedef enum { + MFX_HANDLE_DIRECT3D_DEVICE_MANAGER9 =1, /* IDirect3DDeviceManager9 */ + MFX_HANDLE_D3D9_DEVICE_MANAGER = MFX_HANDLE_DIRECT3D_DEVICE_MANAGER9, + MFX_HANDLE_RESERVED1 = 2, + MFX_HANDLE_D3D11_DEVICE = 3, + MFX_HANDLE_VA_DISPLAY = 4, + MFX_HANDLE_RESERVED3 = 5, +#if (MFX_VERSION >= MFX_VERSION_NEXT) + MFX_HANDLE_VA_CONFIG_ID = 6, + MFX_HANDLE_VA_CONTEXT_ID = 7, + MFX_HANDLE_CM_DEVICE = 8 +#endif +} mfxHandleType; + +typedef enum { + MFX_SKIPMODE_NOSKIP=0, + MFX_SKIPMODE_MORE=1, + MFX_SKIPMODE_LESS=2 +} mfxSkipMode; + +typedef struct { + mfxExtBuffer Header; + mfxU8 *SPSBuffer; + mfxU8 *PPSBuffer; + mfxU16 SPSBufSize; + mfxU16 PPSBufSize; + mfxU16 SPSId; + mfxU16 PPSId; +} mfxExtCodingOptionSPSPPS; + +typedef struct { + mfxExtBuffer Header; + + union { + mfxU8 *VPSBuffer; + mfxU64 reserved1; + }; + mfxU16 VPSBufSize; + mfxU16 VPSId; + + mfxU16 reserved[6]; +} mfxExtCodingOptionVPS; + +typedef struct { + mfxExtBuffer Header; + mfxU16 VideoFormat; + mfxU16 VideoFullRange; + mfxU16 ColourDescriptionPresent; + mfxU16 ColourPrimaries; + mfxU16 TransferCharacteristics; + mfxU16 MatrixCoefficients; +} mfxExtVideoSignalInfo; + +typedef struct { + mfxExtBuffer Header; + mfxU32 NumAlg; + mfxU32 *AlgList; +} mfxExtVPPDoUse; + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[2]; + struct { + mfxFrameSurface1 **Surfaces; + mfxU32 reserved2[5]; + mfxU16 Type; + mfxU16 NumSurface; + } In, Out; +} mfxExtOpaqueSurfaceAlloc; + +typedef struct { + mfxExtBuffer Header; + mfxU16 NumRefIdxL0Active; + mfxU16 NumRefIdxL1Active; + + struct { + mfxU32 FrameOrder; + mfxU16 PicStruct; + mfxU16 ViewId; + mfxU16 LongTermIdx; + mfxU16 reserved[3]; + } PreferredRefList[32], RejectedRefList[16], LongTermRefList[16]; + + mfxU16 ApplyLongTermIdx; + mfxU16 reserved[15]; +} mfxExtAVCRefListCtrl; + +enum { + MFX_FRCALGM_PRESERVE_TIMESTAMP = 0x0001, + MFX_FRCALGM_DISTRIBUTED_TIMESTAMP = 0x0002, + MFX_FRCALGM_FRAME_INTERPOLATION = 0x0004 +}; + +typedef struct { + mfxExtBuffer Header; + mfxU16 Algorithm; + mfxU16 reserved; + mfxU32 reserved2[15]; +} mfxExtVPPFrameRateConversion; + +enum { + MFX_IMAGESTAB_MODE_UPSCALE = 0x0001, + MFX_IMAGESTAB_MODE_BOXING = 0x0002 +}; + +typedef struct { + mfxExtBuffer Header; + mfxU16 Mode; + mfxU16 reserved[11]; +} mfxExtVPPImageStab; + +#if (MFX_VERSION >= 1025) + +enum { + MFX_PAYLOAD_OFF = 0, + MFX_PAYLOAD_IDR = 1 +}; + +typedef struct { + mfxExtBuffer Header; + mfxU16 reserved[15]; + + mfxU16 InsertPayloadToggle; + mfxU16 DisplayPrimariesX[3]; + mfxU16 DisplayPrimariesY[3]; + mfxU16 WhitePointX; + mfxU16 WhitePointY; + mfxU32 MaxDisplayMasteringLuminance; + mfxU32 MinDisplayMasteringLuminance; +} mfxExtMasteringDisplayColourVolume; + + +typedef struct { + mfxExtBuffer Header; + mfxU16 reserved[9]; + + mfxU16 InsertPayloadToggle; + mfxU16 MaxContentLightLevel; + mfxU16 MaxPicAverageLightLevel; +} mfxExtContentLightLevelInfo; + +#endif + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved[14]; + + struct { + mfxU16 ClockTimestampFlag; + mfxU16 CtType; + mfxU16 NuitFieldBasedFlag; + mfxU16 CountingType; + mfxU16 FullTimestampFlag; + mfxU16 DiscontinuityFlag; + mfxU16 CntDroppedFlag; + mfxU16 NFrames; + mfxU16 SecondsFlag; + mfxU16 MinutesFlag; + mfxU16 HoursFlag; + mfxU16 SecondsValue; + mfxU16 MinutesValue; + mfxU16 HoursValue; + mfxU32 TimeOffset; + } TimeStamp[3]; +} mfxExtPictureTimingSEI; + +typedef struct { + mfxExtBuffer Header; + mfxU32 reserved1[4]; + mfxU16 reserved2; + mfxU16 BaseLayerPID; + + struct { + mfxU16 Scale; + mfxU16 reserved[3]; + }Layer[8]; +} mfxExtAvcTemporalLayers; + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + +enum { + MFX_MEMORY_TILING_LINEAR = 0x0001, + MFX_MEMORY_TILING_Y = 0x0002, + MFX_MEMORY_TILING_X = 0x0004 +}; + +#endif + +typedef struct { + mfxExtBuffer Header; + + mfxU32 MBPerSec; +#if (MFX_VERSION >= MFX_VERSION_NEXT) + mfxU16 InputMemoryTiling; + mfxU16 reserved[57]; +#else + mfxU16 reserved[58]; +#endif +} mfxExtEncoderCapability; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 StartNewSequence; + mfxU16 reserved[11]; +} mfxExtEncoderResetOption; + +/*LongTermIdx*/ +enum { + MFX_LONGTERM_IDX_NO_IDX = 0xFFFF +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU32 FrameOrder; + mfxU16 PicStruct; + mfxU16 LongTermIdx; + mfxU32 MAD; + mfxU16 BRCPanicMode; + mfxU16 QP; + mfxU32 SecondFieldOffset; + mfxU16 reserved[2]; + + struct { + mfxU32 FrameOrder; + mfxU16 PicStruct; + mfxU16 LongTermIdx; + mfxU16 reserved[4]; + } UsedRefListL0[32], UsedRefListL1[32]; +} mfxExtAVCEncodedFrameInfo; + +typedef struct mfxVPPCompInputStream { + mfxU32 DstX; + mfxU32 DstY; + mfxU32 DstW; + mfxU32 DstH; + + mfxU16 LumaKeyEnable; + mfxU16 LumaKeyMin; + mfxU16 LumaKeyMax; + + mfxU16 GlobalAlphaEnable; + mfxU16 GlobalAlpha; + mfxU16 PixelAlphaEnable; + + mfxU16 TileId; + + mfxU16 reserved2[17]; +} mfxVPPCompInputStream; + +typedef struct { + mfxExtBuffer Header; + + /* background color*/ + union { + mfxU16 Y; + mfxU16 R; + }; + union { + mfxU16 U; + mfxU16 G; + }; + union { + mfxU16 V; + mfxU16 B; + }; + mfxU16 NumTiles; + mfxU16 reserved1[23]; + + mfxU16 NumInputStream; + mfxVPPCompInputStream *InputStream; +} mfxExtVPPComposite; + +/* TransferMatrix */ +enum { + MFX_TRANSFERMATRIX_UNKNOWN = 0, + MFX_TRANSFERMATRIX_BT709 = 1, + MFX_TRANSFERMATRIX_BT601 = 2 +}; + +/* NominalRange */ +enum { + MFX_NOMINALRANGE_UNKNOWN = 0, + MFX_NOMINALRANGE_0_255 = 1, + MFX_NOMINALRANGE_16_235 = 2 +}; + +typedef struct { + mfxExtBuffer Header; + mfxU16 reserved1[4]; + + union { + struct { // Init + struct { + mfxU16 TransferMatrix; + mfxU16 NominalRange; + mfxU16 reserved2[6]; + } In, Out; + }; + struct { // Runtime + mfxU16 TransferMatrix; + mfxU16 NominalRange; + mfxU16 reserved3[14]; + }; + }; +} mfxExtVPPVideoSignalInfo; + +/* ROI encoding mode */ +enum { + MFX_ROI_MODE_PRIORITY = 0, + MFX_ROI_MODE_QP_DELTA = 1 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 NumROI; + mfxU16 ROIMode; + mfxU16 reserved1[10]; + + struct { + mfxU32 Left; + mfxU32 Top; + mfxU32 Right; + mfxU32 Bottom; + union { + mfxI16 Priority; + mfxI16 DeltaQP; + }; + mfxU16 reserved2[7]; + } ROI[256]; +} mfxExtEncoderROI; + +/*Deinterlacing Mode*/ +enum { + MFX_DEINTERLACING_BOB = 1, + MFX_DEINTERLACING_ADVANCED = 2, + MFX_DEINTERLACING_AUTO_DOUBLE = 3, + MFX_DEINTERLACING_AUTO_SINGLE = 4, + MFX_DEINTERLACING_FULL_FR_OUT = 5, + MFX_DEINTERLACING_HALF_FR_OUT = 6, + MFX_DEINTERLACING_24FPS_OUT = 7, + MFX_DEINTERLACING_FIXED_TELECINE_PATTERN = 8, + MFX_DEINTERLACING_30FPS_OUT = 9, + MFX_DEINTERLACING_DETECT_INTERLACE = 10, + MFX_DEINTERLACING_ADVANCED_NOREF = 11, + MFX_DEINTERLACING_ADVANCED_SCD = 12, + MFX_DEINTERLACING_FIELD_WEAVING = 13 +}; + +/*TelecinePattern*/ +enum { + MFX_TELECINE_PATTERN_32 = 0, + MFX_TELECINE_PATTERN_2332 = 1, + MFX_TELECINE_PATTERN_FRAME_REPEAT = 2, + MFX_TELECINE_PATTERN_41 = 3, + MFX_TELECINE_POSITION_PROVIDED = 4 +}; + +typedef struct { + mfxExtBuffer Header; + mfxU16 Mode; + mfxU16 TelecinePattern; + mfxU16 TelecineLocation; + mfxU16 reserved[9]; +} mfxExtVPPDeinterlacing; + +typedef struct { + mfxExtBuffer Header; + mfxU16 NumRefIdxL0Active; + mfxU16 NumRefIdxL1Active; + mfxU16 reserved[2]; + + struct mfxRefPic{ + mfxU32 FrameOrder; + mfxU16 PicStruct; + mfxU16 reserved[5]; + } RefPicList0[32], RefPicList1[32]; + +}mfxExtAVCRefLists; + +enum { + MFX_VPP_COPY_FRAME =0x01, + MFX_VPP_COPY_FIELD =0x02, + MFX_VPP_SWAP_FIELDS =0x03 +}; + +/*PicType*/ +enum { + MFX_PICTYPE_UNKNOWN =0x00, + MFX_PICTYPE_FRAME =0x01, + MFX_PICTYPE_TOPFIELD =0x02, + MFX_PICTYPE_BOTTOMFIELD =0x04 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 Mode; + mfxU16 InField; + mfxU16 OutField; + mfxU16 reserved[25]; +} mfxExtVPPFieldProcessing; + +typedef struct { + mfxExtBuffer Header; + + struct mfxIn{ + mfxU16 CropX; + mfxU16 CropY; + mfxU16 CropW; + mfxU16 CropH; + mfxU16 reserved[12]; + }In; + + struct mfxOut{ + mfxU32 FourCC; + mfxU16 ChromaFormat; + mfxU16 reserved1; + + mfxU16 Width; + mfxU16 Height; + + mfxU16 CropX; + mfxU16 CropY; + mfxU16 CropW; + mfxU16 CropH; + mfxU16 reserved[22]; + }Out; + + mfxU16 reserved[13]; +} mfxExtDecVideoProcessing; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 ChromaLocInfoPresentFlag; + mfxU16 ChromaSampleLocTypeTopField; + mfxU16 ChromaSampleLocTypeBottomField; + mfxU16 reserved[9]; +} mfxExtChromaLocInfo; + +/* MBQPMode */ +enum { + MFX_MBQP_MODE_QP_VALUE = 0, // supported in CQP mode only + MFX_MBQP_MODE_QP_DELTA = 1 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU32 reserved[10]; + mfxU16 Mode; // see MBQPMode enum + mfxU16 BlockSize; // QP block size, valid for HEVC only during Init and Runtime + mfxU32 NumQPAlloc; // Size of allocated by application QP or DeltaQP array + union { + mfxU8 *QP; // Block QP value. Valid when Mode = MFX_MBQP_MODE_QP_VALUE + mfxI8 *DeltaQP; // For block i: QP[i] = BrcQP[i] + DeltaQP[i]. Valid when Mode = MFX_MBQP_MODE_QP_DELTA + mfxU64 reserved2; + }; +} mfxExtMBQP; + +typedef struct { + mfxExtBuffer Header; + + mfxU32 reserved[11]; + mfxU32 MapSize; + union { + mfxU8 *Map; + mfxU64 reserved2; + }; +} mfxExtMBForceIntra; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 NumTileRows; + mfxU16 NumTileColumns; + mfxU16 reserved[74]; +}mfxExtHEVCTiles; + +typedef struct { + mfxExtBuffer Header; + + mfxU32 reserved[11]; + mfxU32 MapSize; + union { + mfxU8 *Map; + mfxU64 reserved2; + }; +} mfxExtMBDisableSkipMap; + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + +typedef struct { + mfxExtBuffer Header; + + mfxU16 DPBSize; + mfxU16 reserved[11]; + + struct { + mfxU32 FrameOrder; + mfxU16 PicType; + mfxU16 LongTermIdx; + mfxU16 reserved[4]; + } DPB[32]; +} mfxExtDPB; + +#endif + +/*GeneralConstraintFlags*/ +enum { + /* REXT Profile constraint flags*/ + MFX_HEVC_CONSTR_REXT_MAX_12BIT = (1 << 0), + MFX_HEVC_CONSTR_REXT_MAX_10BIT = (1 << 1), + MFX_HEVC_CONSTR_REXT_MAX_8BIT = (1 << 2), + MFX_HEVC_CONSTR_REXT_MAX_422CHROMA = (1 << 3), + MFX_HEVC_CONSTR_REXT_MAX_420CHROMA = (1 << 4), + MFX_HEVC_CONSTR_REXT_MAX_MONOCHROME = (1 << 5), + MFX_HEVC_CONSTR_REXT_INTRA = (1 << 6), + MFX_HEVC_CONSTR_REXT_ONE_PICTURE_ONLY = (1 << 7), + MFX_HEVC_CONSTR_REXT_LOWER_BIT_RATE = (1 << 8) +}; + +#if (MFX_VERSION >= 1026) + +/* SampleAdaptiveOffset */ +enum { + MFX_SAO_UNKNOWN = 0x00, + MFX_SAO_DISABLE = 0x01, + MFX_SAO_ENABLE_LUMA = 0x02, + MFX_SAO_ENABLE_CHROMA = 0x04 +}; + +#endif + +#pragma pack(push, 4) +typedef struct { + mfxExtBuffer Header; + + mfxU16 PicWidthInLumaSamples; + mfxU16 PicHeightInLumaSamples; + mfxU64 GeneralConstraintFlags; +#if (MFX_VERSION >= 1026) + mfxU16 SampleAdaptiveOffset; /* see enum SampleAdaptiveOffset, valid during Init and Runtime */ + mfxU16 LCUSize; + mfxU16 reserved[116]; +#else + mfxU16 reserved[118]; +#endif +} mfxExtHEVCParam; +#pragma pack(pop) + +#if (MFX_VERSION >= 1025) +/*ErrorTypes in mfxExtDecodeErrorReport*/ +enum { + MFX_ERROR_PPS = (1 << 0), + MFX_ERROR_SPS = (1 << 1), + MFX_ERROR_SLICEHEADER = (1 << 2), + MFX_ERROR_SLICEDATA = (1 << 3), + MFX_ERROR_FRAME_GAP = (1 << 4), +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU32 ErrorTypes; + mfxU16 reserved[10]; +} mfxExtDecodeErrorReport; + +#endif + +typedef struct { + mfxExtBuffer Header; + + mfxU16 FrameType; + mfxU16 reserved[59]; +} mfxExtDecodedFrameInfo; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 DropFrameFlag; + mfxU16 TimeCodeHours; + mfxU16 TimeCodeMinutes; + mfxU16 TimeCodeSeconds; + mfxU16 TimeCodePictures; + mfxU16 reserved[7]; +} mfxExtTimeCode; + +/*RegionType*/ +enum { + MFX_HEVC_REGION_SLICE = 0 +}; + +/*RegionEncoding*/ +enum { + MFX_HEVC_REGION_ENCODING_ON = 0, + MFX_HEVC_REGION_ENCODING_OFF = 1 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU32 RegionId; + mfxU16 RegionType; + mfxU16 RegionEncoding; + mfxU16 reserved[24]; +} mfxExtHEVCRegion; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 LumaLog2WeightDenom; // 0..7 + mfxU16 ChromaLog2WeightDenom; // 0..7 + mfxU16 LumaWeightFlag[2][32]; // [list] 0,1 + mfxU16 ChromaWeightFlag[2][32]; // [list] 0,1 + mfxI16 Weights[2][32][3][2]; // [list][list entry][Y, Cb, Cr][weight, offset] + mfxU16 reserved[58]; +} mfxExtPredWeightTable; + +#if (MFX_VERSION >= 1027) +typedef struct { + mfxExtBuffer Header; + + mfxU16 EnableRoundingIntra; // tri-state option + mfxU16 RoundingOffsetIntra; // valid value [0,7] + mfxU16 EnableRoundingInter; // tri-state option + mfxU16 RoundingOffsetInter; // valid value [0,7] + + mfxU16 reserved[24]; +} mfxExtAVCRoundingOffset; +#endif + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + +typedef struct { + mfxExtBuffer Header; + + mfxU16 reserved[12]; + + struct { + mfxU16 Scale; + mfxU16 QPI; + mfxU16 QPP; + mfxU16 QPB; + mfxU32 TargetKbps; + mfxU32 MaxKbps; + mfxU32 BufferSizeInKB; + mfxU32 InitialDelayInKB; + mfxU16 reserved1[20]; + } Layer[8]; +} mfxExtTemporalLayers; + +#endif + +typedef struct { + mfxExtBuffer Header; + + mfxU16 NumRect; + mfxU16 reserved1[11]; + + struct { + mfxU32 Left; + mfxU32 Top; + mfxU32 Right; + mfxU32 Bottom; + + mfxU16 reserved2[8]; + } Rect[256]; +} mfxExtDirtyRect; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 NumRect; + mfxU16 reserved1[11]; + + struct { + mfxU32 DestLeft; + mfxU32 DestTop; + mfxU32 DestRight; + mfxU32 DestBottom; + + mfxU32 SourceLeft; + mfxU32 SourceTop; + mfxU16 reserved2[4]; + } Rect[256]; +} mfxExtMoveRect; + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + +/* ScalingMatrixType */ +enum { + MFX_SCALING_MATRIX_SPS = 0, + MFX_SCALING_MATRIX_PPS = 1 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 Type; + mfxU16 reserved[5]; + + /* [4x4_Intra_Y, 4x4_Intra_Cb, 4x4_Intra_Cr, + 4x4_Inter_Y, 4x4_Inter_Cb, 4x4_Inter_Cr, + 8x8_Intra_Y, 8x8_Inter_Y, 8x8_Intra_Cb, + 8x8_Inter_Cb, 8x8_Intra_Cr, 8x8_Inter_Cr] */ + mfxU8 ScalingListPresent[12]; + + /* [Intra_Y, Intra_Cb, Intra_Cr, + Inter_Y, Inter_Cb, Inter_Cr] */ + mfxU8 ScalingList4x4[6][16]; + + /* [Intra_Y, Inter_Y, Intra_Cb, + Inter_Cb, Intra_Cr, Inter_Cr] */ + mfxU8 ScalingList8x8[6][64]; +} mfxExtAVCScalingMatrix; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 reserved[28]; + + mfxU8 LoadMatrix[4]; // [LumaIntra, LumaInter, ChromaIntra, ChromaInter] + mfxU8 Matrix[4][64]; // [LumaIntra, LumaInter, ChromaIntra, ChromaInter] +} mfxExtMPEG2QuantMatrix; + +#endif + +/* Angle */ +enum { + MFX_ANGLE_0 = 0, + MFX_ANGLE_90 = 90, + MFX_ANGLE_180 = 180, + MFX_ANGLE_270 = 270 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 Angle; + mfxU16 reserved[11]; +} mfxExtVPPRotation; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 SliceSizeOverflow; + mfxU16 NumSliceNonCopliant; + mfxU16 NumEncodedSlice; + mfxU16 NumSliceSizeAlloc; + union { + mfxU16 *SliceSize; + mfxU64 reserved1; + }; + + mfxU16 reserved[20]; +} mfxExtEncodedSlicesInfo; + +/* ScalingMode */ +enum { + MFX_SCALING_MODE_DEFAULT = 0, + MFX_SCALING_MODE_LOWPOWER = 1, + MFX_SCALING_MODE_QUALITY = 2 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 ScalingMode; + mfxU16 reserved[11]; +} mfxExtVPPScaling; + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + +/* SceneChangeType */ +enum { + MFX_SCENE_NO_CHANGE = 0, + MFX_SCENE_START = 1, + MFX_SCENE_END = 2 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 Type; + mfxU16 reserved[11]; +} mfxExtSceneChange; + +#endif + +typedef mfxExtAVCRefListCtrl mfxExtHEVCRefListCtrl; +typedef mfxExtAVCRefLists mfxExtHEVCRefLists; +typedef mfxExtAvcTemporalLayers mfxExtHEVCTemporalLayers; + +/* MirroringType */ +enum +{ + MFX_MIRRORING_DISABLED = 0, + MFX_MIRRORING_HORIZONTAL = 1, + MFX_MIRRORING_VERTICAL = 2 +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 Type; + mfxU16 reserved[11]; +} mfxExtVPPMirroring; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 StickTop; /* tri-state option */ + mfxU16 StickBottom; /* tri-state option */ + mfxU16 StickLeft; /* tri-state option */ + mfxU16 StickRight; /* tri-state option */ + mfxU16 reserved[8]; +} mfxExtMVOverPicBoundaries; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 Enable; /* tri-state option */ + mfxU16 reserved[11]; +} mfxExtVPPColorFill; + +#if (MFX_VERSION >= 1025) + +/* ChromaSiting */ +enum { + MFX_CHROMA_SITING_UNKNOWN = 0x0000, + MFX_CHROMA_SITING_VERTICAL_TOP = 0x0001, /* Chroma samples are co-sited vertically on the top with the luma samples. */ + MFX_CHROMA_SITING_VERTICAL_CENTER = 0x0002, /* Chroma samples are not co-sited vertically with the luma samples. */ + MFX_CHROMA_SITING_VERTICAL_BOTTOM = 0x0004, /* Chroma samples are co-sited vertically on the bottom with the luma samples. */ + MFX_CHROMA_SITING_HORIZONTAL_LEFT = 0x0010, /* Chroma samples are co-sited horizontally on the left with the luma samples. */ + MFX_CHROMA_SITING_HORIZONTAL_CENTER = 0x0020 /* Chroma samples are not co-sited horizontally with the luma samples. */ +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 ChromaSiting; + mfxU16 reserved[27]; +} mfxExtColorConversion; + +#endif + +#if (MFX_VERSION >= 1026) +/* VP9ReferenceFrame */ +enum { + MFX_VP9_REF_INTRA = 0, + MFX_VP9_REF_LAST = 1, + MFX_VP9_REF_GOLDEN = 2, + MFX_VP9_REF_ALTREF = 3 +}; + +/* SegmentIdBlockSize */ +enum { + MFX_VP9_SEGMENT_ID_BLOCK_SIZE_UNKNOWN = 0, + MFX_VP9_SEGMENT_ID_BLOCK_SIZE_8x8 = 8, + MFX_VP9_SEGMENT_ID_BLOCK_SIZE_16x16 = 16, + MFX_VP9_SEGMENT_ID_BLOCK_SIZE_32x32 = 32, + MFX_VP9_SEGMENT_ID_BLOCK_SIZE_64x64 = 64, +}; + +/* SegmentFeature */ +enum { + MFX_VP9_SEGMENT_FEATURE_QINDEX = 0x0001, + MFX_VP9_SEGMENT_FEATURE_LOOP_FILTER = 0x0002, + MFX_VP9_SEGMENT_FEATURE_REFERENCE = 0x0004, + MFX_VP9_SEGMENT_FEATURE_SKIP = 0x0008 /* (0,0) MV, no residual */ +}; + +typedef struct { + mfxU16 FeatureEnabled; /* see enum SegmentFeature */ + mfxI16 QIndexDelta; + mfxI16 LoopFilterLevelDelta; + mfxU16 ReferenceFrame; /* see enum VP9ReferenceFrame */ + mfxU16 reserved[12]; +} mfxVP9SegmentParam; + +typedef struct { + mfxExtBuffer Header; + mfxU16 NumSegments; /* 0..8 */ + mfxVP9SegmentParam Segment[8]; + mfxU16 SegmentIdBlockSize; /* see enum SegmentIdBlockSize */ + mfxU32 NumSegmentIdAlloc; /* >= (Ceil(Width / SegmentIdBlockSize) * Ceil(Height / SegmentIdBlockSize)) */ + union { + mfxU8 *SegmentId; /*[NumSegmentIdAlloc] = 0..7, index in Segment array, blocks of SegmentIdBlockSize map */ + mfxU64 reserved1; + }; + mfxU16 reserved[52]; +} mfxExtVP9Segmentation; + +typedef struct { + mfxU16 FrameRateScale; /* Layer[n].FrameRateScale = Layer[n - 1].FrameRateScale * (uint)m */ + mfxU16 TargetKbps; /* affected by BRCParamMultiplier, Layer[n].TargetKbps > Layer[n - 1].TargetKbps */ + mfxU16 reserved[14]; +} mfxVP9TemporalLayer; + +typedef struct { + mfxExtBuffer Header; + mfxVP9TemporalLayer Layer[8]; + mfxU16 reserved[60]; +} mfxExtVP9TemporalLayers; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 FrameWidth; + mfxU16 FrameHeight; + + mfxU16 WriteIVFHeaders; /* tri-state option */ + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + mfxI16 LoopFilterRefDelta[4]; + mfxI16 LoopFilterModeDelta[2]; +#else // API 1.26 + mfxI16 reserved1[6]; +#endif + mfxI16 QIndexDeltaLumaDC; + mfxI16 QIndexDeltaChromaAC; + mfxI16 QIndexDeltaChromaDC; +#if (MFX_VERSION >= MFX_VERSION_NEXT) + mfxU16 NumTileRows; + mfxU16 NumTileColumns; + mfxU16 reserved[110]; +#else // API 1.26 + mfxU16 reserved[112]; +#endif +} mfxExtVP9Param; + +#endif // #if (MFX_VERSION >= 1026) + +#if (MFX_VERSION >= 1025) +/* Multi-Frame Mode */ +enum { + MFX_MF_DEFAULT = 0, + MFX_MF_DISABLED = 1, + MFX_MF_AUTO = 2, + MFX_MF_MANUAL = 3 +}; + +/* Multi-Frame Initialization parameters */ +typedef struct { + mfxExtBuffer Header; + + mfxU16 MFMode; + mfxU16 MaxNumFrames; + + mfxU16 reserved[58]; +} mfxExtMultiFrameParam; + +/* Multi-Frame Run-time controls */ +typedef struct { + mfxExtBuffer Header; + + mfxU32 Timeout; /* timeout in millisecond */ + mfxU16 Flush; /* Flush internal frame buffer, e.g. submit all collected frames. */ + + mfxU16 reserved[57]; +} mfxExtMultiFrameControl; + +typedef struct { + mfxU16 Type; + mfxU16 reserved1; + mfxU32 Offset; + mfxU32 Size; + mfxU32 reserved[5]; +} mfxEncodedUnitInfo; + +typedef struct { + mfxExtBuffer Header; + + union { + mfxEncodedUnitInfo *UnitInfo; + mfxU64 reserved1; + }; + mfxU16 NumUnitsAlloc; + mfxU16 NumUnitsEncoded; + + mfxU16 reserved[22]; +} mfxExtEncodedUnitsInfo; + +#endif + +#if (MFX_VERSION >= 1026) +#if (MFX_VERSION >= MFX_VERSION_NEXT) +/* MCTFTemporalMode */ +enum { + MFX_MCTF_TEMPORAL_MODE_UNKNOWN = 0, + MFX_MCTF_TEMPORAL_MODE_SPATIAL = 1, + MFX_MCTF_TEMPORAL_MODE_1REF = 2, + MFX_MCTF_TEMPORAL_MODE_2REF = 3, + MFX_MCTF_TEMPORAL_MODE_4REF = 4 +}; +#endif + +/* MCTF initialization & runtime */ +typedef struct { + mfxExtBuffer Header; + mfxU16 FilterStrength; +#if (MFX_VERSION >= MFX_VERSION_NEXT) + mfxU16 Overlap; /* tri-state option */ + mfxU32 BitsPerPixelx100k; + mfxU16 Deblocking; /* tri-state option */ + mfxU16 TemporalMode; + mfxU16 MVPrecision; + mfxU16 reserved[21]; +#else + mfxU16 reserved[27]; +#endif +} mfxExtVppMctf; + +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/vshampor/deshuffler/msdk_api/include/mfxvideo++.h b/vshampor/deshuffler/msdk_api/include/mfxvideo++.h new file mode 100644 index 0000000..9256c95 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxvideo++.h @@ -0,0 +1,188 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef __MFXVIDEOPLUSPLUS_H +#define __MFXVIDEOPLUSPLUS_H + +#include "mfxvideo.h" +#include "mfxenc.h" +#include "mfxpak.h" + +class MFXVideoSession +{ +public: + MFXVideoSession(void) { m_session = (mfxSession) 0; } + virtual ~MFXVideoSession(void) { Close(); } + + virtual mfxStatus Init(mfxIMPL impl, mfxVersion *ver) { return MFXInit(impl, ver, &m_session); } + virtual mfxStatus InitEx(mfxInitParam par) { return MFXInitEx(par, &m_session); } + virtual mfxStatus Close(void) + { + mfxStatus mfxRes; + mfxRes = MFXClose(m_session); m_session = (mfxSession) 0; + return mfxRes; + } + + virtual mfxStatus QueryIMPL(mfxIMPL *impl) { return MFXQueryIMPL(m_session, impl); } + virtual mfxStatus QueryVersion(mfxVersion *version) { return MFXQueryVersion(m_session, version); } + + virtual mfxStatus JoinSession(mfxSession child_session) { return MFXJoinSession(m_session, child_session);} + virtual mfxStatus DisjoinSession( ) { return MFXDisjoinSession(m_session);} + virtual mfxStatus CloneSession( mfxSession *clone) { return MFXCloneSession(m_session, clone);} + virtual mfxStatus SetPriority( mfxPriority priority) { return MFXSetPriority(m_session, priority);} + virtual mfxStatus GetPriority( mfxPriority *priority) { return MFXGetPriority(m_session, priority);} + + virtual mfxStatus SetBufferAllocator(mfxBufferAllocator *allocator) { return MFXVideoCORE_SetBufferAllocator(m_session, allocator); } + virtual mfxStatus SetFrameAllocator(mfxFrameAllocator *allocator) { return MFXVideoCORE_SetFrameAllocator(m_session, allocator); } + virtual mfxStatus SetHandle(mfxHandleType type, mfxHDL hdl) { return MFXVideoCORE_SetHandle(m_session, type, hdl); } + virtual mfxStatus GetHandle(mfxHandleType type, mfxHDL *hdl) { return MFXVideoCORE_GetHandle(m_session, type, hdl); } + virtual mfxStatus QueryPlatform(mfxPlatform* platform) { return MFXVideoCORE_QueryPlatform(m_session, platform); } + + virtual mfxStatus SyncOperation(mfxSyncPoint syncp, mfxU32 wait) { return MFXVideoCORE_SyncOperation(m_session, syncp, wait); } + + virtual mfxStatus DoWork() { return MFXDoWork(m_session); } + + virtual operator mfxSession (void) { return m_session; } + +protected: + + mfxSession m_session; // (mfxSession) handle to the owning session +private: + MFXVideoSession(const MFXVideoSession &); + void operator=(MFXVideoSession &); +}; + +class MFXVideoENCODE +{ +public: + + MFXVideoENCODE(mfxSession session) { m_session = session; } + virtual ~MFXVideoENCODE(void) { Close(); } + + virtual mfxStatus Query(mfxVideoParam *in, mfxVideoParam *out) { return MFXVideoENCODE_Query(m_session, in, out); } + virtual mfxStatus QueryIOSurf(mfxVideoParam *par, mfxFrameAllocRequest *request) { return MFXVideoENCODE_QueryIOSurf(m_session, par, request); } + virtual mfxStatus Init(mfxVideoParam *par) { return MFXVideoENCODE_Init(m_session, par); } + virtual mfxStatus Reset(mfxVideoParam *par) { return MFXVideoENCODE_Reset(m_session, par); } + virtual mfxStatus Close(void) { return MFXVideoENCODE_Close(m_session); } + + virtual mfxStatus GetVideoParam(mfxVideoParam *par) { return MFXVideoENCODE_GetVideoParam(m_session, par); } + virtual mfxStatus GetEncodeStat(mfxEncodeStat *stat) { return MFXVideoENCODE_GetEncodeStat(m_session, stat); } + + virtual mfxStatus EncodeFrameAsync(mfxEncodeCtrl *ctrl, mfxFrameSurface1 *surface, mfxBitstream *bs, mfxSyncPoint *syncp) { return MFXVideoENCODE_EncodeFrameAsync(m_session, ctrl, surface, bs, syncp); } + +protected: + + mfxSession m_session; // (mfxSession) handle to the owning session +}; + +class MFXVideoDECODE +{ +public: + + MFXVideoDECODE(mfxSession session) { m_session = session; } + virtual ~MFXVideoDECODE(void) { Close(); } + + virtual mfxStatus Query(mfxVideoParam *in, mfxVideoParam *out) { return MFXVideoDECODE_Query(m_session, in, out); } + virtual mfxStatus DecodeHeader(mfxBitstream *bs, mfxVideoParam *par) { return MFXVideoDECODE_DecodeHeader(m_session, bs, par); } + virtual mfxStatus QueryIOSurf(mfxVideoParam *par, mfxFrameAllocRequest *request) { return MFXVideoDECODE_QueryIOSurf(m_session, par, request); } + virtual mfxStatus Init(mfxVideoParam *par) { return MFXVideoDECODE_Init(m_session, par); } + virtual mfxStatus Reset(mfxVideoParam *par) { return MFXVideoDECODE_Reset(m_session, par); } + virtual mfxStatus Close(void) { return MFXVideoDECODE_Close(m_session); } + + virtual mfxStatus GetVideoParam(mfxVideoParam *par) { return MFXVideoDECODE_GetVideoParam(m_session, par); } + + virtual mfxStatus GetDecodeStat(mfxDecodeStat *stat) { return MFXVideoDECODE_GetDecodeStat(m_session, stat); } + virtual mfxStatus GetPayload(mfxU64 *ts, mfxPayload *payload) {return MFXVideoDECODE_GetPayload(m_session, ts, payload); } + virtual mfxStatus SetSkipMode(mfxSkipMode mode) { return MFXVideoDECODE_SetSkipMode(m_session, mode); } + virtual mfxStatus DecodeFrameAsync(mfxBitstream *bs, mfxFrameSurface1 *surface_work, mfxFrameSurface1 **surface_out, mfxSyncPoint *syncp) { return MFXVideoDECODE_DecodeFrameAsync(m_session, bs, surface_work, surface_out, syncp); } + +protected: + + mfxSession m_session; // (mfxSession) handle to the owning session +}; + +class MFXVideoVPP +{ +public: + + MFXVideoVPP(mfxSession session) { m_session = session; } + virtual ~MFXVideoVPP(void) { Close(); } + + virtual mfxStatus Query(mfxVideoParam *in, mfxVideoParam *out) { return MFXVideoVPP_Query(m_session, in, out); } + virtual mfxStatus QueryIOSurf(mfxVideoParam *par, mfxFrameAllocRequest request[2]) { return MFXVideoVPP_QueryIOSurf(m_session, par, request); } + virtual mfxStatus Init(mfxVideoParam *par) { return MFXVideoVPP_Init(m_session, par); } + virtual mfxStatus Reset(mfxVideoParam *par) { return MFXVideoVPP_Reset(m_session, par); } + virtual mfxStatus Close(void) { return MFXVideoVPP_Close(m_session); } + + virtual mfxStatus GetVideoParam(mfxVideoParam *par) { return MFXVideoVPP_GetVideoParam(m_session, par); } + virtual mfxStatus GetVPPStat(mfxVPPStat *stat) { return MFXVideoVPP_GetVPPStat(m_session, stat); } + virtual mfxStatus RunFrameVPPAsync(mfxFrameSurface1 *in, mfxFrameSurface1 *out, mfxExtVppAuxData *aux, mfxSyncPoint *syncp) { return MFXVideoVPP_RunFrameVPPAsync(m_session, in, out, aux, syncp); } + virtual mfxStatus RunFrameVPPAsyncEx(mfxFrameSurface1 *in, mfxFrameSurface1 *work, mfxFrameSurface1 **out, mfxSyncPoint *syncp) {return MFXVideoVPP_RunFrameVPPAsyncEx(m_session, in, work, out, syncp); } + +protected: + + mfxSession m_session; // (mfxSession) handle to the owning session +}; + +class MFXVideoENC +{ +public: + + MFXVideoENC(mfxSession session) { m_session = session; } + virtual ~MFXVideoENC(void) { Close(); } + + virtual mfxStatus Query(mfxVideoParam *in, mfxVideoParam *out) { return MFXVideoENC_Query(m_session, in, out); } + virtual mfxStatus QueryIOSurf(mfxVideoParam *par, mfxFrameAllocRequest *request) { return MFXVideoENC_QueryIOSurf(m_session, par, request); } + virtual mfxStatus Init(mfxVideoParam *par) { return MFXVideoENC_Init(m_session, par); } + virtual mfxStatus Reset(mfxVideoParam *par) { return MFXVideoENC_Reset(m_session, par); } + virtual mfxStatus Close(void) { return MFXVideoENC_Close(m_session); } + + virtual mfxStatus GetVideoParam(mfxVideoParam *par) { return MFXVideoENC_GetVideoParam(m_session, par); } + virtual mfxStatus ProcessFrameAsync(mfxENCInput *in, mfxENCOutput *out, mfxSyncPoint *syncp) { return MFXVideoENC_ProcessFrameAsync(m_session, in, out, syncp); } + +protected: + + mfxSession m_session; // (mfxSession) handle to the owning session +}; + +class MFXVideoPAK +{ +public: + + MFXVideoPAK(mfxSession session) { m_session = session; } + virtual ~MFXVideoPAK(void) { Close(); } + + virtual mfxStatus Query(mfxVideoParam *in, mfxVideoParam *out) { return MFXVideoPAK_Query(m_session, in, out); } + virtual mfxStatus QueryIOSurf(mfxVideoParam *par, mfxFrameAllocRequest *request) { return MFXVideoPAK_QueryIOSurf(m_session, par, request); } + virtual mfxStatus Init(mfxVideoParam *par) { return MFXVideoPAK_Init(m_session, par); } + virtual mfxStatus Reset(mfxVideoParam *par) { return MFXVideoPAK_Reset(m_session, par); } + virtual mfxStatus Close(void) { return MFXVideoPAK_Close(m_session); } + + virtual mfxStatus GetVideoParam(mfxVideoParam *par) { return MFXVideoPAK_GetVideoParam(m_session, par); } + //virtual mfxStatus GetEncodeStat(mfxEncodeStat *stat) { return MFXVideoENCODE_GetEncodeStat(m_session, stat); } + + virtual mfxStatus ProcessFrameAsync(mfxPAKInput *in, mfxPAKOutput *out, mfxSyncPoint *syncp) { return MFXVideoPAK_ProcessFrameAsync(m_session, in, out, syncp); } + +protected: + + mfxSession m_session; // (mfxSession) handle to the owning session +}; + +#endif // __MFXVIDEOPLUSPLUS_H diff --git a/vshampor/deshuffler/msdk_api/include/mfxvideo.h b/vshampor/deshuffler/msdk_api/include/mfxvideo.h new file mode 100644 index 0000000..727dd24 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxvideo.h @@ -0,0 +1,100 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXVIDEO_H__ +#define __MFXVIDEO_H__ +#include "mfxsession.h" +#include "mfxvstructures.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* MFXVideoCORE */ +typedef struct { + mfxU32 reserved[4]; + mfxHDL pthis; + mfxStatus (MFX_CDECL *Alloc) (mfxHDL pthis, mfxU32 nbytes, mfxU16 type, mfxMemId *mid); + mfxStatus (MFX_CDECL *Lock) (mfxHDL pthis, mfxMemId mid, mfxU8 **ptr); + mfxStatus (MFX_CDECL *Unlock) (mfxHDL pthis, mfxMemId mid); + mfxStatus (MFX_CDECL *Free) (mfxHDL pthis, mfxMemId mid); +} mfxBufferAllocator; + +typedef struct { + mfxU32 reserved[4]; + mfxHDL pthis; + + mfxStatus (MFX_CDECL *Alloc) (mfxHDL pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response); + mfxStatus (MFX_CDECL *Lock) (mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); + mfxStatus (MFX_CDECL *Unlock) (mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); + mfxStatus (MFX_CDECL *GetHDL) (mfxHDL pthis, mfxMemId mid, mfxHDL *handle); + mfxStatus (MFX_CDECL *Free) (mfxHDL pthis, mfxFrameAllocResponse *response); +} mfxFrameAllocator; + +/* VideoCORE */ +mfxStatus MFX_CDECL MFXVideoCORE_SetBufferAllocator(mfxSession session, mfxBufferAllocator *allocator); +mfxStatus MFX_CDECL MFXVideoCORE_SetFrameAllocator(mfxSession session, mfxFrameAllocator *allocator); +mfxStatus MFX_CDECL MFXVideoCORE_SetHandle(mfxSession session, mfxHandleType type, mfxHDL hdl); +mfxStatus MFX_CDECL MFXVideoCORE_GetHandle(mfxSession session, mfxHandleType type, mfxHDL *hdl); +mfxStatus MFX_CDECL MFXVideoCORE_QueryPlatform(mfxSession session, mfxPlatform* platform); +mfxStatus MFX_CDECL MFXVideoCORE_SyncOperation(mfxSession session, mfxSyncPoint syncp, mfxU32 wait); + +/* VideoENCODE */ +mfxStatus MFX_CDECL MFXVideoENCODE_Query(mfxSession session, mfxVideoParam *in, mfxVideoParam *out); +mfxStatus MFX_CDECL MFXVideoENCODE_QueryIOSurf(mfxSession session, mfxVideoParam *par, mfxFrameAllocRequest *request); +mfxStatus MFX_CDECL MFXVideoENCODE_Init(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoENCODE_Reset(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoENCODE_Close(mfxSession session); + +mfxStatus MFX_CDECL MFXVideoENCODE_GetVideoParam(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoENCODE_GetEncodeStat(mfxSession session, mfxEncodeStat *stat); +mfxStatus MFX_CDECL MFXVideoENCODE_EncodeFrameAsync(mfxSession session, mfxEncodeCtrl *ctrl, mfxFrameSurface1 *surface, mfxBitstream *bs, mfxSyncPoint *syncp); + +/* VideoDECODE */ +mfxStatus MFX_CDECL MFXVideoDECODE_Query(mfxSession session, mfxVideoParam *in, mfxVideoParam *out); +mfxStatus MFX_CDECL MFXVideoDECODE_DecodeHeader(mfxSession session, mfxBitstream *bs, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoDECODE_QueryIOSurf(mfxSession session, mfxVideoParam *par, mfxFrameAllocRequest *request); +mfxStatus MFX_CDECL MFXVideoDECODE_Init(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoDECODE_Reset(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoDECODE_Close(mfxSession session); + +mfxStatus MFX_CDECL MFXVideoDECODE_GetVideoParam(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoDECODE_GetDecodeStat(mfxSession session, mfxDecodeStat *stat); +mfxStatus MFX_CDECL MFXVideoDECODE_SetSkipMode(mfxSession session, mfxSkipMode mode); +mfxStatus MFX_CDECL MFXVideoDECODE_GetPayload(mfxSession session, mfxU64 *ts, mfxPayload *payload); +mfxStatus MFX_CDECL MFXVideoDECODE_DecodeFrameAsync(mfxSession session, mfxBitstream *bs, mfxFrameSurface1 *surface_work, mfxFrameSurface1 **surface_out, mfxSyncPoint *syncp); + +/* VideoVPP */ +mfxStatus MFX_CDECL MFXVideoVPP_Query(mfxSession session, mfxVideoParam *in, mfxVideoParam *out); +mfxStatus MFX_CDECL MFXVideoVPP_QueryIOSurf(mfxSession session, mfxVideoParam *par, mfxFrameAllocRequest request[2]); +mfxStatus MFX_CDECL MFXVideoVPP_Init(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoVPP_Reset(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoVPP_Close(mfxSession session); + +mfxStatus MFX_CDECL MFXVideoVPP_GetVideoParam(mfxSession session, mfxVideoParam *par); +mfxStatus MFX_CDECL MFXVideoVPP_GetVPPStat(mfxSession session, mfxVPPStat *stat); +mfxStatus MFX_CDECL MFXVideoVPP_RunFrameVPPAsync(mfxSession session, mfxFrameSurface1 *in, mfxFrameSurface1 *out, mfxExtVppAuxData *aux, mfxSyncPoint *syncp); +mfxStatus MFX_CDECL MFXVideoVPP_RunFrameVPPAsyncEx(mfxSession session, mfxFrameSurface1 *in, mfxFrameSurface1 *surface_work, mfxFrameSurface1 **surface_out, mfxSyncPoint *syncp); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/vshampor/deshuffler/msdk_api/include/mfxvp8.h b/vshampor/deshuffler/msdk_api/include/mfxvp8.h new file mode 100644 index 0000000..1adcc94 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxvp8.h @@ -0,0 +1,69 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXVP8_H__ +#define __MFXVP8_H__ + +#include "mfxdefs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + MFX_CODEC_VP8 = MFX_MAKEFOURCC('V','P','8',' '), +}; + +/* CodecProfile*/ +enum { + MFX_PROFILE_VP8_0 = 0+1, + MFX_PROFILE_VP8_1 = 1+1, + MFX_PROFILE_VP8_2 = 2+1, + MFX_PROFILE_VP8_3 = 3+1, +}; + +/* Extended Buffer Ids */ +enum { + MFX_EXTBUFF_VP8_CODING_OPTION = MFX_MAKEFOURCC('V','P','8','E'), +}; + +typedef struct { + mfxExtBuffer Header; + + mfxU16 Version; + mfxU16 EnableMultipleSegments; + mfxU16 LoopFilterType; + mfxU16 LoopFilterLevel[4]; + mfxU16 SharpnessLevel; + mfxU16 NumTokenPartitions; + mfxI16 LoopFilterRefTypeDelta[4]; + mfxI16 LoopFilterMbModeDelta[4]; + mfxI16 SegmentQPDelta[4]; + mfxI16 CoeffTypeQPDelta[5]; + mfxU16 WriteIVFHeaders; + mfxU32 NumFramesForIVFHeader; + mfxU16 reserved[223]; +} mfxExtVP8CodingOption; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif + diff --git a/vshampor/deshuffler/msdk_api/include/mfxvp9.h b/vshampor/deshuffler/msdk_api/include/mfxvp9.h new file mode 100644 index 0000000..e29670a --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxvp9.h @@ -0,0 +1,52 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#ifndef __MFXVP9_H__ +#define __MFXVP9_H__ + +#include "mfxdefs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + +/* Extended Buffer Ids */ +enum { + MFX_EXTBUFF_VP9_DECODED_FRAME_INFO = MFX_MAKEFOURCC('9','D','F','I') +}; + + +typedef struct { + mfxExtBuffer Header; + + mfxU16 DisplayWidth; + mfxU16 DisplayHeight; + mfxU16 reserved[58]; +} mfxExtVP9DecodedFrameInfo; + +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif + diff --git a/vshampor/deshuffler/msdk_api/include/mfxvstructures.h b/vshampor/deshuffler/msdk_api/include/mfxvstructures.h new file mode 100644 index 0000000..5fbefec --- /dev/null +++ b/vshampor/deshuffler/msdk_api/include/mfxvstructures.h @@ -0,0 +1,22 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#include "mfxstructures.h" + + diff --git a/vshampor/deshuffler/msdk_api/mediasdk_structures/ts_ext_buffers_decl.h b/vshampor/deshuffler/msdk_api/mediasdk_structures/ts_ext_buffers_decl.h new file mode 100644 index 0000000..1a79833 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/mediasdk_structures/ts_ext_buffers_decl.h @@ -0,0 +1,141 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +EXTBUF(mfxExtCodingOption , MFX_EXTBUFF_CODING_OPTION ) +EXTBUF(mfxExtCodingOptionSPSPPS , MFX_EXTBUFF_CODING_OPTION_SPSPPS ) +EXTBUF(mfxExtVPPDoNotUse , MFX_EXTBUFF_VPP_DONOTUSE ) +EXTBUF(mfxExtVppAuxData , MFX_EXTBUFF_VPP_AUXDATA ) +EXTBUF(mfxExtVPPDenoise , MFX_EXTBUFF_VPP_DENOISE ) +EXTBUF(mfxExtVPPProcAmp , MFX_EXTBUFF_VPP_PROCAMP ) +EXTBUF(mfxExtVPPDetail , MFX_EXTBUFF_VPP_DETAIL ) +EXTBUF(mfxExtVideoSignalInfo , MFX_EXTBUFF_VIDEO_SIGNAL_INFO ) +EXTBUF(mfxExtVPPDoUse , MFX_EXTBUFF_VPP_DOUSE ) +EXTBUF(mfxExtOpaqueSurfaceAlloc , MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION ) +EXTBUF(mfxExtAVCRefListCtrl , MFX_EXTBUFF_AVC_REFLIST_CTRL ) +EXTBUF(mfxExtVPPFrameRateConversion , MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION ) +EXTBUF(mfxExtPictureTimingSEI , MFX_EXTBUFF_PICTURE_TIMING_SEI ) +EXTBUF(mfxExtAvcTemporalLayers , MFX_EXTBUFF_AVC_TEMPORAL_LAYERS ) +EXTBUF(mfxExtCodingOption2 , MFX_EXTBUFF_CODING_OPTION2 ) +EXTBUF(mfxExtVPPImageStab , MFX_EXTBUFF_VPP_IMAGE_STABILIZATION ) +EXTBUF(mfxExtEncoderCapability , MFX_EXTBUFF_ENCODER_CAPABILITY ) +EXTBUF(mfxExtEncoderResetOption , MFX_EXTBUFF_ENCODER_RESET_OPTION ) +EXTBUF(mfxExtAVCEncodedFrameInfo , MFX_EXTBUFF_ENCODED_FRAME_INFO ) +EXTBUF(mfxExtVPPComposite , MFX_EXTBUFF_VPP_COMPOSITE ) +EXTBUF(mfxExtVPPVideoSignalInfo , MFX_EXTBUFF_VPP_VIDEO_SIGNAL_INFO ) +EXTBUF(mfxExtEncoderROI , MFX_EXTBUFF_ENCODER_ROI ) +EXTBUF(mfxExtVPPDeinterlacing , MFX_EXTBUFF_VPP_DEINTERLACING ) +EXTBUF(mfxExtVP8CodingOption , MFX_EXTBUFF_VP8_CODING_OPTION ) +EXTBUF(mfxExtLAControl , MFX_EXTBUFF_LOOKAHEAD_CTRL ) +EXTBUF(mfxExtVPPFieldProcessing , MFX_EXTBUFF_VPP_FIELD_PROCESSING ) +EXTBUF(mfxExtContentLightLevelInfo , MFX_EXTBUFF_CONTENT_LIGHT_LEVEL_INFO ) +EXTBUF(mfxExtMasteringDisplayColourVolume, MFX_EXTBUFF_MASTERING_DISPLAY_COLOUR_VOLUME ) +EXTBUF(mfxExtMultiFrameParam , MFX_EXTBUFF_MULTI_FRAME_PARAM ) +EXTBUF(mfxExtMultiFrameControl , MFX_EXTBUFF_MULTI_FRAME_CONTROL ) +#if (MFX_VERSION >= 1026) +EXTBUF(mfxExtVppMctf , MFX_EXTBUFF_VPP_MCTF ) +#endif +EXTBUF(mfxExtColorConversion , MFX_EXTBUFF_VPP_COLOR_CONVERSION ) +// FEI +EXTBUF(mfxExtFeiParam , MFX_EXTBUFF_FEI_PARAM ) +EXTBUF(mfxExtFeiSPS , MFX_EXTBUFF_FEI_SPS ) +EXTBUF(mfxExtFeiPPS , MFX_EXTBUFF_FEI_PPS ) +EXTBUF(mfxExtFeiEncFrameCtrl , MFX_EXTBUFF_FEI_ENC_CTRL ) +EXTBUF(mfxExtFeiEncMVPredictors , MFX_EXTBUFF_FEI_ENC_MV_PRED ) +EXTBUF(mfxExtFeiEncMBCtrl , MFX_EXTBUFF_FEI_ENC_MB ) +EXTBUF(mfxExtFeiEncMV , MFX_EXTBUFF_FEI_ENC_MV ) +EXTBUF(mfxExtFeiEncMBStat , MFX_EXTBUFF_FEI_ENC_MB_STAT ) +EXTBUF(mfxExtFeiEncQP , MFX_EXTBUFF_FEI_ENC_QP ) +EXTBUF(mfxExtFeiPreEncCtrl , MFX_EXTBUFF_FEI_PREENC_CTRL ) +EXTBUF(mfxExtFeiPreEncMVPredictors , MFX_EXTBUFF_FEI_PREENC_MV_PRED ) +EXTBUF(mfxExtFeiPreEncMV , MFX_EXTBUFF_FEI_PREENC_MV ) +EXTBUF(mfxExtFeiPreEncMBStat , MFX_EXTBUFF_FEI_PREENC_MB ) +EXTBUF(mfxExtFeiPakMBCtrl , MFX_EXTBUFF_FEI_PAK_CTRL ) +EXTBUF(mfxExtFeiSliceHeader , MFX_EXTBUFF_FEI_SLICE ) +EXTBUF(mfxExtFeiRepackCtrl , MFX_EXTBUFF_FEI_REPACK_CTRL ) +EXTBUF(mfxExtFeiDecStreamOut , MFX_EXTBUFF_FEI_DEC_STREAM_OUT ) +#if (MFX_VERSION >= 1027) +EXTBUF(mfxExtFeiHevcEncFrameCtrl , MFX_EXTBUFF_HEVCFEI_ENC_CTRL ) +EXTBUF(mfxExtFeiHevcEncMVPredictors , MFX_EXTBUFF_HEVCFEI_ENC_MV_PRED ) +EXTBUF(mfxExtFeiHevcEncQP , MFX_EXTBUFF_HEVCFEI_ENC_QP ) +EXTBUF(mfxExtFeiHevcEncCtuCtrl , MFX_EXTBUFF_HEVCFEI_ENC_CTU_CTRL ) +#endif +// end of FEI +// Camera +EXTBUF(mfxExtCamTotalColorControl , MFX_EXTBUF_CAM_TOTAL_COLOR_CONTROL ) +EXTBUF(mfxExtCamCscYuvRgb , MFX_EXTBUF_CAM_CSC_YUV_RGB ) +EXTBUF(mfxExtCamGammaCorrection , MFX_EXTBUF_CAM_GAMMA_CORRECTION ) +EXTBUF(mfxExtCamWhiteBalance , MFX_EXTBUF_CAM_WHITE_BALANCE ) +EXTBUF(mfxExtCamHotPixelRemoval , MFX_EXTBUF_CAM_HOT_PIXEL_REMOVAL ) +EXTBUF(mfxExtCamBlackLevelCorrection, MFX_EXTBUF_CAM_BLACK_LEVEL_CORRECTION ) +EXTBUF(mfxExtCamVignetteCorrection , MFX_EXTBUF_CAM_VIGNETTE_CORRECTION ) +EXTBUF(mfxExtCamBayerDenoise , MFX_EXTBUF_CAM_BAYER_DENOISE ) +EXTBUF(mfxExtCamColorCorrection3x3 , MFX_EXTBUF_CAM_COLOR_CORRECTION_3X3 ) +EXTBUF(mfxExtCamPadding , MFX_EXTBUF_CAM_PADDING ) +EXTBUF(mfxExtCamPipeControl , MFX_EXTBUF_CAM_PIPECONTROL ) +// end of Camera +EXTBUF(mfxExtAVCRefLists , MFX_EXTBUFF_AVC_REFLISTS ) +EXTBUF(mfxExtCodingOption3 , MFX_EXTBUFF_CODING_OPTION3 ) +EXTBUF(mfxExtMBQP , MFX_EXTBUFF_MBQP ) +EXTBUF(mfxExtMBForceIntra , MFX_EXTBUFF_MB_FORCE_INTRA ) + +EXTBUF(mfxExtChromaLocInfo , MFX_EXTBUFF_CHROMA_LOC_INFO ) +EXTBUF(mfxExtDecodedFrameInfo , MFX_EXTBUFF_DECODED_FRAME_INFO ) +EXTBUF(mfxExtDecodeErrorReport , MFX_EXTBUFF_DECODE_ERROR_REPORT ) + +// Threading API +EXTBUF(mfxExtThreadsParam , MFX_EXTBUFF_THREADS_PARAM) + +EXTBUF(mfxExtVPPRotation , MFX_EXTBUFF_VPP_ROTATION) +EXTBUF(mfxExtVPPMirroring , MFX_EXTBUFF_VPP_MIRRORING) +EXTBUF(mfxExtMVCSeqDesc , MFX_EXTBUFF_MVC_SEQ_DESC ) +//EXTBUF(mfxExtMVCTargetViews , MFX_EXTBUFF_MVC_TARGET_VIEWS ) +//EXTBUF(mfxExtJPEGQuantTables , MFX_EXTBUFF_JPEG_QT ) +//EXTBUF(mfxExtJPEGHuffmanTables , MFX_EXTBUFF_JPEG_HUFFMAN ) +//EXTBUF(mfxExtPAVPOption , MFX_EXTBUFF_PAVP_OPTION ) +EXTBUF(mfxExtMBDisableSkipMap , MFX_EXTBUFF_MB_DISABLE_SKIP_MAP) + +//Screen capture +EXTBUF(mfxExtScreenCaptureParam , MFX_EXTBUFF_SCREEN_CAPTURE_PARAM ) +EXTBUF(mfxExtDirtyRect , MFX_EXTBUFF_DIRTY_RECTANGLES ) +EXTBUF(mfxExtMoveRect , MFX_EXTBUFF_MOVING_RECTANGLES ) + +#if (MFX_VERSION >= MFX_VERSION_NEXT) +EXTBUF(mfxExtVP9DecodedFrameInfo , MFX_EXTBUFF_VP9_DECODED_FRAME_INFO ) +#endif + +#if (MFX_VERSION >= 1026) +EXTBUF(mfxExtVP9Segmentation , MFX_EXTBUFF_VP9_SEGMENTATION ) +EXTBUF(mfxExtVP9TemporalLayers , MFX_EXTBUFF_VP9_TEMPORAL_LAYERS ) +EXTBUF(mfxExtVP9Param , MFX_EXTBUFF_VP9_PARAM ) +#endif + +EXTBUF(mfxExtHEVCParam , MFX_EXTBUFF_HEVC_PARAM ) +EXTBUF(mfxExtPredWeightTable , MFX_EXTBUFF_PRED_WEIGHT_TABLE ) + +#if defined(__MFXBRC_H__) +EXTBUF(mfxExtBRC, MFX_EXTBUFF_BRC) +#endif // defined(__MFXBRC_H__) + +EXTBUF(mfxExtEncodedUnitsInfo , MFX_EXTBUFF_ENCODED_UNITS_INFO ) + +#if defined(__MFXSCD_H__) +EXTBUF(mfxExtSCD, MFX_EXTBUFF_SCD) +#endif // defined(__MFXSCD_H__) diff --git a/vshampor/deshuffler/msdk_api/mediasdk_structures/ts_struct_decl.h b/vshampor/deshuffler/msdk_api/mediasdk_structures/ts_struct_decl.h new file mode 100644 index 0000000..c563fc3 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/mediasdk_structures/ts_struct_decl.h @@ -0,0 +1,1376 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +STRUCT(mfxI16Pair, + FIELD_T(mfxI16, x) + FIELD_T(mfxI16, y) +) + +STRUCT(mfxHDLPair, + FIELD_T(mfxHDL, first ) + FIELD_T(mfxHDL, second) +) + +STRUCT(mfxExtBuffer, + FIELD_T(mfx4CC, BufferId) + FIELD_T(mfxU32, BufferSz) +) + +STRUCT(mfxVersion, + FIELD_T(mfxU16, Minor ) + FIELD_T(mfxU16, Major ) + FIELD_T(mfxU32, Version) +) + +STRUCT(mfxBitstream, + FIELD_T(mfxEncryptedData*, EncryptedData ) + FIELD_T(mfxExtBuffer ** , ExtParam ) + FIELD_T(mfxU16 , NumExtParam ) + FIELD_T(mfxI64 , DecodeTimeStamp) + FIELD_T(mfxU64 , TimeStamp ) + FIELD_T(mfxU8* , Data ) + FIELD_T(mfxU32 , DataOffset ) + FIELD_T(mfxU32 , DataLength ) + FIELD_T(mfxU32 , MaxLength ) + FIELD_T(mfxU16 , PicStruct ) + FIELD_T(mfxU16 , FrameType ) + FIELD_T(mfxU16 , DataFlag ) +) + +STRUCT(mfxFrameId, + FIELD_T(mfxU16, TemporalId ) + FIELD_T(mfxU16, PriorityId ) + FIELD_T(mfxU16, DependencyId) + FIELD_T(mfxU16, QualityId ) + FIELD_T(mfxU16, ViewId ) +) + +STRUCT(mfxFrameInfo, + FIELD_S(mfxFrameId, FrameId) + FIELD_T(mfxU16, BitDepthLuma ) + FIELD_T(mfxU16, BitDepthChroma) + FIELD_T(mfxU16, Shift ) + FIELD_T(mfx4CC, FourCC ) + FIELD_T(mfxU16, Width ) + FIELD_T(mfxU16, Height ) + FIELD_T(mfxU16, CropX ) + FIELD_T(mfxU16, CropY ) + FIELD_T(mfxU16, CropW ) + FIELD_T(mfxU16, CropH ) + FIELD_T(mfxU32, FrameRateExtN ) + FIELD_T(mfxU32, FrameRateExtD ) + FIELD_T(mfxU16, AspectRatioW ) + FIELD_T(mfxU16, AspectRatioH ) + FIELD_T(mfxU16, PicStruct ) + FIELD_T(mfxU16, ChromaFormat ) +) + +STRUCT(mfxFrameData, + FIELD_T(mfxExtBuffer**, ExtParam ) + FIELD_T(mfxU16 , NumExtParam ) + FIELD_T(mfxU16 , PitchHigh ) + FIELD_T(mfxU64 , TimeStamp ) + FIELD_T(mfxU32 , FrameOrder ) + FIELD_T(mfxU16 , Locked ) + FIELD_T(mfxU16 , Pitch ) + FIELD_T(mfxU16 , PitchLow ) + FIELD_T(mfxU8 * , Y ) + FIELD_T(mfxU16* , Y16 ) + FIELD_T(mfxU8 * , R ) + FIELD_T(mfxU8 * , UV ) + FIELD_T(mfxU8 * , VU ) + FIELD_T(mfxU8 * , CbCr ) + FIELD_T(mfxU8 * , CrCb ) + FIELD_T(mfxU8 * , Cb ) + FIELD_T(mfxU8 * , U ) + FIELD_T(mfxU16* , U16 ) + FIELD_T(mfxU8 * , G ) + FIELD_T(mfxU8 * , Cr ) + FIELD_T(mfxU8 * , V ) + FIELD_T(mfxU16* , V16 ) + FIELD_T(mfxU8 * , B ) + FIELD_T(mfxU8 * , A ) + FIELD_T(mfxMemId, MemId ) + FIELD_T(mfxU16 , Corrupted ) + FIELD_T(mfxU16 , DataFlag ) +) + +STRUCT(mfxFrameSurface1, + FIELD_S(mfxFrameInfo, Info) + FIELD_S(mfxFrameData, Data) +) + +STRUCT(mfxInfoMFX, + FIELD_T(mfxU16, LowPower ) + FIELD_T(mfxU16, BRCParamMultiplier) + FIELD_S(mfxFrameInfo, FrameInfo ) + FIELD_T(mfx4CC, CodecId ) + FIELD_T(mfxU16, CodecProfile ) + FIELD_T(mfxU16, CodecLevel ) + FIELD_T(mfxU16, NumThread ) + FIELD_T(mfxU16, TargetUsage ) + FIELD_T(mfxU16, GopPicSize ) + FIELD_T(mfxU16, GopRefDist ) + FIELD_T(mfxU16, GopOptFlag ) + FIELD_T(mfxU16, IdrInterval ) + FIELD_T(mfxU16, RateControlMethod ) + FIELD_T(mfxU16, InitialDelayInKB ) + FIELD_T(mfxU16, QPI ) + FIELD_T(mfxU16, Accuracy ) + FIELD_T(mfxU16, BufferSizeInKB ) + FIELD_T(mfxU16, TargetKbps ) + FIELD_T(mfxU16, QPP ) + FIELD_T(mfxU16, ICQQuality ) + FIELD_T(mfxU16, MaxKbps ) + FIELD_T(mfxU16, QPB ) + FIELD_T(mfxU16, Convergence ) + FIELD_T(mfxU16, NumSlice ) + FIELD_T(mfxU16, NumRefFrame ) + FIELD_T(mfxU16, EncodedOrder ) + FIELD_T(mfxU16, DecodedOrder ) + FIELD_T(mfxU16, MaxDecFrameBuffering ) + FIELD_T(mfxU16, ExtendedPicStruct ) + FIELD_T(mfxU16, TimeStampCalc ) + FIELD_T(mfxU16, SliceGroupsPresent) + FIELD_T(mfxU16, JPEGChromaFormat ) + FIELD_T(mfxU16, Rotation ) + FIELD_T(mfxU16, JPEGColorFormat ) + FIELD_T(mfxU16, InterleavedDec ) + FIELD_T(mfxU16, Interleaved ) + FIELD_T(mfxU16, Quality ) + FIELD_T(mfxU16, RestartInterval ) +) + +STRUCT(mfxInfoVPP, + FIELD_S(mfxFrameInfo, In) + FIELD_S(mfxFrameInfo, Out) +) + +STRUCT(mfxVideoParam, + FIELD_S(mfxInfoMFX, mfx) + FIELD_S(mfxInfoVPP, vpp) + FIELD_T(mfxU16 , AsyncDepth ) + FIELD_T(mfxU16 , Protected ) + FIELD_T(mfxU16 , IOPattern ) + FIELD_T(mfxExtBuffer**, ExtParam ) + FIELD_T(mfxU16 , NumExtParam ) +) + +STRUCT(mfxExtCodingOption, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16 , reserved1 ) + FIELD_T(mfxU16 , RateDistortionOpt ) + FIELD_T(mfxU16 , MECostType ) + FIELD_T(mfxU16 , MESearchType ) + FIELD_S(mfxI16Pair , MVSearchWindow ) + FIELD_T(mfxU16 , EndOfSequence ) + FIELD_T(mfxU16 , FramePicture ) + FIELD_T(mfxU16 , CAVLC ) + FIELD_T(mfxU16 , RecoveryPointSEI ) + FIELD_T(mfxU16 , ViewOutput ) + FIELD_T(mfxU16 , NalHrdConformance ) + FIELD_T(mfxU16 , SingleSeiNalUnit ) + FIELD_T(mfxU16 , VuiVclHrdParameters ) + FIELD_T(mfxU16 , RefPicListReordering ) + FIELD_T(mfxU16 , ResetRefList ) + FIELD_T(mfxU16 , RefPicMarkRep ) + FIELD_T(mfxU16 , FieldOutput ) + FIELD_T(mfxU16 , IntraPredBlockSize ) + FIELD_T(mfxU16 , InterPredBlockSize ) + FIELD_T(mfxU16 , MVPrecision ) + FIELD_T(mfxU16 , MaxDecFrameBuffering ) + FIELD_T(mfxU16 , AUDelimiter ) + FIELD_T(mfxU16 , EndOfStream ) + FIELD_T(mfxU16 , PicTimingSEI ) + FIELD_T(mfxU16 , VuiNalHrdParameters ) +) + +STRUCT(mfxExtCodingOption2, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16 , IntRefType ) + FIELD_T(mfxU16 , IntRefCycleSize ) + FIELD_T(mfxI16 , IntRefQPDelta ) + FIELD_T(mfxU32 , MaxFrameSize ) + FIELD_T(mfxU32 , MaxSliceSize ) + FIELD_T(mfxU16 , BitrateLimit ) + FIELD_T(mfxU16 , MBBRC ) + FIELD_T(mfxU16 , ExtBRC ) + FIELD_T(mfxU16 , LookAheadDepth ) + FIELD_T(mfxU16 , Trellis ) + FIELD_T(mfxU16 , RepeatPPS ) + FIELD_T(mfxU16 , BRefType ) + FIELD_T(mfxU16 , AdaptiveI ) + FIELD_T(mfxU16 , AdaptiveB ) + FIELD_T(mfxU16 , LookAheadDS ) + FIELD_T(mfxU16 , NumMbPerSlice ) + FIELD_T(mfxU16 , SkipFrame ) + FIELD_T(mfxU8 , MinQPI ) + FIELD_T(mfxU8 , MaxQPI ) + FIELD_T(mfxU8 , MinQPP ) + FIELD_T(mfxU8 , MaxQPP ) + FIELD_T(mfxU8 , MinQPB ) + FIELD_T(mfxU8 , MaxQPB ) + FIELD_T(mfxU16 , FixedFrameRate ) + FIELD_T(mfxU16 , DisableDeblockingIdc ) + FIELD_T(mfxU16 , DisableVUI ) + FIELD_T(mfxU16 , BufferingPeriodSEI) + FIELD_T(mfxU16 , EnableMAD ) + FIELD_T(mfxU16 , UseRawRef ) +) + +STRUCT(mfxExtVPPDoNotUse, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32 , NumAlg) + FIELD_T(mfxU32*, AlgList) +) + +STRUCT(mfxExtVPPDenoise, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, DenoiseFactor) +) + +STRUCT(mfxExtVPPDetail, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, DetailFactor) +) + +STRUCT(mfxExtVPPProcAmp, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxF64, Brightness ) + FIELD_T(mfxF64, Contrast ) + FIELD_T(mfxF64, Hue ) + FIELD_T(mfxF64, Saturation ) +) + +STRUCT(mfxEncodeStat, + FIELD_T(mfxU32, NumFrame ) + FIELD_T(mfxU64, NumBit ) + FIELD_T(mfxU32, NumCachedFrame ) +) + +STRUCT(mfxDecodeStat, + FIELD_T(mfxU32, NumFrame ) + FIELD_T(mfxU32, NumSkippedFrame ) + FIELD_T(mfxU32, NumError ) + FIELD_T(mfxU32, NumCachedFrame ) +) + +STRUCT(mfxVPPStat, + FIELD_T(mfxU32, NumFrame ) + FIELD_T(mfxU32, NumCachedFrame ) +) + +STRUCT(mfxExtVppAuxData, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, PicStruct ) + FIELD_T(mfxU16, SceneChangeRate ) + FIELD_T(mfxU16, RepeatedFrame ) +) + +STRUCT(mfxPayload, + FIELD_T(mfxU8*, Data ) + FIELD_T(mfxU32, NumBit ) + FIELD_T(mfxU16, Type ) + FIELD_T(mfxU16, BufSize ) +) + +STRUCT(mfxEncodeCtrl, + FIELD_S(mfxExtBuffer , Header ) + FIELD_T(mfxU16 , MfxNalUnitType) + FIELD_T(mfxU16 , SkipFrame ) + FIELD_T(mfxU16 , QP ) + FIELD_T(mfxU16 , FrameType ) + FIELD_T(mfxU16 , NumExtParam) + FIELD_T(mfxU16 , NumPayload ) + FIELD_T(mfxExtBuffer**, ExtParam ) + FIELD_T(mfxPayload** , Payload ) +) + +STRUCT(mfxFrameAllocRequest, + FIELD_S(mfxFrameInfo, Info) + FIELD_T(mfxU16, Type ) + FIELD_T(mfxU16, NumFrameMin ) + FIELD_T(mfxU16, NumFrameSuggested ) + ) + +STRUCT(mfxFrameAllocResponse, + FIELD_T(mfxMemId*, mids ) + FIELD_T(mfxU16 , NumFrameActual ) +) + +STRUCT(mfxExtCodingOptionSPSPPS, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU8* , SPSBuffer ) + FIELD_T(mfxU8* , PPSBuffer ) + FIELD_T(mfxU16 , SPSBufSize) + FIELD_T(mfxU16 , PPSBufSize) + FIELD_T(mfxU16 , SPSId ) + FIELD_T(mfxU16 , PPSId ) +) + +STRUCT(mfxExtVideoSignalInfo, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16, VideoFormat ) + FIELD_T(mfxU16, VideoFullRange ) + FIELD_T(mfxU16, ColourDescriptionPresent) + FIELD_T(mfxU16, ColourPrimaries ) + FIELD_T(mfxU16, TransferCharacteristics ) + FIELD_T(mfxU16, MatrixCoefficients ) +) + +STRUCT(mfxExtVPPDoUse, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32 , NumAlg) + FIELD_T(mfxU32*, AlgList) +) + +STRUCT(mfxExtOpaqueSurfaceAlloc_InOut, + FIELD_T(mfxFrameSurface1 ** , Surfaces ) + FIELD_T(mfxU16 , Type ) + FIELD_T(mfxU16 , NumSurface) +) + +STRUCT(mfxExtOpaqueSurfaceAlloc, + FIELD_S(mfxExtBuffer, Header) + FIELD_S(mfxExtOpaqueSurfaceAlloc_InOut, In) + FIELD_S(mfxExtOpaqueSurfaceAlloc_InOut, Out) +) + +STRUCT(mfxExtAVCRefListCtrl_Entry, + FIELD_T(mfxU32, FrameOrder ) + FIELD_T(mfxU16, PicStruct ) + FIELD_T(mfxU16, ViewId ) + FIELD_T(mfxU16, LongTermIdx ) +) + +STRUCT(mfxExtAVCRefListCtrl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, NumRefIdxL0Active) + FIELD_T(mfxU16, NumRefIdxL1Active) + FIELD_S(mfxExtAVCRefListCtrl_Entry, PreferredRefList) + FIELD_S(mfxExtAVCRefListCtrl_Entry, RejectedRefList ) + FIELD_S(mfxExtAVCRefListCtrl_Entry, LongTermRefList ) + FIELD_T(mfxU16, ApplyLongTermIdx ) +) + +STRUCT(mfxExtVPPFrameRateConversion, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, Algorithm) +) + +STRUCT(mfxExtVPPImageStab, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, Mode) +) + +STRUCT(mfxExtPictureTimingSEI_TimeStamp, + FIELD_T(mfxU16, ClockTimestampFlag) + FIELD_T(mfxU16, CtType ) + FIELD_T(mfxU16, NuitFieldBasedFlag) + FIELD_T(mfxU16, CountingType ) + FIELD_T(mfxU16, FullTimestampFlag ) + FIELD_T(mfxU16, DiscontinuityFlag ) + FIELD_T(mfxU16, CntDroppedFlag ) + FIELD_T(mfxU16, NFrames ) + FIELD_T(mfxU16, SecondsFlag ) + FIELD_T(mfxU16, MinutesFlag ) + FIELD_T(mfxU16, HoursFlag ) + FIELD_T(mfxU16, SecondsValue ) + FIELD_T(mfxU16, MinutesValue ) + FIELD_T(mfxU16, HoursValue ) + FIELD_T(mfxU32, TimeOffset ) +) + +STRUCT(mfxExtPictureTimingSEI, + FIELD_S(mfxExtBuffer, Header) + FIELD_S(mfxExtPictureTimingSEI_TimeStamp, TimeStamp) +) + +STRUCT(mfxExtAvcTemporalLayers_Layer, + FIELD_T(mfxU16, Scale) +) + +STRUCT(mfxExtAvcTemporalLayers, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, BaseLayerPID) + FIELD_S(mfxExtAvcTemporalLayers_Layer, Layer) +) + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + +STRUCT(mfxExtEncoderCapability, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, MBPerSec) + FIELD_T(mfxU16, InputMemoryTiling) +) + +#else + +STRUCT(mfxExtEncoderCapability, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, MBPerSec) +) + +#endif + +STRUCT(mfxExtEncoderResetOption, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, StartNewSequence) +) + +STRUCT(mfxExtAVCEncodedFrameInfo_RefList, + FIELD_T(mfxU32, FrameOrder ) + FIELD_T(mfxU16, PicStruct ) + FIELD_T(mfxU16, LongTermIdx ) +) + +STRUCT(mfxExtAVCEncodedFrameInfo, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, FrameOrder ) + FIELD_T(mfxU16, PicStruct ) + FIELD_T(mfxU16, LongTermIdx ) + FIELD_T(mfxU32, MAD ) + FIELD_T(mfxU16, BRCPanicMode) + FIELD_T(mfxU16, QP ) + FIELD_T(mfxU32, SecondFieldOffset ) + FIELD_S(mfxExtAVCEncodedFrameInfo_RefList, UsedRefListL0) + FIELD_S(mfxExtAVCEncodedFrameInfo_RefList, UsedRefListL1) +) + +STRUCT(mfxVPPCompInputStream, + FIELD_T(mfxU32, DstX ) + FIELD_T(mfxU32, DstY ) + FIELD_T(mfxU32, DstW ) + FIELD_T(mfxU32, DstH ) + FIELD_T(mfxU16, LumaKeyEnable ) + FIELD_T(mfxU16, LumaKeyMin ) + FIELD_T(mfxU16, LumaKeyMax ) + FIELD_T(mfxU16, GlobalAlphaEnable) + FIELD_T(mfxU16, GlobalAlpha ) + FIELD_T(mfxU16, PixelAlphaEnable ) + FIELD_T(mfxU16, TileId ) +) + +STRUCT(mfxExtVPPComposite, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, Y) + FIELD_T(mfxU16, U) + FIELD_T(mfxU16, V) + FIELD_T(mfxU16, R) + FIELD_T(mfxU16, G) + FIELD_T(mfxU16, B) + FIELD_T(mfxU16, NumTiles) + FIELD_T(mfxU16, NumInputStream) + FIELD_T(mfxVPPCompInputStream*, InputStream) +) + +STRUCT(mfxExtColorConversion, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, ChromaSiting) +) + +STRUCT(mfxExtVPPVideoSignalInfo_InOut, + FIELD_T(mfxU16, TransferMatrix ) + FIELD_T(mfxU16, NominalRange ) +) + +STRUCT(mfxExtVPPVideoSignalInfo, + FIELD_S(mfxExtBuffer, Header) + FIELD_S(mfxExtVPPVideoSignalInfo_InOut, In ) + FIELD_S(mfxExtVPPVideoSignalInfo_InOut, Out) +) + +STRUCT(mfxExtEncoderROI_Entry, + FIELD_T(mfxU32, Left ) + FIELD_T(mfxU32, Top ) + FIELD_T(mfxU32, Right ) + FIELD_T(mfxU32, Bottom ) + FIELD_T(mfxI16, Priority) +) + +STRUCT(mfxExtEncoderROI, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxI16, NumROI) + FIELD_T(mfxU16, ROIMode) + FIELD_S(mfxExtEncoderROI_Entry, ROI) +) + +STRUCT(mfxExtVPPDeinterlacing, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, Mode) + FIELD_T(mfxU16, TelecinePattern) + FIELD_T(mfxU16, TelecineLocation) +) + +STRUCT(mfxExtVP8CodingOption, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, Version) + FIELD_T(mfxU16, EnableMultipleSegments) + FIELD_T(mfxU16, LoopFilterType) +) + +STRUCT(mfxPluginUID, + FIELD_T(mfxU8, Data) +) + +STRUCT(mfxExtFeiParam, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxFeiFunction, Func) + FIELD_T(mfxU16, SingleFieldProcessing) +) + +STRUCT(mfxExtFeiSPS, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, SPSId) + FIELD_T(mfxU16, PicOrderCntType) + FIELD_T(mfxU16, Log2MaxPicOrderCntLsb) +) + +#if MFX_VERSION >= 1023 +STRUCT(mfxExtFeiPPS_mfxExtFeiPpsDPB, + FIELD_T(mfxU16, Index) + FIELD_T(mfxU16, PicType) + FIELD_T(mfxI32, FrameNumWrap) + FIELD_T(mfxU16, LongTermFrameIdx) +) + +STRUCT(mfxExtFeiPPS, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, SPSId) + FIELD_T(mfxU16, PPSId) + FIELD_T(mfxU16, PictureType) + FIELD_T(mfxU16, FrameType) + FIELD_T(mfxU16, PicInitQP) + FIELD_T(mfxU16, NumRefIdxL0Active) + FIELD_T(mfxU16, NumRefIdxL1Active) + FIELD_T(mfxI16, ChromaQPIndexOffset) + FIELD_T(mfxI16, SecondChromaQPIndexOffset) + FIELD_T(mfxU16, Transform8x8ModeFlag) + FIELD_S(mfxExtFeiPPS_mfxExtFeiPpsDPB, DpbBefore) + FIELD_S(mfxExtFeiPPS_mfxExtFeiPpsDPB, DpbAfter) +) + +#else +STRUCT(mfxExtFeiPPS, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, SPSId) + FIELD_T(mfxU16, PPSId) + FIELD_T(mfxU16, PictureType) + FIELD_T(mfxU16, PicInitQP) + FIELD_T(mfxU16, NumRefIdxL0Active) + FIELD_T(mfxU16, NumRefIdxL1Active) + FIELD_T(mfxU16, ReferenceFrames) + FIELD_T(mfxI16, ChromaQPIndexOffset) + FIELD_T(mfxI16, SecondChromaQPIndexOffset) + FIELD_T(mfxU16, Transform8x8ModeFlag) +) +#endif //MFX_VERSION >= 1023 + +STRUCT(mfxExtFeiPreEncCtrl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, Qp) + FIELD_T(mfxU16, LenSP) + FIELD_T(mfxU16, SearchPath) + FIELD_T(mfxU16, SubMBPartMask) + FIELD_T(mfxU16, SubPelMode) + FIELD_T(mfxU16, InterSAD) + FIELD_T(mfxU16, IntraSAD) + FIELD_T(mfxU16, AdaptiveSearch) + FIELD_T(mfxU16, MVPredictor) + FIELD_T(mfxU16, MBQp) + FIELD_T(mfxU16, FTEnable) + FIELD_T(mfxU16, IntraPartMask) + FIELD_T(mfxU16, RefWidth) + FIELD_T(mfxU16, RefHeight) + FIELD_T(mfxU16, SearchWindow) + FIELD_T(mfxU16, DisableMVOutput) + FIELD_T(mfxU16, DisableStatisticsOutput) + FIELD_T(mfxU16, Enable8x8Stat) + FIELD_T(mfxU16, PictureType) + FIELD_T(mfxU16, DownsampleInput) +) + +STRUCT(mfxExtFeiPreEncMVPredictors, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, NumMBAlloc) + FIELD_T(mfxExtFeiPreEncMVPredictors_MB*, MB) +) + +STRUCT(mfxExtFeiPreEncMV, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, NumMBAlloc) + FIELD_T(mfxExtFeiPreEncMV_MB*, MB) +) + +STRUCT(mfxExtFeiPreEncMBStat, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, NumMBAlloc) + FIELD_T(mfxExtFeiPreEncMBStat_MB*, MB) +) + +STRUCT(mfxExtFeiEncFrameCtrl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, SearchPath) + FIELD_T(mfxU16, LenSP) + FIELD_T(mfxU16, SubMBPartMask) + FIELD_T(mfxU16, IntraPartMask) + FIELD_T(mfxU16, MultiPredL0) + FIELD_T(mfxU16, MultiPredL1) + FIELD_T(mfxU16, SubPelMode) + FIELD_T(mfxU16, InterSAD) + FIELD_T(mfxU16, IntraSAD) + FIELD_T(mfxU16, DistortionType) + FIELD_T(mfxU16, RepartitionCheckEnable) + FIELD_T(mfxU16, AdaptiveSearch) + FIELD_T(mfxU16, MVPredictor) + FIELD_T(mfxU16, NumMVPredictors) + FIELD_T(mfxU16, PerMBQp) + FIELD_T(mfxU16, PerMBInput) + FIELD_T(mfxU16, MBSizeCtrl) + FIELD_T(mfxU16, RefWidth) + FIELD_T(mfxU16, RefHeight) + FIELD_T(mfxU16, SearchWindow) +) + +STRUCT(mfxExtFeiEncMVPredictors, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, NumMBAlloc) +) + +STRUCT(mfxExtFeiEncMV_MB, + FIELD_S(mfxI16Pair, MV) +) + +STRUCT(mfxExtFeiEncMV, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, NumMBAlloc) + FIELD_T(mfxExtFeiEncMV_MB*, MB) +) + +STRUCT(mfxExtFeiEncMBCtrl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, NumMBAlloc) + FIELD_T(mfxExtFeiEncMBCtrl_MB*, MB) +) +STRUCT(mfxExtFeiEncMBStat, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, NumMBAlloc) +) + +STRUCT(mfxExtFeiEncQP, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, NumMBAlloc) +) + +STRUCT(mfxExtFeiPakMBCtrl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, NumMBAlloc) + FIELD_T(mfxFeiPakMBCtrl*, MB) +) + + +STRUCT(mfxExtFeiSliceHeader, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, NumSlice) +) + +STRUCT(mfxExtFeiRepackCtrl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, MaxFrameSize) + FIELD_T(mfxU32, NumPasses) +) + +STRUCT(mfxExtFeiDecStreamOut, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, NumMBAlloc) + FIELD_T(mfxU16, RemapRefIdx) + FIELD_T(mfxU16, PicStruct) + FIELD_T(mfxFeiDecStreamOutMBCtrl*, MB) +) + +#if (MFX_VERSION >= 1027) +STRUCT(mfxExtFeiHevcEncFrameCtrl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, SearchPath) + FIELD_T(mfxU16, LenSP) + FIELD_T(mfxU16, RefWidth) + FIELD_T(mfxU16, RefHeight) + FIELD_T(mfxU16, SearchWindow) + FIELD_T(mfxU16, NumMvPredictors) + FIELD_T(mfxU16, MultiPred) + FIELD_T(mfxU16, SubPelMode) + FIELD_T(mfxU16, AdaptiveSearch) + FIELD_T(mfxU16, MVPredictor) + FIELD_T(mfxU16, PerCuQp) + FIELD_T(mfxU16, PerCtuInput) + FIELD_T(mfxU16, ForceCtuSplit) + FIELD_T(mfxU16, NumFramePartitions) + FIELD_T(mfxU16, FastIntraMode) +) + +STRUCT(mfxExtFeiHevcEncMVPredictors, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, VaBufferID) + FIELD_T(mfxU32, Pitch) + FIELD_T(mfxU32, Height) +) + +STRUCT(mfxExtFeiHevcEncCtuCtrl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, VaBufferID) + FIELD_T(mfxU32, Pitch) + FIELD_T(mfxU32, Height) +) + +STRUCT(mfxExtFeiHevcEncQP, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, VaBufferID) + FIELD_T(mfxU32, Pitch) + FIELD_T(mfxU32, Height) + FIELD_T(mfxU8*, Data) +) +#endif + +STRUCT(mfxExtCamGammaCorrection, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16, Mode ) + FIELD_T(mfxU16, reserved1 ) + FIELD_T(mfxF64, GammaValue ) + FIELD_T(mfxU16, reserved2 ) + FIELD_T(mfxU16, NumPoints ) + FIELD_T(mfxU16, GammaPoint ) + FIELD_T(mfxU16, GammaCorrected ) +) +STRUCT(mfxExtCamWhiteBalance, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU32, Mode ) + FIELD_T(mfxF64, R ) + FIELD_T(mfxF64, G0 ) + FIELD_T(mfxF64, B ) + FIELD_T(mfxF64, G1 ) + FIELD_T(mfxU32, reserved) /* Fixed size array */ +) +STRUCT(mfxExtCamHotPixelRemoval, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, PixelThresholdDifference ) + FIELD_T(mfxU16, PixelCountThreshold ) +) +STRUCT(mfxCamVignetteCorrectionElement, + FIELD_T(mfxU8, integer ) + FIELD_T(mfxU8, mantissa) +) +STRUCT(mfxExtCamBlackLevelCorrection, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16, R ) + FIELD_T(mfxU16, G0 ) + FIELD_T(mfxU16, B ) + FIELD_T(mfxU16, G1 ) + FIELD_T(mfxU32, reserved ) /* Fixed size array */ +) + STRUCT(mfxExtCamTotalColorControl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU8, R) + FIELD_T(mfxU8, G) + FIELD_T(mfxU8, B) + FIELD_T(mfxU8, C) + FIELD_T(mfxU8, M) + FIELD_T(mfxU8, Y) +) + +STRUCT(mfxExtCamCscYuvRgb, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxF32, PreOffset) + FIELD_T(mfxF32, Matrix) + FIELD_T(mfxF32, PostOffset) + FIELD_T(mfxU16, reserved) +) + +STRUCT(mfxCamVignetteCorrectionParam, + FIELD_S(mfxCamVignetteCorrectionElement, R ) + FIELD_S(mfxCamVignetteCorrectionElement, G0) + FIELD_S(mfxCamVignetteCorrectionElement, B ) + FIELD_S(mfxCamVignetteCorrectionElement, G1) +) +STRUCT(mfxExtCamVignetteCorrection, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32 , Width ) + FIELD_T(mfxU32 , Height) + FIELD_T(mfxU32 , Pitch ) +) +STRUCT(mfxExtCamBayerDenoise, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16, Threshold) +) +STRUCT(mfxExtCamColorCorrection3x3, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxF64, CCM ) +) +STRUCT(mfxExtCamPadding, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, Top ) + FIELD_T(mfxU16, Bottom) + FIELD_T(mfxU16, Left ) + FIELD_T(mfxU16, Right ) +) +STRUCT(mfxExtCamPipeControl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, RawFormat ) +) + +STRUCT(mfxExtAVCRefLists_mfxRefPic, + FIELD_T(mfxU32, FrameOrder) + FIELD_T(mfxU16, PicStruct ) +) + +STRUCT(mfxExtAVCRefLists, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16 , NumRefIdxL0Active) + FIELD_T(mfxU16 , NumRefIdxL1Active) + FIELD_S(mfxExtAVCRefLists_mfxRefPic, RefPicList0) + FIELD_S(mfxExtAVCRefLists_mfxRefPic, RefPicList1) +) + +#if (MFX_VERSION >= MFX_VERSION_NEXT) +STRUCT(mfxExtCodingOption3, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, NumSliceI) + FIELD_T(mfxU16, NumSliceP) + FIELD_T(mfxU16, NumSliceB) + FIELD_T(mfxU16, WinBRCMaxAvgKbps) + FIELD_T(mfxU16, WinBRCSize) + FIELD_T(mfxU16, QVBRQuality) + FIELD_T(mfxU16, EnableMBQP) + FIELD_T(mfxU16, IntRefCycleDist) + FIELD_T(mfxU16, DirectBiasAdjustment) + FIELD_T(mfxU16, GlobalMotionBiasAdjustment)/* tri-state option */ + FIELD_T(mfxU16, MVCostScalingFactor) + FIELD_T(mfxU16, MBDisableSkipMap)/* tri-state option */ + FIELD_T(mfxU16, WeightedPred) + FIELD_T(mfxU16, WeightedBiPred) + FIELD_T(mfxU16, AspectRatioInfoPresent) /* tri-state option */ + FIELD_T(mfxU16, OverscanInfoPresent) /* tri-state option */ + FIELD_T(mfxU16, OverscanAppropriate) /* tri-state option */ + FIELD_T(mfxU16, TimingInfoPresent) /* tri-state option */ + FIELD_T(mfxU16, BitstreamRestriction) /* tri-state option */ + FIELD_T(mfxU16, LowDelayHrd) /* tri-state option */ + FIELD_T(mfxU16, MotionVectorsOverPicBoundaries) /* tri-state option */ + FIELD_T(mfxU16, Log2MaxMvLengthHorizontal) /* 0..16 */ + FIELD_T(mfxU16, Log2MaxMvLengthVertical) /* 0..16 */ + FIELD_T(mfxU16, ScenarioInfo) + FIELD_T(mfxU16, ContentInfo) + FIELD_T(mfxU16, PRefType) + FIELD_T(mfxU16, FadeDetection) /* tri-state option */ + FIELD_T(mfxI16, DeblockingAlphaTcOffset) /* -12..12 (slice_alpha_c0_offset_div2 << 1) */ + FIELD_T(mfxI16, DeblockingBetaOffset) /* -12..12 (slice_beta_offset_div2 << 1) */ + FIELD_T(mfxU16, GPB) + FIELD_T(mfxU32, MaxFrameSizeI) + FIELD_T(mfxU32, MaxFrameSizeP) + FIELD_T(mfxU16, EnableQPOffset) + FIELD_T(mfxI16, QPOffset) + FIELD_T(mfxU16, NumRefActiveP) + FIELD_T(mfxU16, NumRefActiveBL0) + FIELD_T(mfxU16, NumRefActiveBL1) + FIELD_T(mfxU16, TransformSkip) + FIELD_T(mfxU16, TargetChromaFormatPlus1) + FIELD_T(mfxU16, TargetBitDepthLuma) + FIELD_T(mfxU16, TargetBitDepthChroma) + FIELD_T(mfxU16, BRCPanicMode) + FIELD_T(mfxU16, LowDelayBRC) + FIELD_T(mfxU16, EnableMBForceIntra) + FIELD_T(mfxU16, AdaptiveMaxFrameSize) + FIELD_T(mfxU16, RepartitionCheckEnable) /* tri-state option */ + FIELD_T(mfxU16, QuantScaleType) + FIELD_T(mfxU16, IntraVLCFormat) + FIELD_T(mfxU16, ScanType) + FIELD_T(mfxU16, EncodedUnitsInfo) + FIELD_T(mfxU16, EnableNalUnitType) + FIELD_T(mfxU16, ExtBrcAdaptiveLTR) /* tri-state option for ExtBrcAdaptiveLTR */ +) +#elif (MFX_VERSION >= 1027) +STRUCT(mfxExtCodingOption3, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, NumSliceI) + FIELD_T(mfxU16, NumSliceP) + FIELD_T(mfxU16, NumSliceB) + FIELD_T(mfxU16, WinBRCMaxAvgKbps) + FIELD_T(mfxU16, WinBRCSize) + FIELD_T(mfxU16, QVBRQuality) + FIELD_T(mfxU16, EnableMBQP) + FIELD_T(mfxU16, IntRefCycleDist) + FIELD_T(mfxU16, DirectBiasAdjustment) + FIELD_T(mfxU16, GlobalMotionBiasAdjustment)/* tri-state option */ + FIELD_T(mfxU16, MVCostScalingFactor) + FIELD_T(mfxU16, MBDisableSkipMap)/* tri-state option */ + FIELD_T(mfxU16, WeightedPred) + FIELD_T(mfxU16, WeightedBiPred) + FIELD_T(mfxU16, AspectRatioInfoPresent) /* tri-state option */ + FIELD_T(mfxU16, OverscanInfoPresent) /* tri-state option */ + FIELD_T(mfxU16, OverscanAppropriate) /* tri-state option */ + FIELD_T(mfxU16, TimingInfoPresent) /* tri-state option */ + FIELD_T(mfxU16, BitstreamRestriction) /* tri-state option */ + FIELD_T(mfxU16, LowDelayHrd) /* tri-state option */ + FIELD_T(mfxU16, MotionVectorsOverPicBoundaries) /* tri-state option */ + FIELD_T(mfxU16, ScenarioInfo) + FIELD_T(mfxU16, ContentInfo) + FIELD_T(mfxU16, PRefType) + FIELD_T(mfxU16, FadeDetection) /* tri-state option */ + FIELD_T(mfxU16, GPB) + FIELD_T(mfxU32, MaxFrameSizeI) + FIELD_T(mfxU32, MaxFrameSizeP) + FIELD_T(mfxU16, EnableQPOffset) + FIELD_T(mfxI16, QPOffset) + FIELD_T(mfxU16, NumRefActiveP) + FIELD_T(mfxU16, NumRefActiveBL0) + FIELD_T(mfxU16, NumRefActiveBL1) + FIELD_T(mfxU16, TransformSkip) + FIELD_T(mfxU16, TargetChromaFormatPlus1) + FIELD_T(mfxU16, TargetBitDepthLuma) + FIELD_T(mfxU16, TargetBitDepthChroma) + FIELD_T(mfxU16, BRCPanicMode) + FIELD_T(mfxU16, LowDelayBRC) + FIELD_T(mfxU16, EnableMBForceIntra) + FIELD_T(mfxU16, AdaptiveMaxFrameSize) + FIELD_T(mfxU16, RepartitionCheckEnable) /* tri-state option */ + FIELD_T(mfxU16, EncodedUnitsInfo) + FIELD_T(mfxU16, EnableNalUnitType) + FIELD_T(mfxU16, ExtBrcAdaptiveLTR) +) + +#elif (MFX_VERSION >= 1026) +STRUCT(mfxExtCodingOption3, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16 , NumSliceI ) + FIELD_T(mfxU16 , NumSliceP ) + FIELD_T(mfxU16 , NumSliceB ) + FIELD_T(mfxU16 , WinBRCMaxAvgKbps ) + FIELD_T(mfxU16 , WinBRCSize ) + FIELD_T(mfxU16 , QVBRQuality ) + FIELD_T(mfxU16 , EnableMBQP ) + FIELD_T(mfxU16 , IntRefCycleDist ) + FIELD_T(mfxU16 , DirectBiasAdjustment ) + FIELD_T(mfxU16 , GlobalMotionBiasAdjustment )/* tri-state option */ + FIELD_T(mfxU16 , MVCostScalingFactor ) + FIELD_T(mfxU16 , MBDisableSkipMap )/* tri-state option */ + FIELD_T(mfxU16 , WeightedPred ) + FIELD_T(mfxU16 , WeightedBiPred ) + FIELD_T(mfxU16 , AspectRatioInfoPresent ) /* tri-state option */ + FIELD_T(mfxU16 , OverscanInfoPresent ) /* tri-state option */ + FIELD_T(mfxU16 , OverscanAppropriate ) /* tri-state option */ + FIELD_T(mfxU16 , TimingInfoPresent ) /* tri-state option */ + FIELD_T(mfxU16 , BitstreamRestriction ) /* tri-state option */ + FIELD_T(mfxU16 , LowDelayHrd ) /* tri-state option */ + FIELD_T(mfxU16 , MotionVectorsOverPicBoundaries) /* tri-state option */ + FIELD_T(mfxU16 , ScenarioInfo ) + FIELD_T(mfxU16 , ContentInfo ) + FIELD_T(mfxU16 , PRefType ) + FIELD_T(mfxU16 , FadeDetection ) /* tri-state option */ + FIELD_T(mfxU16 , GPB ) + FIELD_T(mfxU32 , MaxFrameSizeI ) + FIELD_T(mfxU32 , MaxFrameSizeP ) + FIELD_T(mfxU16 , EnableQPOffset ) + FIELD_T(mfxI16 , QPOffset ) + FIELD_T(mfxU16 , NumRefActiveP ) + FIELD_T(mfxU16 , NumRefActiveBL0 ) + FIELD_T(mfxU16 , NumRefActiveBL1 ) + FIELD_T(mfxU16 , TransformSkip) + FIELD_T(mfxU16 , BRCPanicMode ) + FIELD_T(mfxU16 , LowDelayBRC ) + FIELD_T(mfxU16 , EnableMBForceIntra ) + FIELD_T(mfxU16 , AdaptiveMaxFrameSize ) + FIELD_T(mfxU16 , RepartitionCheckEnable ) /* tri-state option */ + FIELD_T(mfxU16 , EncodedUnitsInfo ) + FIELD_T(mfxU16 , EnableNalUnitType ) + FIELD_T(mfxU16 , ExtBrcAdaptiveLTR) +) +#else +STRUCT(mfxExtCodingOption3, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16 , NumSliceI ) + FIELD_T(mfxU16 , NumSliceP ) + FIELD_T(mfxU16 , NumSliceB ) + FIELD_T(mfxU16 , WinBRCMaxAvgKbps ) + FIELD_T(mfxU16 , WinBRCSize ) + FIELD_T(mfxU16 , QVBRQuality ) + FIELD_T(mfxU16 , EnableMBQP ) + FIELD_T(mfxU16 , IntRefCycleDist ) + FIELD_T(mfxU16 , DirectBiasAdjustment ) + FIELD_T(mfxU16 , GlobalMotionBiasAdjustment )/* tri-state option */ + FIELD_T(mfxU16 , MVCostScalingFactor ) + FIELD_T(mfxU16 , MBDisableSkipMap )/* tri-state option */ + FIELD_T(mfxU16 , WeightedPred ) + FIELD_T(mfxU16 , WeightedBiPred ) + FIELD_T(mfxU16 , AspectRatioInfoPresent ) /* tri-state option */ + FIELD_T(mfxU16 , OverscanInfoPresent ) /* tri-state option */ + FIELD_T(mfxU16 , OverscanAppropriate ) /* tri-state option */ + FIELD_T(mfxU16 , TimingInfoPresent ) /* tri-state option */ + FIELD_T(mfxU16 , BitstreamRestriction ) /* tri-state option */ + FIELD_T(mfxU16 , LowDelayHrd ) /* tri-state option */ + FIELD_T(mfxU16 , MotionVectorsOverPicBoundaries) /* tri-state option */ + FIELD_T(mfxU16 , ScenarioInfo ) + FIELD_T(mfxU16 , ContentInfo ) + FIELD_T(mfxU16 , PRefType ) + FIELD_T(mfxU16 , FadeDetection ) /* tri-state option */ + FIELD_T(mfxU16 , GPB ) + FIELD_T(mfxU32 , MaxFrameSizeI ) + FIELD_T(mfxU32 , MaxFrameSizeP ) + FIELD_T(mfxU16 , EnableQPOffset ) + FIELD_T(mfxI16 , QPOffset ) + FIELD_T(mfxU16 , NumRefActiveP ) + FIELD_T(mfxU16 , NumRefActiveBL0 ) + FIELD_T(mfxU16 , NumRefActiveBL1 ) + FIELD_T(mfxU16 , BRCPanicMode ) + FIELD_T(mfxU16 , LowDelayBRC ) + FIELD_T(mfxU16 , EnableMBForceIntra ) + FIELD_T(mfxU16 , AdaptiveMaxFrameSize ) + FIELD_T(mfxU16 , RepartitionCheckEnable ) /* tri-state option */ + FIELD_T(mfxU16 , EncodedUnitsInfo ) + FIELD_T(mfxU16 , EnableNalUnitType ) +) +#endif + +STRUCT(mfxExtLAControl, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16 , LookAheadDepth) + FIELD_T(mfxU16 , DependencyDepth) + FIELD_T(mfxU16 , DownScaleFactor) + FIELD_T(mfxU16 , NumOutStream) + +) + +STRUCT(mfxExtMBQP, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32 , NumQPAlloc) + FIELD_T(mfxU8* , QP) +) + +STRUCT(mfxExtChromaLocInfo, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16 , ChromaLocInfoPresentFlag) + FIELD_T(mfxU16 , ChromaSampleLocTypeTopField) + FIELD_T(mfxU16 , ChromaSampleLocTypeBottomField) +) + + +STRUCT(mfxExtDecodedFrameInfo, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16 , FrameType) +) + +STRUCT(mfxExtDecodeErrorReport, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32 , ErrorTypes) +) + + + +STRUCT(mfxInitParam, + FIELD_T(mfxIMPL, Implementation) + FIELD_S(mfxVersion, Version) + FIELD_T(mfxU16, ExternalThreads) + FIELD_T(mfxExtBuffer**, ExtParam) + FIELD_T(mfxU16, NumExtParam) + FIELD_T(mfxU16, GPUCopy) +) + + +STRUCT(mfxExtThreadsParam, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU16 , NumThread ) + FIELD_T(mfxI32 , SchedulingType) + FIELD_T(mfxI32 , Priority ) +) + +STRUCT(mfxExtVPPFieldProcessing, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16 , Mode) + FIELD_T(mfxU16 , InField) + FIELD_T(mfxU16 , OutField) +) + +STRUCT(mfxExtVPPRotation, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16 , Angle) +) + +STRUCT(mfxExtVPPMirroring, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16 , Type) +) + +STRUCT(mfxExtScreenCaptureParam, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU32 , DisplayIndex ) + FIELD_T(mfxU16 , EnableDirtyRect ) + FIELD_T(mfxU16 , EnableCursorCapture) +) + +STRUCT(mfxExtDirtyRect_Entry, + FIELD_T(mfxU32, Left ) + FIELD_T(mfxU32, Top ) + FIELD_T(mfxU32, Right ) + FIELD_T(mfxU32, Bottom ) +) + +STRUCT(mfxExtDirtyRect, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxI16, NumRect) + FIELD_S(mfxExtDirtyRect_Entry, Rect ) +) + +STRUCT(mfxExtMoveRect_Entry, + FIELD_T(mfxU32, DestLeft ) + FIELD_T(mfxU32, DestTop ) + FIELD_T(mfxU32, DestRight ) + FIELD_T(mfxU32, DestBottom) + + FIELD_T(mfxU32, SourceLeft) + FIELD_T(mfxU32, SourceTop ) +) + +STRUCT(mfxExtMoveRect, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxI16, NumRect) + FIELD_S(mfxExtMoveRect_Entry, Rect ) +) + +STRUCT(mfxExtMVCSeqDesc, + FIELD_S(mfxExtBuffer , Header ) + FIELD_T(mfxU32 , NumView ) + FIELD_T(mfxU32 , NumViewAlloc ) + //FIELD_S(mfxMVCViewDependency* , View ) + FIELD_T(mfxU32 , NumViewId ) + FIELD_T(mfxU32 , NumViewIdAlloc) + //FIELD_S(mfxU16* , ViewId ) + FIELD_T(mfxU32 , NumOP ) + FIELD_T(mfxU32 , NumOPAlloc ) + //FIELD_S(mfxMVCOperationPoint* , OP ) + FIELD_T(mfxU16 , NumRefsTotal ) +) + +STRUCT(mfxExtMBDisableSkipMap, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU32 , MapSize) +) + +//STRUCT(mfxExtCodingOptionVPS, +// FIELD_S(mfxExtBuffer, Header ) +// FIELD_T(mfxU8* , VPSBuffer ) +// FIELD_T(mfxU16 , VPSBufSize) +// FIELD_T(mfxU16 , VPSId) +//) + +#if (MFX_VERSION >= MFX_VERSION_NEXT) + +STRUCT(mfxExtHEVCParam, + FIELD_S(mfxExtBuffer , Header ) + FIELD_T(mfxU16 , PicWidthInLumaSamples ) + FIELD_T(mfxU16 , PicHeightInLumaSamples) + FIELD_T(mfxU64 , GeneralConstraintFlags) + FIELD_T(mfxU16 , SampleAdaptiveOffset) + FIELD_T(mfxU16 , LCUSize) +) + +#elif (MFX_VERSION >= 1026) + +STRUCT(mfxExtHEVCParam, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, PicWidthInLumaSamples) + FIELD_T(mfxU16, PicHeightInLumaSamples) + FIELD_T(mfxU64, GeneralConstraintFlags) + FIELD_T(mfxU16, SampleAdaptiveOffset) + FIELD_T(mfxU16, LCUSize) +) + +#else + +STRUCT(mfxExtHEVCParam, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, PicWidthInLumaSamples) + FIELD_T(mfxU16, PicHeightInLumaSamples) + FIELD_T(mfxU64, GeneralConstraintFlags) + +) + +#endif + +STRUCT(mfxExtHEVCRegion, + FIELD_S(mfxExtBuffer, Header ) + FIELD_T(mfxU32 , RegionId) + FIELD_T(mfxU16 , RegionType) + FIELD_T(mfxU16 , RegionEncoding) +) + +#if (MFX_VERSION >= MFX_VERSION_NEXT) +STRUCT(mfxExtVP9DecodedFrameInfo, + FIELD_S(mfxExtBuffer, Header ) + //FIELD_T(mfxU16 , DisplayWidth) + //FIELD_T(mfxU16 , DisplayHeight) +) +#endif + +#if (MFX_VERSION >= 1026) +STRUCT(mfxVP9SegmentParam, + FIELD_T(mfxU16, FeatureEnabled) + FIELD_T(mfxI16, QIndexDelta) + FIELD_T(mfxI16, LoopFilterLevelDelta) + FIELD_T(mfxU16, ReferenceFrame) +) + +STRUCT(mfxExtVP9Segmentation, + FIELD_S(mfxExtBuffer , Header) + FIELD_T(mfxU16 , NumSegments) + FIELD_S(mfxVP9SegmentParam, Segment) + FIELD_T(mfxU16 , SegmentIdBlockSize) + FIELD_T(mfxU32 , NumSegmentIdAlloc) + FIELD_T(mfxU8* , SegmentId) +) + +STRUCT(mfxVP9TemporalLayer, + FIELD_T(mfxU16, FrameRateScale) + FIELD_T(mfxU16, TargetKbps) +) + +STRUCT(mfxExtVP9TemporalLayers, + FIELD_S(mfxExtBuffer , Header) + FIELD_S(mfxVP9TemporalLayer , Layer) +) +#endif + +#if (MFX_VERSION >= MFX_VERSION_NEXT) +STRUCT(mfxExtVP9Param, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16 , FrameWidth) + FIELD_T(mfxU16 , FrameHeight) + FIELD_T(mfxU16 , WriteIVFHeaders) + FIELD_T(mfxI16 , LoopFilterRefDelta) + FIELD_T(mfxI16 , LoopFilterModeDelta) + FIELD_T(mfxI16 , QIndexDeltaLumaDC) + FIELD_T(mfxI16 , QIndexDeltaChromaAC) + FIELD_T(mfxI16 , QIndexDeltaChromaDC) + FIELD_T(mfxU16 , NumTileRows) + FIELD_T(mfxU16 , NumTileColumns) +) +#elif (MFX_VERSION >= 1026) +STRUCT(mfxExtVP9Param, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16 , FrameWidth) + FIELD_T(mfxU16 , FrameHeight) + FIELD_T(mfxU16 , WriteIVFHeaders) + FIELD_T(mfxI16 , QIndexDeltaLumaDC) + FIELD_T(mfxI16 , QIndexDeltaChromaAC) + FIELD_T(mfxI16 , QIndexDeltaChromaDC) +) +#endif + +STRUCT(mfxExtMBForceIntra, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32, MapSize) + FIELD_T(mfxU8*, Map) +) + +STRUCT(mfxExtMasteringDisplayColourVolume, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16 , InsertPayloadToggle) + FIELD_T(mfxU16 , DisplayPrimariesX) + FIELD_T(mfxU16 , DisplayPrimariesY) + FIELD_T(mfxU16 , WhitePointX) + FIELD_T(mfxU16 , WhitePointY) + FIELD_T(mfxU32 , MaxDisplayMasteringLuminance) + FIELD_T(mfxU32 , MinDisplayMasteringLuminance) +) + +STRUCT(mfxExtContentLightLevelInfo, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16 , InsertPayloadToggle) + FIELD_T(mfxU16 , MaxContentLightLevel) + FIELD_T(mfxU16 , MaxPicAverageLightLevel) +) + +STRUCT(mfxExtPredWeightTable, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, LumaLog2WeightDenom) + FIELD_T(mfxU16, ChromaLog2WeightDenom) + FIELD_T(mfxU16, LumaWeightFlag) + FIELD_T(mfxU16, ChromaWeightFlag) + FIELD_T(mfxI16, Weights) +) + +#if defined(__MFXBRC_H__) +STRUCT(mfxExtBRC, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxHDL, pthis) + FIELD_T(mfxHDL, Init) + FIELD_T(mfxHDL, Reset) + FIELD_T(mfxHDL, Close) + FIELD_T(mfxHDL, GetFrameCtrl) + FIELD_T(mfxHDL, Update) +) +#endif // defined(__MFXBRC_H__) + +STRUCT(mfxExtMultiFrameParam, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16 , MFMode) + FIELD_T(mfxU16 , MaxNumFrames) +) + +STRUCT(mfxExtMultiFrameControl, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU32 , Timeout) + FIELD_T(mfxU16 , Flush) +) + +STRUCT(mfxEncodedUnitInfo, + FIELD_T(mfxU16, Type) + FIELD_T(mfxU32, Offset) + FIELD_T(mfxU32, Size) +) + +STRUCT(mfxExtEncodedUnitsInfo, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxEncodedUnitInfo*, UnitInfo) + FIELD_T(mfxU16, NumUnitsAlloc) + FIELD_T(mfxU16, NumUnitsEncoded) +) + +#if defined(__MFXSCD_H__) +STRUCT(mfxExtSCD, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, SceneType) +) +#endif // defined(__MFXSCD_H__) + +#if (MFX_VERSION >= 1026) +#if (MFX_VERSION >= MFX_VERSION_NEXT) +STRUCT(mfxExtVppMctf, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, FilterStrength) + FIELD_T(mfxU16, Overlap) + FIELD_T(mfxU32, BitsPerPixelx100k) + FIELD_T(mfxU16, Deblocking) + FIELD_T(mfxU16, TemporalMode) + FIELD_T(mfxU16, MVPrecision) +) +#else +STRUCT(mfxExtVppMctf, + FIELD_S(mfxExtBuffer, Header) + FIELD_T(mfxU16, FilterStrength) +) +#endif +#endif diff --git a/vshampor/deshuffler/msdk_api/mediasdk_structures/ts_typedef.h b/vshampor/deshuffler/msdk_api/mediasdk_structures/ts_typedef.h new file mode 100644 index 0000000..dea5cc7 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/mediasdk_structures/ts_typedef.h @@ -0,0 +1,49 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#if __cplusplus >= 201103L +#define TYPEDEF_MEMBER(base, member, name) typedef std::remove_referencemember)>::type name; +#else +#if defined(__GNUC__) +#define TYPEDEF_MEMBER(base, member, name) typedef typeof(((base*)0)->member) name; +#endif +#endif +TYPEDEF_MEMBER(mfxExtOpaqueSurfaceAlloc, In, mfxExtOpaqueSurfaceAlloc_InOut) +TYPEDEF_MEMBER(mfxExtAVCRefListCtrl, PreferredRefList[0], mfxExtAVCRefListCtrl_Entry) +TYPEDEF_MEMBER(mfxExtPictureTimingSEI, TimeStamp[0], mfxExtPictureTimingSEI_TimeStamp) +TYPEDEF_MEMBER(mfxExtAvcTemporalLayers, Layer[0], mfxExtAvcTemporalLayers_Layer) +TYPEDEF_MEMBER(mfxExtAVCEncodedFrameInfo, UsedRefListL0[0], mfxExtAVCEncodedFrameInfo_RefList) +TYPEDEF_MEMBER(mfxExtVPPVideoSignalInfo, In, mfxExtVPPVideoSignalInfo_InOut) +TYPEDEF_MEMBER(mfxExtEncoderROI, ROI[0], mfxExtEncoderROI_Entry) +TYPEDEF_MEMBER(mfxExtDirtyRect, Rect[0], mfxExtDirtyRect_Entry) +TYPEDEF_MEMBER(mfxExtMoveRect, Rect[0], mfxExtMoveRect_Entry) +typedef union { mfxU32 n; char c[4]; } mfx4CC; +typedef mfxExtAVCRefLists::mfxRefPic mfxExtAVCRefLists_mfxRefPic; +typedef mfxExtFeiEncMV::mfxExtFeiEncMVMB mfxExtFeiEncMV_MB; +typedef mfxExtFeiEncMBCtrl::mfxExtFeiEncMBCtrlMB mfxExtFeiEncMBCtrl_MB; +typedef mfxExtFeiPreEncMVPredictors::mfxExtFeiPreEncMVPredictorsMB mfxExtFeiPreEncMVPredictors_MB; +typedef mfxExtFeiPreEncMV::mfxExtFeiPreEncMVMB mfxExtFeiPreEncMV_MB; +typedef mfxExtFeiPreEncMBStat::mfxExtFeiPreEncMBStatMB mfxExtFeiPreEncMBStat_MB; + +#if MFX_VERSION >= 1023 +typedef mfxExtFeiPPS::mfxExtFeiPpsDPB mfxExtFeiPPS_mfxExtFeiPpsDPB; +#endif // MFX_VERSION >= 1023 diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/CMakeLists.txt b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/CMakeLists.txt new file mode 100644 index 0000000..0636d06 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/CMakeLists.txt @@ -0,0 +1,177 @@ +# Copyright (c) 2017 Intel Corporation +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required( VERSION 2.8.5 FATAL_ERROR ) +project( mfx ) + +set( MFX_API_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/../../include ) + +# While equal to get_mfx_version in samples/builder, this function should remain separate to make this file self-sufficient +function( get_api_version mfx_version_major mfx_version_minor ) + file(STRINGS ${MFX_API_FOLDER}/mfxdefs.h major REGEX "#define MFX_VERSION_MAJOR") + file(STRINGS ${MFX_API_FOLDER}/mfxdefs.h minor REGEX "#define MFX_VERSION_MINOR") + string(REPLACE "#define MFX_VERSION_MAJOR " "" major ${major}) + string(REPLACE "#define MFX_VERSION_MINOR " "" minor ${minor}) + set(${mfx_version_major} ${major} PARENT_SCOPE) + set(${mfx_version_minor} ${minor} PARENT_SCOPE) +endfunction() + +set( CMAKE_LIB_DIR ${CMAKE_BINARY_DIR}/__lib ) + +if( CMAKE_SYSTEM_NAME MATCHES Windows ) + add_definitions(-DMINGW_HAS_SECURE_API -DWIN32) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + add_definitions(-DWIN64) + endif( ) +else( ) + + # If user did not override CMAKE_INSTALL_PREFIX, then set the default prefix + # to /opt/intel/mediasdk instead of cmake's default + if( CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT ) + set( CMAKE_INSTALL_PREFIX /opt/intel/mediasdk CACHE PATH "Install Path Prefix" FORCE ) + endif( ) + message( STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}" ) + + include( GNUInstallDirs ) + + if( NOT DEFINED MFX_PLUGINS_DIR ) + set( MFX_PLUGINS_DIR ${CMAKE_INSTALL_PREFIX}/plugins ) + endif( ) + add_definitions( -DMFX_PLUGINS_DIR="${MFX_PLUGINS_DIR}" ) + message( STATUS "MFX_PLUGINS_DIR=${MFX_PLUGINS_DIR}" ) + + if( NOT DEFINED MFX_MODULES_DIR ) + set( MFX_MODULES_DIR ${CMAKE_INSTALL_FULL_LIBDIR} ) + endif( ) + add_definitions( -DMFX_MODULES_DIR="${MFX_MODULES_DIR}" ) + message( STATUS "MFX_MODULES_DIR=${MFX_MODULES_DIR}" ) + + add_definitions(-DUNIX) + + if( CMAKE_SYSTEM_NAME MATCHES Linux ) + add_definitions(-D__USE_LARGEFILE64 -D_FILE_OFFSET_BITS=64 -DLINUX -DLINUX32) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + add_definitions(-DLINUX64) + endif( ) + endif( ) + + if( CMAKE_SYSTEM_NAME MATCHES Darwin ) + add_definitions(-DOSX) + add_definitions(-DOSX32) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + add_definitions(-DOSX64) + endif( ) + endif( ) + + set(no_warnings "-Wno-unknown-pragmas -Wno-unused") + set(warnings "-Wall -Wformat -Wformat-security") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pipe -fPIC -std=c++11 ${warnings} ${no_warnings}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG") + + if (DEFINED CMAKE_FIND_ROOT_PATH) + append("--sysroot=${CMAKE_FIND_ROOT_PATH} " LINK_FLAGS) + endif (DEFINED CMAKE_FIND_ROOT_PATH) +endif( ) + +include_directories ( + ${MFX_API_FOLDER} + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +list(APPEND sources + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_critical_section.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_critical_section_linux.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_dispatcher.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_function_table.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_library_iterator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_library_iterator_linux.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_load_dll.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_load_dll_linux.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_win_reg_key.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_dxva2_device.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_plugin_hive.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_plugin_hive_linux.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_plugin_cfg_parser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_load_plugin.cpp + ) + +function( append_property target property_name property ) + get_target_property( property ${ARGV0} ${ARGV1} ) + if( property MATCHES NOTFOUND) + set( property "" ) + endif( ) + string( REPLACE ";" " " property "${ARGV2} ${property}" ) + set_target_properties( ${ARGV0} PROPERTIES ${ARGV1} "${property}" ) +endfunction( ) + +function( make_static_library name ) + if( sources.plus ) + list( APPEND sources ${sources.plus} ) + endif( ) + + set( target ${ARGV0} ) + + add_library( ${target} STATIC ${include} ${sources} ) + + set( link_flags_list "-Wl,--no-undefined,-z,relro,-z,now,-z,noexecstack") + append_property( ${target} LINK_FLAGS "${link_flags_list} -fstack-protector" ) + + if( defs ) + append_property( ${target} COMPILE_FLAGS ${defs} ) + endif( ) + + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_LIB_DIR}/${CMAKE_BUILD_TYPE} FOLDER ${target} ) +endfunction( ) + +set( defs "" ) +make_static_library( mfx ) + +set( defs "-DMFX_DISPATCHER_EXPOSED_PREFIX" ) +make_static_library( dispatch_shared ) + +list(APPEND sources.plus + ${CMAKE_CURRENT_SOURCE_DIR}/src/mfx_dispatcher_log.cpp + ) + +set( defs "-DMFX_DISPATCHER_LOG -DDXVA2DEVICE_LOG" ) +make_static_library( dispatch_trace ) + +get_api_version(MFX_VERSION_MAJOR MFX_VERSION_MINOR) +set( PKG_CONFIG_FNAME "${CMAKE_LIB_DIR}/${CMAKE_BUILD_TYPE}/lib${PROJECT_NAME}.pc") +set( PKG_CONFIG_FNAME_SHARED "${CMAKE_LIB_DIR}/${CMAKE_BUILD_TYPE}/lib${PROJECT_NAME}-shared.pc") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/pkg-config.pc.cmake" ${PKG_CONFIG_FNAME} @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/pkg-config-shared.pc.cmake" ${PKG_CONFIG_FNAME_SHARED} @ONLY) + +install( TARGETS mfx ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) +install( TARGETS dispatch_shared ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) +install( FILES ${PKG_CONFIG_FNAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) +install( FILES ${PKG_CONFIG_FNAME_SHARED} DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) +install( DIRECTORY ${MFX_API_FOLDER}/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN *.h ) + +# For backwards compatibility, create a relative symbolic link without the "lib" +# prefix to the .pc file. +set( PKG_CONFIG_LFNAME "${CMAKE_LIB_DIR}/${CMAKE_BUILD_TYPE}/${PROJECT_NAME}.pc" ) +add_custom_target(pc_link_target ALL COMMAND ${CMAKE_COMMAND} -E create_symlink lib${PROJECT_NAME}.pc ${PKG_CONFIG_LFNAME}) +install( FILES ${PKG_CONFIG_LFNAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/intel_api_factory.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/intel_api_factory.h new file mode 100644 index 0000000..0be5dcb --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/intel_api_factory.h @@ -0,0 +1,33 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +HRESULT APIENTRY InitialiseMediaSession(_Out_ HANDLE* handle, _In_ LPVOID lpParam, _Reserved_ LPVOID lpReserved); +HRESULT APIENTRY DisposeMediaSession(_In_ const HANDLE handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_critical_section.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_critical_section.h new file mode 100644 index 0000000..376dd30 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_critical_section.h @@ -0,0 +1,66 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if !defined(__MFX_CRITICAL_SECTION_H) +#define __MFX_CRITICAL_SECTION_H + +#include + +namespace MFX +{ + +// Just set "critical section" instance to zero for initialization. +typedef volatile mfxL32 mfxCriticalSection; + +// Enter the global critical section. +void mfxEnterCriticalSection(mfxCriticalSection *pCSection); + +// Leave the global critical section. +void mfxLeaveCriticalSection(mfxCriticalSection *pCSection); + +class MFXAutomaticCriticalSection +{ +public: + // Constructor + explicit MFXAutomaticCriticalSection(mfxCriticalSection *pCSection) + { + m_pCSection = pCSection; + mfxEnterCriticalSection(m_pCSection); + } + + // Destructor + ~MFXAutomaticCriticalSection() + { + mfxLeaveCriticalSection(m_pCSection); + } + +protected: + // Pointer to a critical section + mfxCriticalSection *m_pCSection; + +private: + // unimplemented by intent to make this class non-copyable + MFXAutomaticCriticalSection(const MFXAutomaticCriticalSection &); + void operator=(const MFXAutomaticCriticalSection &); +}; + +} // namespace MFX + +#endif // __MFX_CRITICAL_SECTION_H diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher.h new file mode 100644 index 0000000..f20d465 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher.h @@ -0,0 +1,219 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if !defined(__MFX_DISPATCHER_H) +#define __MFX_DISPATCHER_H + +#include +#include +#include +#include +#include "mfx_dispatcher_defs.h" +#include "mfx_load_plugin.h" +#include "mfxenc.h" +#include "mfxpak.h" + +#define INTEL_VENDOR_ID 0x8086 + +mfxStatus MFXQueryVersion(mfxSession session, mfxVersion *version); + +enum +{ + // to avoid code changing versions are just inherited + // from the API header file. + DEFAULT_API_VERSION_MAJOR = MFX_VERSION_MAJOR, + DEFAULT_API_VERSION_MINOR = MFX_VERSION_MINOR +}; + +// +// declare functions' integer identifiers. +// + +#undef FUNCTION +#define FUNCTION(return_value, func_name, formal_param_list, actual_param_list) \ + e##func_name, + +enum eFunc +{ + eMFXInit, + eMFXClose, + eMFXQueryIMPL, + eMFXQueryVersion, + eMFXJoinSession, + eMFXDisjoinSession, + eMFXCloneSession, + eMFXSetPriority, + eMFXGetPriority, + eMFXInitEx, +#include "mfx_exposed_functions_list.h" + eVideoFuncTotal +}; + +enum ePluginFunc +{ + eMFXVideoUSER_Load, + eMFXVideoUSER_LoadByPath, + eMFXVideoUSER_UnLoad, + eMFXAudioUSER_Load, + eMFXAudioUSER_UnLoad, + ePluginFuncTotal +}; + +enum eAudioFunc +{ + eFakeAudioEnum = eMFXGetPriority, +#include "mfxaudio_exposed_functions_list.h" + eAudioFuncTotal +}; + +// declare max buffer length for regsitry key name +enum +{ + MFX_MAX_REGISTRY_KEY_NAME = 256 +}; + +// declare the maximum DLL path +enum +{ + MFX_MAX_DLL_PATH = 1024 +}; + +// declare library's implementation types +enum eMfxImplType +{ + MFX_LIB_HARDWARE = 0, + MFX_LIB_SOFTWARE = 1, + MFX_LIB_PSEUDO = 2, + + MFX_LIB_IMPL_TYPES +}; + +// declare dispatcher's version +enum +{ + MFX_DISPATCHER_VERSION_MAJOR = 1, + MFX_DISPATCHER_VERSION_MINOR = 2 +}; + +struct _mfxSession +{ + // A real handle from MFX engine passed to a called function + mfxSession session; + + mfxFunctionPointer callTable[eVideoFuncTotal]; + mfxFunctionPointer callPlugInsTable[ePluginFuncTotal]; + mfxFunctionPointer callAudioTable[eAudioFuncTotal]; + + // Current library's implementation (exact implementation) + mfxIMPL impl; +}; + +// declare a dispatcher's handle +struct MFX_DISP_HANDLE : public _mfxSession +{ + // Default constructor + MFX_DISP_HANDLE(const mfxVersion requiredVersion); + // Destructor + ~MFX_DISP_HANDLE(void); + + // Load the library's module + mfxStatus LoadSelectedDLL(const msdk_disp_char *pPath, eMfxImplType implType, mfxIMPL impl, mfxIMPL implInterface, mfxInitParam &par); + // Unload the library's module + mfxStatus UnLoadSelectedDLL(void); + + // Close the handle + mfxStatus Close(void); + + // NOTE: changing order of struct's members can make different version of + // dispatchers incompatible. Think of different modules (e.g. MFT filters) + // within a single application. + + // Library's implementation type (hardware or software) + eMfxImplType implType; + // Current library's VIA interface + mfxIMPL implInterface; + // Dispatcher's version. If version is 1.1 or lower, then old dispatcher's + // architecture is used. Otherwise it means current dispatcher's version. + mfxVersion dispVersion; + // Required API version of session initialized + const mfxVersion apiVersion; + // Actual library API version + mfxVersion actualApiVersion; + // Status of loaded dll + mfxStatus loadStatus; + // Resgistry subkey name for windows version + msdk_disp_char subkeyName[MFX_MAX_REGISTRY_KEY_NAME]; + // Storage ID for windows version + int storageID; + + // Library's module handle + mfxModuleHandle hModule; + + MFX::MFXPluginStorage pluginHive; + MFX::MFXPluginFactory pluginFactory; + +private: + // Declare assignment operator and copy constructor to prevent occasional assignment + MFX_DISP_HANDLE(const MFX_DISP_HANDLE &); + MFX_DISP_HANDLE & operator = (const MFX_DISP_HANDLE &); + +}; + +// declare comparison operator +inline +bool operator == (const mfxVersion &one, const mfxVersion &two) +{ + return (one.Version == two.Version); + +} // bool operator == (const mfxVersion &one, const mfxVersion &two) + +inline +bool operator < (const mfxVersion &one, const mfxVersion &two) +{ + return (one.Major == two.Major) && (one.Minor < two.Minor); + +} // bool operator < (const mfxVersion &one, const mfxVersion &two) + +inline +bool operator <= (const mfxVersion &one, const mfxVersion &two) +{ + return (one == two) || (one < two); +} // bool operator <= (const mfxVersion &one, const mfxVersion &two) + + +// +// declare a table with functions descriptions +// + +typedef +struct FUNCTION_DESCRIPTION +{ + // Literal function's name + const char *pName; + // API version when function appeared first time + mfxVersion apiVersion; +} FUNCTION_DESCRIPTION; + +extern const +FUNCTION_DESCRIPTION APIFunc[eVideoFuncTotal]; + +extern const +FUNCTION_DESCRIPTION APIAudioFunc[eAudioFuncTotal]; +#endif // __MFX_DISPATCHER_H diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher_defs.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher_defs.h new file mode 100644 index 0000000..fff6adb --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher_defs.h @@ -0,0 +1,66 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once +#include "mfxdefs.h" +#include + +#if defined(MFX_DISPATCHER_LOG) +#include +#include +#endif + +#define MAX_PLUGIN_PATH 4096 +#define MAX_PLUGIN_NAME 4096 + +typedef char msdk_disp_char; +//#define msdk_disp_char_cpy_s(to, to_size, from) strcpy(to, from) + +inline void msdk_disp_char_cpy_s(char * to, size_t to_size, const char * from) +{ + size_t source_len = strlen(from); + size_t num_chars = (to_size - 1) < source_len ? (to_size - 1) : source_len; + strncpy(to, from, num_chars); + to[num_chars] = 0; +} + +#if defined(MFX_DISPATCHER_LOG) +#define MSDK2WIDE(x) getWideString(x).c_str() + +inline std::wstring getWideString(const char * string) +{ + size_t len = strlen(string); + return std::wstring(string, string + len); +} +#else + #define MSDK2WIDE(x) x +#endif + + +#if defined(__GNUC__) +#define sscanf_s sscanf +#define swscanf_s swscanf +#endif + + +// declare library module's handle +typedef void * mfxModuleHandle; + +typedef void (MFX_CDECL * mfxFunctionPointer)(void); diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher_log.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher_log.h new file mode 100644 index 0000000..d67598f --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dispatcher_log.h @@ -0,0 +1,240 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if !defined(__MFX_DISPATCHER_LOG_H) +#define __MFX_DISPATCHER_LOG_H + +////////////////////////////////////////////////////////////////////////// +//dispatcher log (DL) level +#define DL_INFO 1 +#define DL_WRN 2 +#define DL_ERROR 4 +#define DL_LOADED_LIBRARY 8 +////////////////////////////////////////////////////////////////////////// +//opcodes used only in events +enum +{ + DL_EVENT_START = 1, + DL_EVENT_STOP, + DL_EVENT_MSG +}; +////////////////////////////////////////////////////////////////////////// +#define DL_SINK_NULL 0 +#define DL_SINK_PRINTF 1 +#define DL_SINK_IMsgHandler 2 + +#define MFXFOURCCTYPE() "%c%c%c%c" +#define ZERO_OR_SPACE(value) ((0==(value)) ? '0' : (value)) +#define MFXU32TOFOURCC(mfxu32)\ + ZERO_OR_SPACE((char)(mfxu32 & 0xFF)), \ + ZERO_OR_SPACE((char)((mfxu32 >> 8) & 0xFF)),\ + ZERO_OR_SPACE((char)((mfxu32 >> 16) & 0xFF)),\ + ZERO_OR_SPACE((char)((mfxu32 >> 24) & 0xFF)) + +#define MFXGUIDTYPE() "%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X" + +#define MFXGUIDTOHEX(guid)\ + (guid)->Data[0],\ + (guid)->Data[1],\ + (guid)->Data[2],\ + (guid)->Data[3],\ + (guid)->Data[4],\ + (guid)->Data[5],\ + (guid)->Data[6],\ + (guid)->Data[7],\ + (guid)->Data[8],\ + (guid)->Data[9],\ + (guid)->Data[10],\ + (guid)->Data[11],\ + (guid)->Data[12],\ + (guid)->Data[13],\ + (guid)->Data[14],\ + (guid)->Data[15] + +#if defined(MFX_DISPATCHER_LOG) + +//---------------------------setup section------------------------ +//using of formating instead of variadic macro with NULL end, +//leads to more flexibility in format, however constructing string +//with vsprintf_s is a time wasting +#define DISPATCHER_LOG_USE_FORMATING 1 + +//creates unique object, event guid registration, factories on heap +//heap reduce stack allocation and reduce reservation time at startup +//is a vital if mediasdk wont use +#define DISPATCHER_LOG_HEAP_SINGLETONES + + +#include +#include + +//callback interface for intercept logging messages +class IMsgHandler +{ +public: + virtual ~IMsgHandler(){} + virtual void Write(int level, int opcode, const char * msg, va_list argptr) = 0; +}; + +#define DISPATCHER_LOG(lvl, opcode, str) +#define DISPATCHER_LOG_OPERATION(operation) + +#define __name_from_line( name, line ) name ## line +#define _name_from_line( name , line) __name_from_line( name, line ) +#define name_from_line( name ) _name_from_line( name, __LINE__) + + +#define DISPATCHER_LOG_AUTO(lvl, msg)\ + DispatchLogBlockHelper name_from_line(__auto_log_)(lvl); name_from_line(__auto_log_).Write msg; + +#include +#include +#include +#include + +template +class DSSingleTone +{ +public: + template + inline static T & get(TParam1 par1) + { + T * pstored; + if (NULL == (pstored = store_or_load())) + { + return *store_or_load(new T(par1)); + } + return *pstored; + } + + inline static T & get() + { + T * pstored; + if (NULL == (pstored = store_or_load())) + { + return *store_or_load(new T()); + } + return *pstored; + } +private: + //if obj == NULL, then it load + //if obj != NULL then it store obj + inline static T * store_or_load(T * obj = NULL) + { + static std::unique_ptr instance; + if (NULL != obj) + { + instance.reset(obj); + } + return instance.get(); + } +}; + +class DispatchLog + : public DSSingleTone +{ + friend class DSSingleTone; + std::listm_Recepients; + int m_DispatcherLogSink; + +public: + //sets current sink + void SetSink(int nsink, IMsgHandler *pHandler); + void AttachSink(int nsink, IMsgHandler *pHandler); + void DetachSink(int nsink, IMsgHandler *pHandler); + void ExchangeSink(int nsink, IMsgHandler *pOld, IMsgHandler *pNew); + void DetachAllSinks(); + void Write(int level, int opcode, const char * msg, va_list argptr); + +protected: + DispatchLog(); +}; + +//allows to push arguments on the stack without declaring them as function parameters +struct DispatcherLogBracketsHelper +{ + int m_level; + int m_opcode; + DispatcherLogBracketsHelper(int level, int opcode) + :m_level(level) + ,m_opcode(opcode) + { + } + void Write(const char * str, ...); +} ; + +//auto log on ctor dtor +struct DispatchLogBlockHelper +{ + int m_level; + void Write(const char * str, ...); + DispatchLogBlockHelper (int level) + : m_level(level) + { + } + ~DispatchLogBlockHelper(); +}; + +//----utility sinks----- + +#if defined(DISPATCHER_LOG_REGISTER_FILE_WRITER) +class FileSink + : public DSSingleTone + , public IMsgHandler +{ + friend class DSSingleTone; +public: + virtual void Write(int level, int opcode, const char * msg, va_list argptr); + ~FileSink() + { + if (NULL != m_hdl) + fclose(m_hdl); + } +private: + FILE * m_hdl; + FileSink(const std::string & log_file) + { + m_hdl = fopen(log_file.c_str(), "a"); + } + +}; +#endif + +//-----utility functions +//since they are not called outside of macro we can define them here +std::string DispatcherLog_GetMFXImplString(int impl); +const char *DispatcherLog_GetMFXStatusString(int sts); + +#else // !defined(MFX_DISPATCHER_LOG) + + #define DISPATCHER_LOG(level, opcode, message) + #define DISPATCHER_LOG_AUTO(level, message) + #define DISPATCHER_LOG_OPERATION(operation) + +#endif// !defined(MFX_DISPATCHER_LOG) + + +#define DISPATCHER_LOG_INFO(msg) DISPATCHER_LOG(DL_INFO, DL_EVENT_MSG, msg) +#define DISPATCHER_LOG_WRN(msg) DISPATCHER_LOG(DL_WRN, DL_EVENT_MSG, msg) +#define DISPATCHER_LOG_ERROR(msg) DISPATCHER_LOG(DL_ERROR, DL_EVENT_MSG, msg) +#define DISPATCHER_LOG_LIBRARY(msg) DISPATCHER_LOG(DL_LOADED_LIBRARY, DL_EVENT_MSG, msg) +#define DISPATCHER_LOG_BLOCK(msg) DISPATCHER_LOG_AUTO(DL_INFO, msg) + +#endif // !defined(__MFX_DISPATCHER_LOG_H) diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dxva2_device.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dxva2_device.h new file mode 100644 index 0000000..38face2 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_dxva2_device.h @@ -0,0 +1,142 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if !defined(__MFX_DXVA2_DEVICE_H) +#define __MFX_DXVA2_DEVICE_H + + +#include + +#ifdef DXVA2DEVICE_LOG +#include +#define DXVA2DEVICE_TRACE(expr) printf expr; +#define DXVA2DEVICE_TRACE_OPERATION(expr) expr; +#else +#define DXVA2DEVICE_TRACE(expr) +#define DXVA2DEVICE_TRACE_OPERATION(expr) +#endif + +namespace MFX +{ + +class DXDevice +{ +public: + // Default constructor + DXDevice(void); + // Destructor + virtual + ~DXDevice(void) = 0; + + // Initialize device using DXGI 1.1 or VAAPI interface + virtual + bool Init(const mfxU32 adapterNum) = 0; + + // Obtain graphic card's parameter + mfxU32 GetVendorID(void) const; + mfxU32 GetDeviceID(void) const; + mfxU64 GetDriverVersion(void) const; + mfxU64 GetLUID(void) const; + + // Provide the number of available adapters + mfxU32 GetAdapterCount(void) const; + + // Close the object + virtual + void Close(void); + + // Load the required DLL module + void LoadDLLModule(const wchar_t *pModuleName); + +protected: + + // Free DLL module + void UnloadDLLModule(void); + + + // Number of adapters available + mfxU32 m_numAdapters; + + // Vendor ID + mfxU32 m_vendorID; + // Device ID + mfxU32 m_deviceID; + // x.x.x.x each x of two bytes + mfxU64 m_driverVersion; + // LUID + mfxU64 m_luid; + +private: + // unimplemented by intent to make this class and its descendants non-copyable + DXDevice(const DXDevice &); + void operator=(const DXDevice &); +}; + + + +class DXVA2Device +{ +public: + // Default constructor + DXVA2Device(void); + // Destructor + ~DXVA2Device(void); + + // Initialize device using D3D v9 interface + bool InitD3D9(const mfxU32 adapterNum); + + // Initialize device using DXGI 1.1 interface + bool InitDXGI1(const mfxU32 adapterNum); + + // Obtain graphic card's parameter + mfxU32 GetVendorID(void) const; + mfxU32 GetDeviceID(void) const; + mfxU64 GetDriverVersion(void) const; + + // Provide the number of available adapters + mfxU32 GetAdapterCount(void) const; + + void Close(void); + +protected: + +#ifdef MFX_D3D9_ENABLED + // Get vendor & device IDs by alternative way (D3D9 in Remote Desktop sessions) + void UseAlternativeWay(const D3D9Device *pD3D9Device); +#endif // MFX_D3D9_ENABLED + // Number of adapters available + mfxU32 m_numAdapters; + + // Vendor ID + mfxU32 m_vendorID; + // Device ID + mfxU32 m_deviceID; + //x.x.x.x + mfxU64 m_driverVersion; + +private: + // unimplemented by intent to make this class non-copyable + DXVA2Device(const DXVA2Device &); + void operator=(const DXVA2Device &); +}; + +} // namespace MFX + +#endif // __MFX_DXVA2_DEVICE_H diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_exposed_functions_list.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_exposed_functions_list.h new file mode 100644 index 0000000..dc22c54 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_exposed_functions_list.h @@ -0,0 +1,141 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// +// WARNING: +// this file doesn't contain an include guard by intension. +// The file may be included into a source file many times. +// That is why this header doesn't contain any include directive. +// Please, do no try to fix it. +// + + + +// Use define API_VERSION to set the API of functions listed further +// When new functions are added new section with functions declarations must be started with updated define + +// +// API version 1.0 functions +// + +// API version where a function is added. Minor value should precedes the major value +#define API_VERSION {{0, 1}} + +// CORE interface functions +FUNCTION(mfxStatus, MFXVideoCORE_SetBufferAllocator, (mfxSession session, mfxBufferAllocator *allocator), (session, allocator)) +FUNCTION(mfxStatus, MFXVideoCORE_SetFrameAllocator, (mfxSession session, mfxFrameAllocator *allocator), (session, allocator)) +FUNCTION(mfxStatus, MFXVideoCORE_SetHandle, (mfxSession session, mfxHandleType type, mfxHDL hdl), (session, type, hdl)) +FUNCTION(mfxStatus, MFXVideoCORE_GetHandle, (mfxSession session, mfxHandleType type, mfxHDL *hdl), (session, type, hdl)) + +FUNCTION(mfxStatus, MFXVideoCORE_SyncOperation, (mfxSession session, mfxSyncPoint syncp, mfxU32 wait), (session, syncp, wait)) + +// ENCODE interface functions +FUNCTION(mfxStatus, MFXVideoENCODE_Query, (mfxSession session, mfxVideoParam *in, mfxVideoParam *out), (session, in, out)) +FUNCTION(mfxStatus, MFXVideoENCODE_QueryIOSurf, (mfxSession session, mfxVideoParam *par, mfxFrameAllocRequest *request), (session, par, request)) +FUNCTION(mfxStatus, MFXVideoENCODE_Init, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoENCODE_Reset, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoENCODE_Close, (mfxSession session), (session)) + +FUNCTION(mfxStatus, MFXVideoENCODE_GetVideoParam, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoENCODE_GetEncodeStat, (mfxSession session, mfxEncodeStat *stat), (session, stat)) +FUNCTION(mfxStatus, MFXVideoENCODE_EncodeFrameAsync, (mfxSession session, mfxEncodeCtrl *ctrl, mfxFrameSurface1 *surface, mfxBitstream *bs, mfxSyncPoint *syncp), (session, ctrl, surface, bs, syncp)) + +// DECODE interface functions +FUNCTION(mfxStatus, MFXVideoDECODE_Query, (mfxSession session, mfxVideoParam *in, mfxVideoParam *out), (session, in, out)) +FUNCTION(mfxStatus, MFXVideoDECODE_DecodeHeader, (mfxSession session, mfxBitstream *bs, mfxVideoParam *par), (session, bs, par)) +FUNCTION(mfxStatus, MFXVideoDECODE_QueryIOSurf, (mfxSession session, mfxVideoParam *par, mfxFrameAllocRequest *request), (session, par, request)) +FUNCTION(mfxStatus, MFXVideoDECODE_Init, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoDECODE_Reset, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoDECODE_Close, (mfxSession session), (session)) + +FUNCTION(mfxStatus, MFXVideoDECODE_GetVideoParam, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoDECODE_GetDecodeStat, (mfxSession session, mfxDecodeStat *stat), (session, stat)) +FUNCTION(mfxStatus, MFXVideoDECODE_SetSkipMode, (mfxSession session, mfxSkipMode mode), (session, mode)) +FUNCTION(mfxStatus, MFXVideoDECODE_GetPayload, (mfxSession session, mfxU64 *ts, mfxPayload *payload), (session, ts, payload)) +FUNCTION(mfxStatus, MFXVideoDECODE_DecodeFrameAsync, (mfxSession session, mfxBitstream *bs, mfxFrameSurface1 *surface_work, mfxFrameSurface1 **surface_out, mfxSyncPoint *syncp), (session, bs, surface_work, surface_out, syncp)) + +// VPP interface functions +FUNCTION(mfxStatus, MFXVideoVPP_Query, (mfxSession session, mfxVideoParam *in, mfxVideoParam *out), (session, in, out)) +FUNCTION(mfxStatus, MFXVideoVPP_QueryIOSurf, (mfxSession session, mfxVideoParam *par, mfxFrameAllocRequest *request), (session, par, request)) +FUNCTION(mfxStatus, MFXVideoVPP_Init, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoVPP_Reset, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoVPP_Close, (mfxSession session), (session)) + +FUNCTION(mfxStatus, MFXVideoVPP_GetVideoParam, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoVPP_GetVPPStat, (mfxSession session, mfxVPPStat *stat), (session, stat)) +FUNCTION(mfxStatus, MFXVideoVPP_RunFrameVPPAsync, (mfxSession session, mfxFrameSurface1 *in, mfxFrameSurface1 *out, mfxExtVppAuxData *aux, mfxSyncPoint *syncp), (session, in, out, aux, syncp)) + +#undef API_VERSION + +// +// API version 1.1 functions +// + +#define API_VERSION {{1, 1}} + +FUNCTION(mfxStatus, MFXVideoUSER_Register, (mfxSession session, mfxU32 type, const mfxPlugin *par), (session, type, par)) +FUNCTION(mfxStatus, MFXVideoUSER_Unregister, (mfxSession session, mfxU32 type), (session, type)) +FUNCTION(mfxStatus, MFXVideoUSER_ProcessFrameAsync, (mfxSession session, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxSyncPoint *syncp), (session, in, in_num, out, out_num, syncp)) + +#undef API_VERSION + +// +// API version 1.10 functions +// + +#define API_VERSION {{10, 1}} + +FUNCTION(mfxStatus, MFXVideoENC_Query,(mfxSession session, mfxVideoParam *in, mfxVideoParam *out), (session,in,out)) +FUNCTION(mfxStatus, MFXVideoENC_QueryIOSurf,(mfxSession session, mfxVideoParam *par, mfxFrameAllocRequest *request), (session,par,request)) +FUNCTION(mfxStatus, MFXVideoENC_Init,(mfxSession session, mfxVideoParam *par), (session,par)) +FUNCTION(mfxStatus, MFXVideoENC_Reset,(mfxSession session, mfxVideoParam *par), (session,par)) +FUNCTION(mfxStatus, MFXVideoENC_Close,(mfxSession session),(session)) +FUNCTION(mfxStatus, MFXVideoENC_ProcessFrameAsync,(mfxSession session, mfxENCInput *in, mfxENCOutput *out, mfxSyncPoint *syncp),(session,in,out,syncp)) + +FUNCTION(mfxStatus, MFXVideoVPP_RunFrameVPPAsyncEx, (mfxSession session, mfxFrameSurface1 *in, mfxFrameSurface1 *work, mfxFrameSurface1 **out, mfxSyncPoint *syncp), (session, in, work, out, syncp)) + +#undef API_VERSION + +#define API_VERSION {{13, 1}} + +FUNCTION(mfxStatus, MFXVideoPAK_Query, (mfxSession session, mfxVideoParam *in, mfxVideoParam *out), (session, in, out)) +FUNCTION(mfxStatus, MFXVideoPAK_QueryIOSurf, (mfxSession session, mfxVideoParam *par, mfxFrameAllocRequest *request), (session, par, request)) +FUNCTION(mfxStatus, MFXVideoPAK_Init, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoPAK_Reset, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoPAK_Close, (mfxSession session), (session)) +FUNCTION(mfxStatus, MFXVideoPAK_ProcessFrameAsync, (mfxSession session, mfxPAKInput *in, mfxPAKOutput *out, mfxSyncPoint *syncp), (session, in, out, syncp)) + +#undef API_VERSION + +#define API_VERSION {{14, 1}} + +// FUNCTION(mfxStatus, MFXInitEx, (mfxInitParam par, mfxSession session), (par, session)) +FUNCTION(mfxStatus, MFXDoWork, (mfxSession session), (session)) + +#undef API_VERSION + +#define API_VERSION {{19, 1}} + +FUNCTION(mfxStatus, MFXVideoENC_GetVideoParam, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoPAK_GetVideoParam, (mfxSession session, mfxVideoParam *par), (session, par)) +FUNCTION(mfxStatus, MFXVideoCORE_QueryPlatform, (mfxSession session, mfxPlatform* platform), (session, platform)) +FUNCTION(mfxStatus, MFXVideoUSER_GetPlugin, (mfxSession session, mfxU32 type, mfxPlugin *par), (session, type, par)) + +#undef API_VERSION \ No newline at end of file diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_library_iterator.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_library_iterator.h new file mode 100644 index 0000000..354fa24 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_library_iterator.h @@ -0,0 +1,130 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if !defined(__MFX_LIBRARY_ITERATOR_H) +#define __MFX_LIBRARY_ITERATOR_H + + +#include + +#if !defined(MEDIASDK_UWP_LOADER) && !defined(MEDIASDK_UWP_PROCTABLE) +#include "mfx_win_reg_key.h" +#endif + +#include "mfx_dispatcher.h" + +struct mfx_disp_adapters +{ + mfxU32 vendor_id; + mfxU32 device_id; +}; + +#define MFX_SO_BASE_NAME_LEN 15 // sizeof("libmfxhw32-p.so") = 15 + +#define MFX_MIN_REAL_LIBNAME MFX_SO_BASE_NAME_LEN + 4 // sizeof("libmfxhw32-p.so.0.0") >= 19 +#define MFX_MAX_REAL_LIBNAME MFX_MIN_REAL_LIBNAME + 8 // sizeof("libmfxhw32-p.so..") <= 27, max(sizeof())=sizeof(0xFFFF) = sizeof(65535) = 5 + +struct mfx_libs +{ + char name[MFX_MAX_REAL_LIBNAME+1]; + mfxVersion version; +}; + +namespace MFX +{ + +// declare desired storage ID +enum +{ + MFX_UNKNOWN_KEY = -1, + MFX_STORAGE_ID_OPT = 0, // storage is: MFX_MODULES_DIR + MFX_APP_FOLDER = 1, + + MFX_STORAGE_ID_FIRST = MFX_STORAGE_ID_OPT, + MFX_STORAGE_ID_LAST = MFX_STORAGE_ID_OPT +}; + +// Try to initialize using given implementation type. Select appropriate type automatically in case of MFX_IMPL_VIA_ANY. +// Params: adapterNum - in, pImplInterface - in/out, pVendorID - out, pDeviceID - out +mfxStatus SelectImplementationType(const mfxU32 adapterNum, mfxIMPL *pImplInterface, mfxU32 *pVendorID, mfxU32 *pDeviceID); + +const mfxU32 msdk_disp_path_len = 1024; + +class MFXLibraryIterator +{ +public: + // Default constructor + MFXLibraryIterator(void); + // Destructor + ~MFXLibraryIterator(void); + + // Initialize the iterator + mfxStatus Init(eMfxImplType implType, mfxIMPL implInterface, const mfxU32 adapterNum, int storageID); + + // Get the next library path + mfxStatus SelectDLLVersion(msdk_disp_char *pPath, size_t pathSize, + eMfxImplType *pImplType, mfxVersion minVersion); + + // Return interface type on which Intel adapter was found (if any): D3D9 or D3D11 + mfxIMPL GetImplementationType(); + + // Retrun registry subkey name on which dll was selected after sucesfull call to selectDllVesion + bool GetSubKeyName(msdk_disp_char *subKeyName, size_t length) const; + + int GetStorageID() const { return m_StorageID; } +protected: + + // Release the iterator + void Release(void); + + // Initialize the registry iterator + mfxStatus InitRegistry(eMfxImplType implType, mfxIMPL implInterface, const mfxU32 adapterNum, int storageID); + // Initialize the app folder iterator + mfxStatus InitFolder(eMfxImplType implType, mfxIMPL implInterface, const mfxU32 adapterNum, const msdk_disp_char * path); + + + eMfxImplType m_implType; // Required library implementation + mfxIMPL m_implInterface; // Required interface (D3D9, D3D11) + + mfxU32 m_vendorID; // (mfxU32) property of used graphic card + mfxU32 m_deviceID; // (mfxU32) property of used graphic card + bool m_bIsSubKeyValid; + wchar_t m_SubKeyName[MFX_MAX_REGISTRY_KEY_NAME]; // registry subkey for selected module loaded + int m_StorageID; + + int m_lastLibIndex; // (mfxU32) index of previously returned library + + mfxU32 m_adapters_num; + struct mfx_disp_adapters* m_adapters; + int m_selected_adapter; + mfxU32 m_libs_num; + struct mfx_libs* m_libs; + + msdk_disp_char m_path[msdk_disp_path_len]; + +private: + // unimplemented by intent to make this class non-copyable + MFXLibraryIterator(const MFXLibraryIterator &); + void operator=(const MFXLibraryIterator &); +}; + +} // namespace MFX + +#endif // __MFX_LIBRARY_ITERATOR_H diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_load_dll.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_load_dll.h new file mode 100644 index 0000000..81d227c --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_load_dll.h @@ -0,0 +1,49 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if !defined(__MFX_LOAD_DLL_H) +#define __MFX_LOAD_DLL_H + +#include "mfx_dispatcher.h" + +namespace MFX +{ + + + // + // declare DLL loading routines + // + + mfxStatus mfx_get_rt_dll_name(msdk_disp_char *pPath, size_t pathSize); + mfxStatus mfx_get_default_dll_name(msdk_disp_char *pPath, size_t pathSize, eMfxImplType implType); + mfxStatus mfx_get_default_plugin_name(msdk_disp_char *pPath, size_t pathSize, eMfxImplType implType); + + mfxStatus mfx_get_default_audio_dll_name(msdk_disp_char *pPath, size_t pathSize, eMfxImplType implType); + + + mfxModuleHandle mfx_dll_load(const msdk_disp_char *file_name); + //increments reference counter + mfxModuleHandle mfx_get_dll_handle(const msdk_disp_char *file_name); + mfxFunctionPointer mfx_dll_get_addr(mfxModuleHandle handle, const char *func_name); + bool mfx_dll_free(mfxModuleHandle handle); + +} // namespace MFX + +#endif // __MFX_LOAD_DLL_H diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_load_plugin.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_load_plugin.h new file mode 100644 index 0000000..fe866a4 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_load_plugin.h @@ -0,0 +1,85 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once +#include "mfxplugin.h" +#include "mfx_dispatcher_defs.h" +#include "mfx_plugin_hive.h" + +namespace MFX +{ + typedef mfxStatus (MFX_CDECL *CreatePluginPtr_t)(mfxPluginUID uid, mfxPlugin* plugin); + + class PluginModule + { + mfxModuleHandle mHmodule; + CreatePluginPtr_t mCreatePluginPtr; + msdk_disp_char mPath[MAX_PLUGIN_PATH]; + + public: + PluginModule(); + PluginModule(const msdk_disp_char * path); + PluginModule(const PluginModule & that) ; + PluginModule & operator = (const PluginModule & that); + bool Create(mfxPluginUID guid, mfxPlugin&); + ~PluginModule(void); + + private: + void Tidy(); + }; + + class MFXPluginFactory { + struct FactoryRecord { + mfxPluginParam plgParams; + PluginModule module; + mfxPlugin plugin; + FactoryRecord () + : plgParams(), plugin() + {} + FactoryRecord(const mfxPluginParam &plgParams, + PluginModule &module, + mfxPlugin plugin) + : plgParams(plgParams) + , module(module) + , plugin(plugin) { + } + }; + MFXVector mPlugins; + mfxU32 nPlugins; + mfxSession mSession; + public: + MFXPluginFactory(mfxSession session); + void Close(); + mfxStatus Create(const PluginDescriptionRecord &); + bool Destroy(const mfxPluginUID &); + + ~MFXPluginFactory(); + protected: + void DestroyPlugin( FactoryRecord & ); + static bool RunVerification( const mfxPlugin & plg, const PluginDescriptionRecord &dsc, mfxPluginParam &pluginParams ); + static bool VerifyEncoder( const mfxVideoCodecPlugin &videoCodec ); + static bool VerifyAudioEncoder( const mfxAudioCodecPlugin &audioCodec ); + static bool VerifyEnc( const mfxVideoCodecPlugin &videoEnc ); + static bool VerifyVpp( const mfxVideoCodecPlugin &videoCodec ); + static bool VerifyDecoder( const mfxVideoCodecPlugin &videoCodec ); + static bool VerifyAudioDecoder( const mfxAudioCodecPlugin &audioCodec ); + static bool VerifyCodecCommon( const mfxVideoCodecPlugin & Video ); + }; +} diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_plugin_cfg_parser.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_plugin_cfg_parser.h new file mode 100644 index 0000000..1e92a5a --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_plugin_cfg_parser.h @@ -0,0 +1,83 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if !defined(__MFX_PLUGIN_CFG_PARSER_H) +#define __MFX_PLUGIN_CFG_PARSER_H + +#include "mfx_dispatcher_defs.h" +#include "mfxplugin.h" +#include "mfx_vector.h" +#include "mfx_plugin_hive.h" +#include +#include +#include + +#pragma once + +namespace MFX +{ + class PluginConfigParser + { + public: + + enum + { + PARSED_TYPE = 1, + PARSED_CODEC_ID = 2, + PARSED_UID = 4, + PARSED_PATH = 8, + PARSED_DEFAULT = 16, + PARSED_VERSION = 32, + PARSED_API_VERSION = 64, + PARSED_NAME = 128, + }; + + explicit PluginConfigParser(const char * name); + ~PluginConfigParser(); + + // Returns current section name if any + bool GetCurrentPluginName(char * pluginName, int nChars); + + template + bool GetCurrentPluginName(char (& pluginName)[N]) + { + return this->GetCurrentPluginName(pluginName, N); + } + + // Tries to advance to the next section in config file + bool AdvanceToNextPlugin(); + // Return to first line of the file + bool Rewind(); + // Enumerates sections in currect file (no section headers - 1 section) + int GetPluginCount(); + // Parses plugin parameters from current section + bool ParsePluginParams(PluginDescriptionRecord & dst, mfxU32 & parsedFields); + + private: + FILE * cfgFile; + fpos_t sectionStart; + + bool ParseSingleParameter(const char * name, char * value, PluginDescriptionRecord & dst, mfxU32 & parsedFields); + }; + + bool parseGUID(const char* src, mfxU8* guid); +} + +#endif // __MFX_PLUGIN_CFG_PARSER_H \ No newline at end of file diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_plugin_hive.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_plugin_hive.h new file mode 100644 index 0000000..5d0520f --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_plugin_hive.h @@ -0,0 +1,109 @@ +// Copyright (c) 2018 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "mfx_dispatcher_defs.h" +#include "mfxplugin.h" +#include "mfx_win_reg_key.h" +#include "mfx_vector.h" +#include +#include +#include + +struct MFX_DISP_HANDLE; + +namespace MFX { + + inline bool operator == (const mfxPluginUID &lhs, const mfxPluginUID & rhs) + { + return !memcmp(lhs.Data, rhs.Data, sizeof(mfxPluginUID)); + } + + inline bool operator != (const mfxPluginUID &lhs, const mfxPluginUID & rhs) + { + return !(lhs == rhs); + } + class PluginDescriptionRecord : public mfxPluginParam + { + public: + msdk_disp_char sPath[MAX_PLUGIN_PATH]; + char sName[MAX_PLUGIN_NAME]; + //used for FS plugins that has poor description + bool onlyVersionRegistered; + bool Default; + PluginDescriptionRecord() + : mfxPluginParam() + , sPath() + , sName() + , onlyVersionRegistered() + , Default() + { + } + }; + + typedef MFXVector MFXPluginStorage; + + class MFXPluginStorageBase : public MFXPluginStorage + { + protected: + mfxVersion mCurrentAPIVersion; + protected: + MFXPluginStorageBase(mfxVersion currentAPIVersion) + : mCurrentAPIVersion(currentAPIVersion) + { + } + void ConvertAPIVersion( mfxU32 APIVersion, PluginDescriptionRecord &descriptionRecord) const + { + descriptionRecord.APIVersion.Minor = static_cast (APIVersion & 0x0ff); + descriptionRecord.APIVersion.Major = static_cast (APIVersion >> 8); + } + }; + + //populated from registry + class MFXPluginsInHive : public MFXPluginStorageBase + { + public: + MFXPluginsInHive(int mfxStorageID, const msdk_disp_char *msdkLibSubKey, mfxVersion currentAPIVersion); + }; + +#if defined(MEDIASDK_USE_CFGFILES) || (!defined(MEDIASDK_UWP_LOADER) && !defined(MEDIASDK_UWP_PROCTABLE)) + //plugins are loaded from FS close to executable + class MFXPluginsInFS : public MFXPluginStorageBase + { + bool mIsVersionParsed; + bool mIsAPIVersionParsed; + public: + MFXPluginsInFS(mfxVersion currentAPIVersion); + private: + bool ParseFile(FILE * f, PluginDescriptionRecord & des); + bool ParseKVPair( msdk_disp_char *key, msdk_disp_char * value, PluginDescriptionRecord & des); + }; +#endif //#if defined(MEDIASDK_USE_CFGFILES) || (!defined(MEDIASDK_UWP_LOADER) && !defined(MEDIASDK_UWP_PROCTABLE)) + + //plugins are loaded from FS close to Runtime library + class MFXDefaultPlugins : public MFXPluginStorageBase + { + public: + MFXDefaultPlugins(mfxVersion currentAPIVersion, MFX_DISP_HANDLE * hdl, int implType); + private: + }; + +} diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_vector.h b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_vector.h new file mode 100644 index 0000000..b320532 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/include/mfx_vector.h @@ -0,0 +1,210 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once +#include "mfxstructures.h" +#include + +namespace MFX +{ + template + class iterator_tmpl + { + template friend class MFXVector; + mfxU32 mIndex; + T* mRecords; + iterator_tmpl(mfxU32 index , T * records) + : mIndex (index) + , mRecords(records) + {} + public: + iterator_tmpl() + : mIndex () + , mRecords() + {} + bool operator ==(const iterator_tmpl & that )const + { + return mIndex == that.mIndex; + } + bool operator !=(const iterator_tmpl & that )const + { + return mIndex != that.mIndex; + } + mfxU32 operator - (const iterator_tmpl &that) const + { + return mIndex - that.mIndex; + } + iterator_tmpl & operator ++() + { + mIndex++; + return * this; + } + iterator_tmpl & operator ++(int) + { + mIndex++; + return * this; + } + T & operator *() + { + return mRecords[mIndex]; + } + T * operator ->() + { + return mRecords + mIndex; + } + }; + + class MFXVectorRangeError : public std::exception + { + }; + + template + class MFXVector + { + T* mRecords; + mfxU32 mNrecords; + public: + MFXVector() + : mRecords() + , mNrecords() + {} + MFXVector(const MFXVector & rhs) + : mRecords() + , mNrecords() + { + insert(end(), rhs.begin(), rhs.end()); + } + MFXVector & operator = (const MFXVector & rhs) + { + if (this != &rhs) + { + clear(); + insert(end(), rhs.begin(), rhs.end()); + } + return *this; + } + virtual ~MFXVector () + { + clear(); + } + typedef iterator_tmpl iterator; + + iterator begin() const + { + return iterator(0u, mRecords); + } + iterator end() const + { + return iterator(mNrecords, mRecords); + } + void insert(iterator where, iterator beg_iter, iterator end_iter) + { + mfxU32 elementsToInsert = (end_iter - beg_iter); + if (!elementsToInsert) + { + return; + } + if (where.mIndex > mNrecords) + { + throw MFXVectorRangeError(); + } + + T *newRecords = new T[mNrecords + elementsToInsert](); + mfxU32 i = 0; + + // save left + for (; i < where.mIndex; i++) + { + newRecords[i] = mRecords[i]; + } + // insert + for (; beg_iter != end_iter; beg_iter++, i++) + { + newRecords[i] = *beg_iter; + } + + //save right + for (; i < mNrecords + elementsToInsert; i++) + { + newRecords[i] = mRecords[i - elementsToInsert]; + } + + delete [] mRecords; + + mRecords = newRecords; + mNrecords = i; + } + T& operator [] (mfxU32 idx) + { + return mRecords[idx]; + } + void push_back(const T& obj) + { + T *newRecords = new T[mNrecords + 1](); + mfxU32 i = 0; + for (; i = mNrecords) + { + throw MFXVectorRangeError(); + } + mNrecords--; + mfxU32 i = at.mIndex; + for (; i != mNrecords; i++) + { + mRecords[i] = mRecords[i+1]; + } + //destroy last element + mRecords[i] = T(); + } + void resize(mfxU32 nSize) + { + T * newRecords = new T[nSize](); + for (mfxU32 i = 0; i +#include + +#include "mfx_dispatcher.h" +#include "mfx_load_dll.h" +#include "mfx_dispatcher_log.h" +#include "mfx_library_iterator.h" +#include "mfx_critical_section.h" + +#include /* for memset on Linux */ + +#include /* for qsort on Linux */ +#include "mfx_load_plugin.h" +#include "mfx_plugin_hive.h" + +// module-local definitions +namespace +{ + + const + struct + { + // instance implementation type + eMfxImplType implType; + // real implementation + mfxIMPL impl; + // adapter numbers + mfxU32 adapterID; + + } implTypes[] = + { + // MFX_IMPL_AUTO case + {MFX_LIB_HARDWARE, MFX_IMPL_HARDWARE, 0}, + {MFX_LIB_SOFTWARE, MFX_IMPL_SOFTWARE, 0}, + + // MFX_IMPL_ANY case + {MFX_LIB_HARDWARE, MFX_IMPL_HARDWARE, 0}, + {MFX_LIB_HARDWARE, MFX_IMPL_HARDWARE2, 1}, + {MFX_LIB_HARDWARE, MFX_IMPL_HARDWARE3, 2}, + {MFX_LIB_HARDWARE, MFX_IMPL_HARDWARE4, 3}, + {MFX_LIB_SOFTWARE, MFX_IMPL_SOFTWARE, 0}, + {MFX_LIB_SOFTWARE, MFX_IMPL_SOFTWARE | MFX_IMPL_AUDIO, 0}, + }; + + const + struct + { + // start index in implTypes table for specified implementation + mfxU32 minIndex; + // last index in implTypes table for specified implementation + mfxU32 maxIndex; + + } implTypesRange[] = + { + {0, 1}, // MFX_IMPL_AUTO + {1, 1}, // MFX_IMPL_SOFTWARE + {0, 0}, // MFX_IMPL_HARDWARE + {2, 6}, // MFX_IMPL_AUTO_ANY + {2, 5}, // MFX_IMPL_HARDWARE_ANY + {3, 3}, // MFX_IMPL_HARDWARE2 + {4, 4}, // MFX_IMPL_HARDWARE3 + {5, 5}, // MFX_IMPL_HARDWARE4 + {2, 6}, // MFX_IMPL_RUNTIME, same as MFX_IMPL_HARDWARE_ANY + {7, 7} // MFX_IMPL_AUDIO + }; + + MFX::mfxCriticalSection dispGuard = 0; + +} // namespace + +using namespace MFX; + +#if defined(MEDIASDK_UWP_LOADER) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + // + // intel_gfx_api-*.dll calls these functions to do not mix MFXInitEx exposed + // from dispatcher_proc_table.lib with the libmfx[hw/sw] engines' call MFXInitEx + // + + mfxStatus InitMediaSDKSession(mfxInitParam par, mfxSession* session) + { + return MFXInitEx(par, session); + } + + mfxStatus DisposeMediaSDKSession(mfxSession session) + { + return MFXClose(session); + } + +#ifdef __cplusplus +}; //extern "C" +#endif /* __cplusplus */ + +#endif // defined(MEDIASDK_UWP_LOADER) + +#if !defined(MEDIASDK_UWP_PROCTABLE) + +// +// Implement DLL exposed functions. MFXInit and MFXClose have to do +// slightly more than other. They require to be implemented explicitly. +// All other functions are implemented implicitly. +// + +typedef MFXVector HandleVector; +typedef MFXVector StatusVector; + +struct VectorHandleGuard +{ + VectorHandleGuard(HandleVector& aVector): m_vector(aVector) {} + ~VectorHandleGuard() + { + HandleVector::iterator it = m_vector.begin(), + et = m_vector.end(); + for ( ; it != et; ++it) + { + delete *it; + } + } + + HandleVector& m_vector; +private: + void operator=(const VectorHandleGuard&); +}; + + +int HandleSort (const void * plhs, const void * prhs) +{ + const MFX_DISP_HANDLE * lhs = *(const MFX_DISP_HANDLE **)plhs; + const MFX_DISP_HANDLE * rhs = *(const MFX_DISP_HANDLE **)prhs; + + if (lhs->actualApiVersion < rhs->actualApiVersion) + { + return -1; + } + if (rhs->actualApiVersion < lhs->actualApiVersion) + { + return 1; + } + + // if versions are equal prefer library with HW + if (lhs->loadStatus == MFX_WRN_PARTIAL_ACCELERATION && rhs->loadStatus == MFX_ERR_NONE) + { + return 1; + } + if (lhs->loadStatus == MFX_ERR_NONE && rhs->loadStatus == MFX_WRN_PARTIAL_ACCELERATION) + { + return -1; + } + + return 0; +} + +// for LEGACY and UWP_LOADER purposes implementation of MFXinitEx is traditionally loading +// required libmfx*.dll and fill the array of API functions' with corresponded pointers to instantiated libmfx*.dll + +mfxStatus MFXInitEx(mfxInitParam par, mfxSession *session) +{ + MFX::MFXAutomaticCriticalSection guard(&dispGuard); + + DISPATCHER_LOG_BLOCK( ("MFXInitEx (impl=%s, pVer=%d.%d, ExternalThreads=%d session=0x%p\n" + , DispatcherLog_GetMFXImplString(par.Implementation).c_str() + , par.Version.Major + , par.Version.Minor + , par.ExternalThreads + , session)); + + mfxStatus mfxRes; + HandleVector allocatedHandle; + VectorHandleGuard handleGuard(allocatedHandle); + + MFX_DISP_HANDLE *pHandle; + msdk_disp_char dllName[MFX_MAX_DLL_PATH] = { 0 }; + MFX::MFXLibraryIterator libIterator; + + // there iterators are used only if the caller specified implicit type like AUTO + mfxU32 curImplIdx, maxImplIdx; + // particular implementation value + mfxIMPL curImpl; + // implementation method masked from the input parameter + // special case for audio library + const mfxIMPL implMethod = (par.Implementation & MFX_IMPL_AUDIO) ? (sizeof(implTypesRange) / sizeof(implTypesRange[0]) - 1) : (par.Implementation & (MFX_IMPL_VIA_ANY - 1)); + + // implementation interface masked from the input parameter + mfxIMPL implInterface = par.Implementation & -MFX_IMPL_VIA_ANY; + mfxIMPL implInterfaceOrig = implInterface; + mfxVersion requiredVersion = {{MFX_VERSION_MINOR, MFX_VERSION_MAJOR}}; + + // check error(s) + if (NULL == session) + { + return MFX_ERR_NULL_PTR; + } + if (((MFX_IMPL_AUTO > implMethod) || (MFX_IMPL_RUNTIME < implMethod)) && !(par.Implementation & MFX_IMPL_AUDIO)) + { + return MFX_ERR_UNSUPPORTED; + } + + // set the minimal required version + requiredVersion = par.Version; + + try + { + // reset the session value + *session = 0; + + // allocate the dispatching handle and call-table + pHandle = new MFX_DISP_HANDLE(requiredVersion); + } + catch(...) + { + return MFX_ERR_MEMORY_ALLOC; + } + + DISPATCHER_LOG_INFO((("Required API version is %u.%u\n"), requiredVersion.Major, requiredVersion.Minor)); + + // Load HW library or RT from system location + curImplIdx = implTypesRange[implMethod].minIndex; + maxImplIdx = implTypesRange[implMethod].maxIndex; + do + { + int currentStorage = MFX::MFX_STORAGE_ID_FIRST; + implInterface = implInterfaceOrig; + do + { + // initialize the library iterator + mfxRes = libIterator.Init(implTypes[curImplIdx].implType, + implInterface, + implTypes[curImplIdx].adapterID, + currentStorage); + + // look through the list of installed SDK version, + // looking for a suitable library with higher merit value. + if (MFX_ERR_NONE == mfxRes) + { + + if ( + MFX_LIB_HARDWARE == implTypes[curImplIdx].implType + && (!implInterface + || MFX_IMPL_VIA_ANY == implInterface)) + { + implInterface = libIterator.GetImplementationType(); + } + + do + { + eMfxImplType implType = implTypes[curImplIdx].implType; + + // select a desired DLL + mfxRes = libIterator.SelectDLLVersion(dllName, + sizeof(dllName) / sizeof(dllName[0]), + &implType, + pHandle->apiVersion); + if (MFX_ERR_NONE != mfxRes) + { + break; + } + DISPATCHER_LOG_INFO((("loading library %S\n"), MSDK2WIDE(dllName))); + // try to load the selected DLL + curImpl = implTypes[curImplIdx].impl; + mfxRes = pHandle->LoadSelectedDLL(dllName, implType, curImpl, implInterface, par); + // unload the failed DLL + if (MFX_ERR_NONE != mfxRes) + { + pHandle->Close(); + } + else + { + libIterator.GetSubKeyName(pHandle->subkeyName, sizeof(pHandle->subkeyName) / sizeof(pHandle->subkeyName[0])); + pHandle->storageID = libIterator.GetStorageID(); + allocatedHandle.push_back(pHandle); + pHandle = new MFX_DISP_HANDLE(requiredVersion); + } + + } while (MFX_ERR_NONE != mfxRes); + } + + // select another place for loading engine + currentStorage += 1; + + } while ((MFX_ERR_NONE != mfxRes) && (MFX::MFX_STORAGE_ID_LAST >= currentStorage)); + + } while ((MFX_ERR_NONE != mfxRes) && (++curImplIdx <= maxImplIdx)); + + + curImplIdx = implTypesRange[implMethod].minIndex; + maxImplIdx = implTypesRange[implMethod].maxIndex; + + // SOLID dispatcher checks if there are other available media sdk engines implementations in working dir + // UWP dispatcher does not use libraries other than in System32 folder +#if !defined(MEDIASDK_UWP_LOADER) + // Load RT from app folder (libmfxsw64 with API >= 1.10) + do + { + implInterface = implInterfaceOrig; + // initialize the library iterator + mfxRes = libIterator.Init(implTypes[curImplIdx].implType, + implInterface, + implTypes[curImplIdx].adapterID, + MFX::MFX_APP_FOLDER); + + if (MFX_ERR_NONE == mfxRes) + { + + if ( + MFX_LIB_HARDWARE == implTypes[curImplIdx].implType + && (!implInterface + || MFX_IMPL_VIA_ANY == implInterface)) + { + implInterface = libIterator.GetImplementationType(); + } + + do + { + eMfxImplType implType; + + // select a desired DLL + mfxRes = libIterator.SelectDLLVersion(dllName, + sizeof(dllName) / sizeof(dllName[0]), + &implType, + pHandle->apiVersion); + if (MFX_ERR_NONE != mfxRes) + { + break; + } + DISPATCHER_LOG_INFO((("loading library %S\n"), MSDK2WIDE(dllName))); + + // try to load the selected DLL + curImpl = implTypes[curImplIdx].impl; + mfxRes = pHandle->LoadSelectedDLL(dllName, implType, curImpl, implInterface, par); + // unload the failed DLL + if (MFX_ERR_NONE != mfxRes) + { + pHandle->Close(); + } + else + { + if (pHandle->actualApiVersion.Major == 1 && pHandle->actualApiVersion.Minor <= 9) + { + // this is not RT, skip it + mfxRes = MFX_ERR_ABORTED; + break; + } + pHandle->storageID = MFX::MFX_UNKNOWN_KEY; + allocatedHandle.push_back(pHandle); + pHandle = new MFX_DISP_HANDLE(requiredVersion); + } + + } while (MFX_ERR_NONE != mfxRes); + } + } while ((MFX_ERR_NONE != mfxRes) && (++curImplIdx <= maxImplIdx)); + +#endif // !defined(MEDIASDK_UWP_LOADER) + + // Load HW and SW libraries using legacy default DLL search mechanism + // set current library index again + curImplIdx = implTypesRange[implMethod].minIndex; + do + { + implInterface = implInterfaceOrig; + + if (par.Implementation & MFX_IMPL_AUDIO) + { + mfxRes = MFX::mfx_get_default_audio_dll_name(dllName, + sizeof(dllName) / sizeof(dllName[0]), + implTypes[curImplIdx].implType); + } + else + { + mfxRes = MFX::mfx_get_default_dll_name(dllName, + sizeof(dllName) / sizeof(dllName[0]), + implTypes[curImplIdx].implType); + } + + if (MFX_ERR_NONE == mfxRes) + { + DISPATCHER_LOG_INFO((("loading default library %S\n"), MSDK2WIDE(dllName))) + + // try to load the selected DLL using default DLL search mechanism + if (MFX_LIB_HARDWARE == implTypes[curImplIdx].implType) + { + if (!implInterface) + { + implInterface = MFX_IMPL_VIA_ANY; + } + mfxU32 curVendorID = 0, curDeviceID = 0; + mfxRes = MFX::SelectImplementationType(implTypes[curImplIdx].adapterID, &implInterface, &curVendorID, &curDeviceID); + if (curVendorID != INTEL_VENDOR_ID) + mfxRes = MFX_ERR_UNKNOWN; + } + if (MFX_ERR_NONE == mfxRes) + { + // try to load the selected DLL using default DLL search mechanism + mfxRes = pHandle->LoadSelectedDLL(dllName, + implTypes[curImplIdx].implType, + implTypes[curImplIdx].impl, + implInterface, + par); + } + // unload the failed DLL + if ((MFX_ERR_NONE != mfxRes) && + (MFX_WRN_PARTIAL_ACCELERATION != mfxRes)) + { + pHandle->Close(); + } + else + { + pHandle->storageID = MFX::MFX_UNKNOWN_KEY; + allocatedHandle.push_back(pHandle); + pHandle = new MFX_DISP_HANDLE(requiredVersion); + } + } + } + while ((MFX_ERR_NONE > mfxRes) && (++curImplIdx <= maxImplIdx)); + delete pHandle; + + if (allocatedHandle.size() == 0) + return MFX_ERR_UNSUPPORTED; + + { // sort candidate list + bool NeedSort = false; + HandleVector::iterator first = allocatedHandle.begin(), + it = allocatedHandle.begin(), + et = allocatedHandle.end(); + for (it++; it != et; ++it) + if (HandleSort(&(*first), &(*it)) != 0) + NeedSort = true; + + // select dll with version with lowest version number still greater or equal to requested + if (NeedSort) + qsort(&(*allocatedHandle.begin()), allocatedHandle.size(), sizeof(MFX_DISP_HANDLE*), &HandleSort); + } + HandleVector::iterator candidate = allocatedHandle.begin(); + // check the final result of loading + try + { + pHandle = *candidate; + //pulling up current mediasdk version, that required to match plugin version + mfxVersion apiVerActual = { 0 }; + mfxStatus stsQueryVersion = MFXQueryVersion((mfxSession)pHandle, &apiVerActual); + + if (MFX_ERR_NONE != stsQueryVersion) + { + DISPATCHER_LOG_ERROR((("MFXQueryVersion returned: %d, cannot load plugins\n"), mfxRes)) + } + else + { + MFX::MFXPluginStorage & hive = pHandle->pluginHive; + + HandleVector::iterator it = allocatedHandle.begin(), + et = allocatedHandle.end(); + for (; it != et; ++it) + { + // Registering default plugins set + MFX::MFXDefaultPlugins defaultPugins(apiVerActual, *it, (*it)->implType); + hive.insert(hive.end(), defaultPugins.begin(), defaultPugins.end()); + + if ((*it)->storageID != MFX::MFX_UNKNOWN_KEY) + { + // Scan HW plugins in subkeys of registry library + MFX::MFXPluginsInHive plgsInHive((*it)->storageID, (*it)->subkeyName, apiVerActual); + hive.insert(hive.end(), plgsInHive.begin(), plgsInHive.end()); + } + } + + //setting up plugins records + for(int i = MFX::MFX_STORAGE_ID_FIRST; i <= MFX::MFX_STORAGE_ID_LAST; i++) + { + MFX::MFXPluginsInHive plgsInHive(i, NULL, apiVerActual); + hive.insert(hive.end(), plgsInHive.begin(), plgsInHive.end()); + } + +#if defined(MEDIASDK_USE_CFGFILES) || !defined(MEDIASDK_UWP_LOADER) + // SOLID dispatcher also loads plug-ins from file system + MFX::MFXPluginsInFS plgsInFS(apiVerActual); + hive.insert(hive.end(), plgsInFS.begin(), plgsInFS.end()); +#endif // defined(MEDIASDK_USE_CFGFILES) || !defined(MEDIASDK_UWP_LOADER) + } + + // UWP dispatcher uses stubs + pHandle->callPlugInsTable[eMFXVideoUSER_Load] = (mfxFunctionPointer)MFXVideoUSER_Load; + pHandle->callPlugInsTable[eMFXVideoUSER_LoadByPath] = (mfxFunctionPointer)MFXVideoUSER_LoadByPath; + pHandle->callPlugInsTable[eMFXVideoUSER_UnLoad] = (mfxFunctionPointer)MFXVideoUSER_UnLoad; + pHandle->callPlugInsTable[eMFXAudioUSER_Load] = (mfxFunctionPointer)MFXAudioUSER_Load; + pHandle->callPlugInsTable[eMFXAudioUSER_UnLoad] = (mfxFunctionPointer)MFXAudioUSER_UnLoad; + + } + catch(...) + { + DISPATCHER_LOG_ERROR((("unknown exception while loading plugins\n"))) + } + + // everything is OK. Save pointers to the output variable + *candidate = 0; // keep this one safe from guard destructor + *((MFX_DISP_HANDLE **) session) = pHandle; + + return pHandle->loadStatus; + +} // mfxStatus MFXInitEx(mfxIMPL impl, mfxVersion *ver, mfxSession *session) + +mfxStatus MFXClose(mfxSession session) +{ + MFX::MFXAutomaticCriticalSection guard(&dispGuard); + + mfxStatus mfxRes = MFX_ERR_INVALID_HANDLE; + MFX_DISP_HANDLE *pHandle = (MFX_DISP_HANDLE *) session; + + // check error(s) + if (pHandle) + { + try + { + // unload the DLL library + mfxRes = pHandle->Close(); + + // it is possible, that there is an active child session. + // can't unload library in that case. + if (MFX_ERR_UNDEFINED_BEHAVIOR != mfxRes) + { + // release the handle + delete pHandle; + } + } + catch(...) + { + mfxRes = MFX_ERR_INVALID_HANDLE; + } + } + + return mfxRes; + +} // mfxStatus MFXClose(mfxSession session) + +mfxStatus MFXVideoUSER_Load(mfxSession session, const mfxPluginUID *uid, mfxU32 version) +{ + mfxStatus sts = MFX_ERR_NONE; + bool ErrFlag = false; + if (!session) { + DISPATCHER_LOG_ERROR((("MFXVideoUSER_Load: session=NULL\n"))); + return MFX_ERR_NULL_PTR; + } + MFX_DISP_HANDLE &pHandle = *(MFX_DISP_HANDLE *) session; + + if (!uid) + { + DISPATCHER_LOG_ERROR((("MFXVideoUSER_Load: uid=NULL\n"))); + return MFX_ERR_NULL_PTR; + } + DISPATCHER_LOG_INFO((("MFXVideoUSER_Load: uid=" MFXGUIDTYPE()" version=%d\n") + , MFXGUIDTOHEX(uid) + , version)) + size_t pluginsChecked = 0; + + for (MFX::MFXPluginStorage::iterator i = pHandle.pluginHive.begin();i != pHandle.pluginHive.end(); i++, pluginsChecked++) + { + if (i->PluginUID != *uid) + { + continue; + } + //check rest in records + if (i->PluginVersion < version) + { + DISPATCHER_LOG_INFO((("MFXVideoUSER_Load: registered \"Plugin Version\" for GUID=" MFXGUIDTYPE()" is %d, that is smaller that requested\n") + , MFXGUIDTOHEX(uid) + , i->PluginVersion)) + continue; + } + try + { + sts = pHandle.pluginFactory.Create(*i); + if( MFX_ERR_NONE != sts) + { + ErrFlag = (ErrFlag || (sts == MFX_ERR_UNDEFINED_BEHAVIOR)); + continue; + } + return MFX_ERR_NONE; + } + catch(...) + { + continue; + } + } + + // Specified UID was not found among individually registed plugins, now try load it from default sets if any + for (MFX::MFXPluginStorage::iterator i = pHandle.pluginHive.begin();i != pHandle.pluginHive.end(); i++, pluginsChecked++) + { + if (!i->Default) + continue; + + i->PluginUID = *uid; + i->PluginVersion = (mfxU16)version; + try + { + sts = pHandle.pluginFactory.Create(*i); + if( MFX_ERR_NONE != sts) + { + ErrFlag = (ErrFlag || (sts == MFX_ERR_UNDEFINED_BEHAVIOR)); + continue; + } + return MFX_ERR_NONE; + } + catch(...) + { + continue; + } + } + + DISPATCHER_LOG_ERROR((("MFXVideoUSER_Load: cannot find registered plugin with requested UID, total plugins available=%d\n"), pHandle.pluginHive.size())); + if (ErrFlag) + return MFX_ERR_UNDEFINED_BEHAVIOR; + else + return MFX_ERR_NOT_FOUND; +} + + +mfxStatus MFXVideoUSER_LoadByPath(mfxSession session, const mfxPluginUID *uid, mfxU32 version, const mfxChar *path, mfxU32 len) +{ + if (!session) + { + DISPATCHER_LOG_ERROR((("MFXVideoUSER_LoadByPath: session=NULL\n"))); + return MFX_ERR_NULL_PTR; + } + MFX_DISP_HANDLE &pHandle = *(MFX_DISP_HANDLE *) session; + if (!uid) + { + DISPATCHER_LOG_ERROR((("MFXVideoUSER_LoadByPath: uid=NULL\n"))); + return MFX_ERR_NULL_PTR; + } + + DISPATCHER_LOG_INFO((("MFXVideoUSER_LoadByPath: %S uid=" MFXGUIDTYPE()" version=%d\n") + , MSDK2WIDE(path) + , MFXGUIDTOHEX(uid) + , version)) + + PluginDescriptionRecord record; + record.sName[0] = 0; + + msdk_disp_char_cpy_s(record.sPath, MAX_PLUGIN_PATH, path); + + record.PluginUID = *uid; + record.PluginVersion = (mfxU16)version; + record.Default = true; + + try + { + return pHandle.pluginFactory.Create(record); + } + catch(...) + { + return MFX_ERR_NOT_FOUND; + } +} + + +mfxStatus MFXVideoUSER_UnLoad(mfxSession session, const mfxPluginUID *uid) +{ + if (!session) + { + DISPATCHER_LOG_ERROR((("MFXVideoUSER_UnLoad: session=NULL\n"))); + return MFX_ERR_NULL_PTR; + } + MFX_DISP_HANDLE &rHandle = *(MFX_DISP_HANDLE *) session; + if (!uid) + { + DISPATCHER_LOG_ERROR((("MFXVideoUSER_UnLoad: uid=NULL\n"))); + return MFX_ERR_NULL_PTR; + } + + bool bDestroyed = rHandle.pluginFactory.Destroy(*uid); + if (bDestroyed) + { + DISPATCHER_LOG_INFO((("MFXVideoUSER_UnLoad : plugin with GUID=" MFXGUIDTYPE()" unloaded\n"), MFXGUIDTOHEX(uid))); + } else + { + DISPATCHER_LOG_ERROR((("MFXVideoUSER_UnLoad : plugin with GUID=" MFXGUIDTYPE()" not found\n"), MFXGUIDTOHEX(uid))); + } + + return bDestroyed ? MFX_ERR_NONE : MFX_ERR_NOT_FOUND; +} + +mfxStatus MFXAudioUSER_Load(mfxSession session, const mfxPluginUID *uid, mfxU32 version) +{ + if (!session) + { + DISPATCHER_LOG_ERROR((("MFXAudioUSER_Load: session=NULL\n"))); + return MFX_ERR_NULL_PTR; + } + MFX_DISP_HANDLE &pHandle = *(MFX_DISP_HANDLE *) session; + if (!uid) + { + DISPATCHER_LOG_ERROR((("MFXAudioUSER_Load: uid=NULL\n"))); + return MFX_ERR_NULL_PTR; + } + DISPATCHER_LOG_INFO((("MFXAudioUSER_Load: uid=" MFXGUIDTYPE()" version=%d\n") + , MFXGUIDTOHEX(uid) + , version)) + size_t pluginsChecked = 0; + PluginDescriptionRecord defaultPluginRecord; + for (MFX::MFXPluginStorage::iterator i = pHandle.pluginHive.begin();i != pHandle.pluginHive.end(); i++, pluginsChecked++) + { + if (i->PluginUID != *uid) + { + if (i->Default) // PluginUID == 0 for default set + { + defaultPluginRecord = *i; + } + continue; + } + //check rest in records + if (i->PluginVersion < version) + { + DISPATCHER_LOG_INFO((("MFXAudioUSER_Load: registered \"Plugin Version\" for GUID=" MFXGUIDTYPE()" is %d, that is smaller that requested\n") + , MFXGUIDTOHEX(uid) + , i->PluginVersion)) + continue; + } + try { + return pHandle.pluginFactory.Create(*i); + } + catch(...) { + return MFX_ERR_UNKNOWN; + } + } + + // Specified UID was not found among individually registed plugins, now try load it from default set if any + if (defaultPluginRecord.Default) + { + defaultPluginRecord.PluginUID = *uid; + defaultPluginRecord.onlyVersionRegistered = true; + defaultPluginRecord.PluginVersion = (mfxU16)version; + try { + return pHandle.pluginFactory.Create(defaultPluginRecord); + } + catch(...) { + return MFX_ERR_UNKNOWN; + } + } + + DISPATCHER_LOG_ERROR((("MFXAudioUSER_Load: cannot find registered plugin with requested UID, total plugins available=%d\n"), pHandle.pluginHive.size())); + return MFX_ERR_NOT_FOUND; +} + +mfxStatus MFXAudioUSER_UnLoad(mfxSession session, const mfxPluginUID *uid) +{ + if (!session) + { + DISPATCHER_LOG_ERROR((("MFXAudioUSER_UnLoad: session=NULL\n"))); + return MFX_ERR_NULL_PTR; + } + MFX_DISP_HANDLE &rHandle = *(MFX_DISP_HANDLE *) session; + if (!uid) + { + DISPATCHER_LOG_ERROR((("MFXAudioUSER_Load: uid=NULL\n"))); + return MFX_ERR_NULL_PTR; + } + + bool bDestroyed = rHandle.pluginFactory.Destroy(*uid); + if (bDestroyed) + { + DISPATCHER_LOG_INFO((("MFXAudioUSER_UnLoad : plugin with GUID=" MFXGUIDTYPE()" unloaded\n"), MFXGUIDTOHEX(uid))); + } else + { + DISPATCHER_LOG_ERROR((("MFXAudioUSER_UnLoad : plugin with GUID=" MFXGUIDTYPE()" not found\n"), MFXGUIDTOHEX(uid))); + } + + return bDestroyed ? MFX_ERR_NONE : MFX_ERR_NOT_FOUND; +} +#else // relates to !defined (MEDIASDK_UWP_PROCTABLE) from line 137, i.e. #else part as if MEDIASDK_UWP_PROCTABLE defined + +#include +#include "intel_api_factory.h" + +// for the UWP_PROCTABLE purposes implementation of MFXinitEx is calling +// InitializeInstance() implemented in intel_uwp-api.dll +mfxStatus MFXInitEx(mfxInitParam par, mfxSession *session) +{ + HRESULT hr = InitialiseMediaSession((HANDLE*)session, &par, nullptr); + return (hr == S_OK) ? mfxStatus::MFX_ERR_NONE : (mfxStatus)hr; +} + +// for the UWP_PROCTABLE purposes implementation of MFXClose is calling +// DisposeInstance() implemented in intel_uwp-api.dll +mfxStatus MFXClose(mfxSession session) +{ + if (nullptr == session) { + return MFX_ERR_INVALID_HANDLE; + } + + HRESULT hr = DisposeMediaSession(HANDLE(session)); + session = (mfxSession)NULL; + return (hr == S_OK) ? MFX_ERR_NONE : mfxStatus(hr); +} + +#undef FUNCTION +#define FUNCTION(return_value, func_name, formal_param_list, actual_param_list) \ + return_value func_name formal_param_list \ +{ \ + mfxStatus mfxRes = MFX_ERR_INVALID_HANDLE; \ +\ + _mfxSession *pHandle = (_mfxSession *) session; \ +\ + /* get the function's address and make a call */ \ + if (pHandle) \ +{ \ + mfxFunctionPointer pFunc = pHandle->callPlugInsTable[e##func_name]; \ + if (pFunc) \ +{ \ + /* pass down the call */ \ + mfxRes = (*(mfxStatus (MFX_CDECL *) formal_param_list) pFunc) actual_param_list; \ +} \ +} \ + return mfxRes; \ +} + +FUNCTION(mfxStatus, MFXVideoUSER_Load, (mfxSession session, const mfxPluginUID *uid, mfxU32 version), (session, uid, version)) +FUNCTION(mfxStatus, MFXVideoUSER_LoadByPath, (mfxSession session, const mfxPluginUID *uid, mfxU32 version, const mfxChar *path, mfxU32 len), (session, uid, version, path, len)) +FUNCTION(mfxStatus, MFXVideoUSER_UnLoad, (mfxSession session, const mfxPluginUID *uid), (session, uid)) +FUNCTION(mfxStatus, MFXAudioUSER_Load, (mfxSession session, const mfxPluginUID *uid, mfxU32 version), (session, uid, version)) +FUNCTION(mfxStatus, MFXAudioUSER_UnLoad, (mfxSession session, const mfxPluginUID *uid), (session, uid)) + +#endif //!defined(MEDIASDK_UWP_PROCTABLE) + + +#if !defined(MEDIASDK_UWP_LOADER) + +mfxStatus MFXJoinSession(mfxSession session, mfxSession child_session) +{ + mfxStatus mfxRes = MFX_ERR_INVALID_HANDLE; + MFX_DISP_HANDLE *pHandle = (MFX_DISP_HANDLE *)session; + MFX_DISP_HANDLE *pChildHandle = (MFX_DISP_HANDLE *)child_session; + + // get the function's address and make a call + if ((pHandle) && (pChildHandle) && (pHandle->apiVersion == pChildHandle->apiVersion)) + { + /* check whether it is audio session or video */ + int tableIndex = eMFXJoinSession; + mfxFunctionPointer pFunc; + if (pHandle->impl & MFX_IMPL_AUDIO) + { + pFunc = pHandle->callAudioTable[tableIndex]; + } + else + { + pFunc = pHandle->callTable[tableIndex]; + } + + if (pFunc) + { + // pass down the call + mfxRes = (*(mfxStatus(MFX_CDECL *) (mfxSession, mfxSession)) pFunc) (pHandle->session, + pChildHandle->session); + } + } + + return mfxRes; + +} // mfxStatus MFXJoinSession(mfxSession session, mfxSession child_session) + +mfxStatus MFXCloneSession(mfxSession session, mfxSession *clone) +{ + mfxStatus mfxRes = MFX_ERR_INVALID_HANDLE; + MFX_DISP_HANDLE *pHandle = (MFX_DISP_HANDLE *)session; + mfxVersion apiVersion; + mfxIMPL impl; + + // check error(s) + if (pHandle) + { + // initialize the clone session + apiVersion = pHandle->apiVersion; + impl = pHandle->impl | pHandle->implInterface; + mfxRes = MFXInit(impl, &apiVersion, clone); + if (MFX_ERR_NONE != mfxRes) + { + return mfxRes; + } + + // join the sessions + mfxRes = MFXJoinSession(session, *clone); + if (MFX_ERR_NONE != mfxRes) + { + MFXClose(*clone); + *clone = NULL; + return mfxRes; + } + } + + return mfxRes; + +} // mfxStatus MFXCloneSession(mfxSession session, mfxSession *clone) + +#endif // !defined(MEDIASDK_UWP_LOADER) + +mfxStatus MFXInit(mfxIMPL impl, mfxVersion *pVer, mfxSession *session) +{ + mfxInitParam par = {}; + + par.Implementation = impl; + if (pVer) + { + par.Version = *pVer; + } + else + { + par.Version.Major = DEFAULT_API_VERSION_MAJOR; + par.Version.Minor = DEFAULT_API_VERSION_MINOR; + } + par.ExternalThreads = 0; + + return MFXInitEx(par, session); +} + +// +// +// implement all other calling functions. +// They just call a procedure of DLL library from the table. +// + +// define for common functions (from mfxsession.h) +#undef FUNCTION +#define FUNCTION(return_value, func_name, formal_param_list, actual_param_list) \ + return_value func_name formal_param_list \ +{ \ + mfxStatus mfxRes = MFX_ERR_INVALID_HANDLE; \ + _mfxSession *pHandle = (_mfxSession *) session; \ + /* get the function's address and make a call */ \ + if (pHandle) \ +{ \ + /* check whether it is audio session or video */ \ + int tableIndex = e##func_name; \ + mfxFunctionPointer pFunc; \ + if (pHandle->impl & MFX_IMPL_AUDIO) \ +{ \ + pFunc = pHandle->callAudioTable[tableIndex]; \ +} \ + else \ +{ \ + pFunc = pHandle->callTable[tableIndex]; \ +} \ + if (pFunc) \ +{ \ + /* get the real session pointer */ \ + session = pHandle->session; \ + /* pass down the call */ \ + mfxRes = (*(mfxStatus (MFX_CDECL *) formal_param_list) pFunc) actual_param_list; \ +} \ +} \ + return mfxRes; \ +} + +FUNCTION(mfxStatus, MFXQueryIMPL, (mfxSession session, mfxIMPL *impl), (session, impl)) +FUNCTION(mfxStatus, MFXQueryVersion, (mfxSession session, mfxVersion *version), (session, version)) + +#if !defined(MEDIASDK_UWP_LOADER) +// these functions are not necessary in LOADER part of dispatcher and +// need to be included only in in SOLID dispatcher or PROCTABLE part of dispatcher + +FUNCTION(mfxStatus, MFXDisjoinSession, (mfxSession session), (session)) +FUNCTION(mfxStatus, MFXSetPriority, (mfxSession session, mfxPriority priority), (session, priority)) +FUNCTION(mfxStatus, MFXGetPriority, (mfxSession session, mfxPriority *priority), (session, priority)) + +#endif // !defined(MEDIASDK_UWP_LOADER) + +#undef FUNCTION +#define FUNCTION(return_value, func_name, formal_param_list, actual_param_list) \ + return_value func_name formal_param_list \ +{ \ + mfxStatus mfxRes = MFX_ERR_INVALID_HANDLE; \ + _mfxSession *pHandle = (_mfxSession *) session;\ + /* get the function's address and make a call */ \ + if (pHandle) \ +{ \ + mfxFunctionPointer pFunc = pHandle->callTable[e##func_name]; \ + if (pFunc) \ +{ \ + /* get the real session pointer */ \ + session = pHandle->session; \ + /* pass down the call */ \ + mfxRes = (*(mfxStatus (MFX_CDECL *) formal_param_list) pFunc) actual_param_list; \ +} \ +} \ + return mfxRes; \ +} + +#include "mfx_exposed_functions_list.h" +#undef FUNCTION +#define FUNCTION(return_value, func_name, formal_param_list, actual_param_list) \ + return_value func_name formal_param_list \ +{ \ + mfxStatus mfxRes = MFX_ERR_INVALID_HANDLE; \ + _mfxSession *pHandle = (_mfxSession *) session; \ + /* get the function's address and make a call */ \ + if (pHandle) \ +{ \ + mfxFunctionPointer pFunc = pHandle->callAudioTable[e##func_name]; \ + if (pFunc) \ +{ \ + /* get the real session pointer */ \ + session = pHandle->session; \ + /* pass down the call */ \ + mfxRes = (*(mfxStatus (MFX_CDECL *) formal_param_list) pFunc) actual_param_list; \ +} \ +} \ + return mfxRes; \ +} + +#include "mfxaudio_exposed_functions_list.h" diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_critical_section.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_critical_section.cpp new file mode 100644 index 0000000..2cbb8b5 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_critical_section.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "mfx_critical_section.h" + diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_critical_section_linux.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_critical_section_linux.cpp new file mode 100644 index 0000000..7748fae --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_critical_section_linux.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +#include "mfx_critical_section.h" +#include + +#define MFX_WAIT() sched_yield() + +// static section of the file +namespace +{ + +enum +{ + MFX_SC_IS_FREE = 0, + MFX_SC_IS_TAKEN = 1 +}; + +} // namespace + +namespace MFX +{ + +mfxU32 mfxInterlockedCas32(mfxCriticalSection *pCSection, mfxU32 value_to_exchange, mfxU32 value_to_compare) +{ + mfxU32 previous_value; + + asm volatile ("lock; cmpxchgl %1,%2" + : "=a" (previous_value) + : "r" (value_to_exchange), "m" (*pCSection), "0" (value_to_compare) + : "memory", "cc"); + return previous_value; +} + +mfxU32 mfxInterlockedXchg32(mfxCriticalSection *pCSection, mfxU32 value) +{ + mfxU32 previous_value = value; + + asm volatile ("lock; xchgl %0,%1" + : "=r" (previous_value), "+m" (*pCSection) + : "0" (previous_value)); + return previous_value; +} + +void mfxEnterCriticalSection(mfxCriticalSection *pCSection) +{ + while (MFX_SC_IS_TAKEN == mfxInterlockedCas32(pCSection, + MFX_SC_IS_TAKEN, + MFX_SC_IS_FREE)) + { + MFX_WAIT(); + } +} // void mfxEnterCriticalSection(mfxCriticalSection *pCSection) + +void mfxLeaveCriticalSection(mfxCriticalSection *pCSection) +{ + mfxInterlockedXchg32(pCSection, MFX_SC_IS_FREE); +} // void mfxLeaveCriticalSection(mfxCriticalSection *pCSection) + +} // namespace MFX + diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dispatcher.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dispatcher.cpp new file mode 100644 index 0000000..cb92081 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dispatcher.cpp @@ -0,0 +1,328 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "mfx_dispatcher.h" +#include "mfx_dispatcher_log.h" +#include "mfx_load_dll.h" + +#include + +#include + #include + #include + + +MFX_DISP_HANDLE::MFX_DISP_HANDLE(const mfxVersion requiredVersion) : + _mfxSession() + ,apiVersion(requiredVersion) + ,pluginFactory((mfxSession)this) +{ + actualApiVersion.Version = 0; + implType = MFX_LIB_SOFTWARE; + impl = MFX_IMPL_SOFTWARE; + loadStatus = MFX_ERR_NOT_FOUND; + dispVersion.Major = MFX_DISPATCHER_VERSION_MAJOR; + dispVersion.Minor = MFX_DISPATCHER_VERSION_MINOR; + storageID = 0; + implInterface = MFX_IMPL_HARDWARE_ANY; + + hModule = (mfxModuleHandle) 0; + +} // MFX_DISP_HANDLE::MFX_DISP_HANDLE(const mfxVersion requiredVersion) + +MFX_DISP_HANDLE::~MFX_DISP_HANDLE(void) +{ + Close(); + +} // MFX_DISP_HANDLE::~MFX_DISP_HANDLE(void) + +mfxStatus MFX_DISP_HANDLE::Close(void) +{ + mfxStatus mfxRes; + + mfxRes = UnLoadSelectedDLL(); + + // the library wasn't unloaded + if (MFX_ERR_NONE == mfxRes) + { + implType = MFX_LIB_SOFTWARE; + impl = MFX_IMPL_SOFTWARE; + loadStatus = MFX_ERR_NOT_FOUND; + dispVersion.Major = MFX_DISPATCHER_VERSION_MAJOR; + dispVersion.Minor = MFX_DISPATCHER_VERSION_MINOR; + *static_cast<_mfxSession*>(this) = _mfxSession(); + hModule = (mfxModuleHandle) 0; + } + + return mfxRes; + +} // mfxStatus MFX_DISP_HANDLE::Close(void) + +mfxStatus MFX_DISP_HANDLE::LoadSelectedDLL(const msdk_disp_char *pPath, eMfxImplType reqImplType, + mfxIMPL reqImpl, mfxIMPL reqImplInterface, mfxInitParam &par) +{ + mfxStatus mfxRes = MFX_ERR_NONE; + + // check error(s) + if ((MFX_LIB_SOFTWARE != reqImplType) && + (MFX_LIB_HARDWARE != reqImplType)) + { + DISPATCHER_LOG_ERROR((("implType == %s, should be either MFX_LIB_SOFTWARE ot MFX_LIB_HARDWARE\n"), DispatcherLog_GetMFXImplString(reqImplType).c_str())); + loadStatus = MFX_ERR_ABORTED; + return loadStatus; + } + // only exact types of implementation is allowed + if (!(reqImpl & MFX_IMPL_AUDIO) && + (MFX_IMPL_SOFTWARE != reqImpl) && + (MFX_IMPL_HARDWARE != reqImpl) && + (MFX_IMPL_HARDWARE2 != reqImpl) && + (MFX_IMPL_HARDWARE3 != reqImpl) && + (MFX_IMPL_HARDWARE4 != reqImpl)) + { + DISPATCHER_LOG_ERROR((("invalid implementation impl == %s\n"), DispatcherLog_GetMFXImplString(impl).c_str())); + loadStatus = MFX_ERR_ABORTED; + return loadStatus; + } + // only mfxExtThreadsParam is allowed + if (par.NumExtParam) + { + if ((par.NumExtParam > 1) || !par.ExtParam) + { + loadStatus = MFX_ERR_ABORTED; + return loadStatus; + } + if ((par.ExtParam[0]->BufferId != MFX_EXTBUFF_THREADS_PARAM) || + (par.ExtParam[0]->BufferSz != sizeof(mfxExtThreadsParam))) + { + loadStatus = MFX_ERR_ABORTED; + return loadStatus; + } + } + + // close the handle before initialization + Close(); + + // save the library's type + this->implType = reqImplType; + this->impl = reqImpl; + this->implInterface = reqImplInterface; + + { + assert(hModule == (mfxModuleHandle)0); + DISPATCHER_LOG_BLOCK(("invoking LoadLibrary(%S)\n", MSDK2WIDE(pPath))); + + // load the DLL into the memory + hModule = MFX::mfx_dll_load(pPath); + + if (hModule) + { + int i; + + DISPATCHER_LOG_OPERATION({ + msdk_disp_char modulePath[1024]; + GetModuleFileNameW((HMODULE)hModule, modulePath, sizeof(modulePath)/sizeof(modulePath[0])); + DISPATCHER_LOG_INFO((("loaded module %S\n"), MSDK2WIDE(modulePath))) + }); + + if (impl & MFX_IMPL_AUDIO) + { + // load audio functions: pointers to exposed functions + for (i = 0; i < eAudioFuncTotal; i += 1) + { + // construct correct name of the function - remove "_a" postfix + + mfxFunctionPointer pProc = (mfxFunctionPointer) MFX::mfx_dll_get_addr(hModule, APIAudioFunc[i].pName); + #ifdef ANDROID + // on Android very first call to dlsym may fail + if (!pProc) pProc = (mfxFunctionPointer) MFX::mfx_dll_get_addr(hModule, APIAudioFunc[i].pName); + #endif + if (pProc) + { + // function exists in the library, + // save the pointer. + callAudioTable[i] = pProc; + } + else + { + // The library doesn't contain the function + DISPATCHER_LOG_WRN((("Can't find API function \"%s\"\n"), APIAudioFunc[i].pName)); + if (apiVersion.Version >= APIAudioFunc[i].apiVersion.Version) + { + DISPATCHER_LOG_ERROR((("\"%s\" is required for API %u.%u\n"), APIAudioFunc[i].pName, apiVersion.Major, apiVersion.Minor)); + mfxRes = MFX_ERR_UNSUPPORTED; + break; + } + } + } + } + else + { + // load video functions: pointers to exposed functions + for (i = 0; i < eVideoFuncTotal; i += 1) + { + mfxFunctionPointer pProc = (mfxFunctionPointer) MFX::mfx_dll_get_addr(hModule, APIFunc[i].pName); + #ifdef ANDROID + // on Android very first call to dlsym may fail + if (!pProc) pProc = (mfxFunctionPointer) MFX::mfx_dll_get_addr(hModule, APIFunc[i].pName); + #endif + if (pProc) + { + // function exists in the library, + // save the pointer. + callTable[i] = pProc; + } + else + { + // The library doesn't contain the function + DISPATCHER_LOG_WRN((("Can't find API function \"%s\"\n"), APIFunc[i].pName)); + if (apiVersion.Version >= APIFunc[i].apiVersion.Version) + { + DISPATCHER_LOG_ERROR((("\"%s\" is required for API %u.%u\n"), APIFunc[i].pName, apiVersion.Major, apiVersion.Minor)); + mfxRes = MFX_ERR_UNSUPPORTED; + break; + } + } + } + } + } + else + { + DISPATCHER_LOG_WRN((("can't find DLL: dlerror() = \"%s\"\n"), dlerror())); + mfxRes = MFX_ERR_UNSUPPORTED; + } + } + + // initialize the loaded DLL + if (MFX_ERR_NONE == mfxRes) + { + mfxVersion version(apiVersion); + + /* check whether it is audio session or video */ + mfxFunctionPointer *actualTable = (impl & MFX_IMPL_AUDIO) ? callAudioTable : callTable; + + // Call old-style MFXInit init for older libraries and audio library + bool callOldInit = (impl & MFX_IMPL_AUDIO) || !actualTable[eMFXInitEx]; // if true call eMFXInit, if false - eMFXInitEx + int tableIndex = (callOldInit) ? eMFXInit : eMFXInitEx; + + mfxFunctionPointer pFunc = actualTable[tableIndex]; + + { + if (callOldInit) + { + DISPATCHER_LOG_BLOCK(("MFXInit(%s,ver=%u.%u,session=0x%p)\n" + , DispatcherLog_GetMFXImplString(impl | implInterface).c_str() + , apiVersion.Major + , apiVersion.Minor + , &session)); + + mfxRes = (*(mfxStatus(MFX_CDECL *) (mfxIMPL, mfxVersion *, mfxSession *)) pFunc) (impl | implInterface, &version, &session); + } + else + { + DISPATCHER_LOG_BLOCK(("MFXInitEx(%s,ver=%u.%u,ExtThreads=%d,session=0x%p)\n" + , DispatcherLog_GetMFXImplString(impl | implInterface).c_str() + , apiVersion.Major + , apiVersion.Minor + , par.ExternalThreads + , &session)); + + mfxInitParam initPar = par; + // adjusting user parameters + initPar.Implementation = impl | implInterface; + initPar.Version = version; + mfxRes = (*(mfxStatus(MFX_CDECL *) (mfxInitParam, mfxSession *)) pFunc) (initPar, &session); + } + } + + if (MFX_ERR_NONE != mfxRes) + { + DISPATCHER_LOG_WRN((("library can't be load. MFXInit returned %s \n"), DispatcherLog_GetMFXStatusString(mfxRes))) + } + else + { + mfxRes = MFXQueryVersion((mfxSession) this, &actualApiVersion); + + if (MFX_ERR_NONE != mfxRes) + { + DISPATCHER_LOG_ERROR((("MFXQueryVersion returned: %d, skiped this library\n"), mfxRes)) + } + else + { + DISPATCHER_LOG_INFO((("MFXQueryVersion returned API: %d.%d\n"), actualApiVersion.Major, actualApiVersion.Minor)) + //special hook for applications that uses sink api to get loaded library path + DISPATCHER_LOG_LIBRARY(("%p" , hModule)); + DISPATCHER_LOG_INFO(("library loaded succesfully\n")) + } + } + } + + loadStatus = mfxRes; + return mfxRes; + +} // mfxStatus MFX_DISP_HANDLE::LoadSelectedDLL(const msdk_disp_char *pPath, eMfxImplType implType, mfxIMPL impl) + +mfxStatus MFX_DISP_HANDLE::UnLoadSelectedDLL(void) +{ + mfxStatus mfxRes = MFX_ERR_NONE; + + //unregistered plugins if any + pluginFactory.Close(); + + // close the loaded DLL + if (session) + { + /* check whether it is audio session or video */ + int tableIndex = eMFXClose; + mfxFunctionPointer pFunc; + if (impl & MFX_IMPL_AUDIO) + { + pFunc = callAudioTable[tableIndex]; + } + else + { + pFunc = callTable[tableIndex]; + } + + mfxRes = (*(mfxStatus (MFX_CDECL *) (mfxSession)) pFunc) (session); + if (MFX_ERR_NONE == mfxRes) + { + session = (mfxSession) 0; + } + + DISPATCHER_LOG_INFO((("MFXClose(0x%x) returned %d\n"), session, mfxRes)); + // actually, the return value is required to pass outside only. + } + + // it is possible, that there is an active child session. + // can't unload library in that case. + if ((MFX_ERR_UNDEFINED_BEHAVIOR != mfxRes) && + (hModule)) + { + // unload the library. + if (!MFX::mfx_dll_free(hModule)) + { + mfxRes = MFX_ERR_UNDEFINED_BEHAVIOR; + } + hModule = (mfxModuleHandle) 0; + } + + return mfxRes; + +} // mfxStatus MFX_DISP_HANDLE::UnLoadSelectedDLL(void) \ No newline at end of file diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dispatcher_log.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dispatcher_log.cpp new file mode 100644 index 0000000..010ba8a --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dispatcher_log.cpp @@ -0,0 +1,424 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if defined(MFX_DISPATCHER_LOG) + +#include "mfx_dispatcher_log.h" +#include "mfxstructures.h" +#include +#include +#include +#include + +struct CodeStringTable +{ + int code; + const char *string; +} LevelStrings []= +{ + {DL_INFO, "INFO: "}, + {DL_WRN, "WARNING:"}, + {DL_ERROR, "ERROR: "} +}; + +#define DEFINE_CODE(code)\ + {code, #code} + +static CodeStringTable StringsOfImpl[] = { + DEFINE_CODE(MFX_IMPL_AUTO), + DEFINE_CODE(MFX_IMPL_SOFTWARE), + DEFINE_CODE(MFX_IMPL_HARDWARE), + DEFINE_CODE(MFX_IMPL_AUTO_ANY), + DEFINE_CODE(MFX_IMPL_HARDWARE_ANY), + DEFINE_CODE(MFX_IMPL_HARDWARE2), + DEFINE_CODE(MFX_IMPL_HARDWARE3), + DEFINE_CODE(MFX_IMPL_HARDWARE4), + + DEFINE_CODE(MFX_IMPL_UNSUPPORTED) +}; + +static CodeStringTable StringsOfImplVIA[] = { + DEFINE_CODE(MFX_IMPL_VIA_ANY), + DEFINE_CODE(MFX_IMPL_VIA_D3D9), + DEFINE_CODE(MFX_IMPL_VIA_D3D11), +}; + +static CodeStringTable StringsOfStatus[] = +{ + DEFINE_CODE(MFX_ERR_NONE ), + DEFINE_CODE(MFX_ERR_UNKNOWN ), + DEFINE_CODE(MFX_ERR_NULL_PTR ), + DEFINE_CODE(MFX_ERR_UNSUPPORTED ), + DEFINE_CODE(MFX_ERR_MEMORY_ALLOC ), + DEFINE_CODE(MFX_ERR_NOT_ENOUGH_BUFFER ), + DEFINE_CODE(MFX_ERR_INVALID_HANDLE ), + DEFINE_CODE(MFX_ERR_LOCK_MEMORY ), + DEFINE_CODE(MFX_ERR_NOT_INITIALIZED ), + DEFINE_CODE(MFX_ERR_NOT_FOUND ), + DEFINE_CODE(MFX_ERR_MORE_DATA ), + DEFINE_CODE(MFX_ERR_MORE_SURFACE ), + DEFINE_CODE(MFX_ERR_ABORTED ), + DEFINE_CODE(MFX_ERR_DEVICE_LOST ), + DEFINE_CODE(MFX_ERR_INCOMPATIBLE_VIDEO_PARAM), + DEFINE_CODE(MFX_ERR_INVALID_VIDEO_PARAM ), + DEFINE_CODE(MFX_ERR_UNDEFINED_BEHAVIOR ), + DEFINE_CODE(MFX_ERR_DEVICE_FAILED ), + DEFINE_CODE(MFX_WRN_IN_EXECUTION ), + DEFINE_CODE(MFX_WRN_DEVICE_BUSY ), + DEFINE_CODE(MFX_WRN_VIDEO_PARAM_CHANGED ), + DEFINE_CODE(MFX_WRN_PARTIAL_ACCELERATION ), + DEFINE_CODE(MFX_WRN_INCOMPATIBLE_VIDEO_PARAM), + DEFINE_CODE(MFX_WRN_VALUE_NOT_CHANGED ), + DEFINE_CODE(MFX_WRN_OUT_OF_RANGE ), + +}; + +#define CODE_TO_STRING(code, array)\ + CodeToString(code, array, sizeof(array)/sizeof(array[0])) + +const char* CodeToString(int code, CodeStringTable array[], int len ) +{ + for (int i = 0 ; i < len; i++) + { + if (array[i].code == code) + return array[i].string; + } + return "undef"; +} + +std::string DispatcherLog_GetMFXImplString(int impl) +{ + std::string str1 = CODE_TO_STRING(impl & ~(-MFX_IMPL_VIA_ANY), StringsOfImpl); + std::string str2 = CODE_TO_STRING(impl & (-MFX_IMPL_VIA_ANY), StringsOfImplVIA); + + return str1 + (str2 == "undef" ? "" : "|"+str2); +} + +const char *DispatcherLog_GetMFXStatusString(int sts) +{ + return CODE_TO_STRING(sts, StringsOfStatus); +} + +////////////////////////////////////////////////////////////////////////// + + +void DispatcherLogBracketsHelper::Write(const char * str, ...) +{ + va_list argsptr; + va_start(argsptr, str); + DispatchLog::get().Write(m_level, m_opcode, str, argsptr); + va_end(argsptr); +} + +void DispatchLogBlockHelper::Write(const char * str, ...) +{ + va_list argsptr; + va_start(argsptr, str); + DispatchLog::get().Write(m_level, DL_EVENT_START, str, argsptr); + va_end(argsptr); +} + +DispatchLogBlockHelper::~DispatchLogBlockHelper() +{ + DispatchLog::get().Write(m_level, DL_EVENT_STOP, NULL, NULL); +} + +////////////////////////////////////////////////////////////////////////// + +DispatchLog::DispatchLog() + : m_DispatcherLogSink(DL_SINK_PRINTF) +{ + +} + +void DispatchLog::SetSink(int nSink, IMsgHandler * pHandler) +{ + DetachAllSinks(); + AttachSink(nSink, pHandler); +} + +void DispatchLog::AttachSink(int nsink, IMsgHandler *pHandler) +{ + m_DispatcherLogSink |= nsink; + if (NULL != pHandler) + m_Recepients.push_back(pHandler); +} + +void DispatchLog::DetachSink(int nsink, IMsgHandler *pHandler) +{ + if (nsink & DL_SINK_IMsgHandler) + { + m_Recepients.remove(pHandler); + } + + m_DispatcherLogSink &= ~nsink; +} + +void DispatchLog::ExchangeSink(int nsink, IMsgHandler *oldHdl, IMsgHandler *newHdl) +{ + if (nsink & DL_SINK_IMsgHandler) + { + std::list :: iterator it = std::find(m_Recepients.begin(), m_Recepients.end(), oldHdl); + + //cannot exchange in that case + if (m_Recepients.end() == it) + return; + + *it = newHdl; + } +} + + +void DispatchLog::DetachAllSinks() +{ + m_Recepients.clear(); + m_DispatcherLogSink = DL_SINK_NULL; +} + +void DispatchLog::Write(int level, int opcode, const char * msg, va_list argptr) +{ + int sinkTable[] = + { + DL_SINK_PRINTF, + DL_SINK_IMsgHandler, + }; + + for (size_t i = 0; i < sizeof(sinkTable) / sizeof(sinkTable[0]); i++) + { + switch(m_DispatcherLogSink & sinkTable[i]) + { + case DL_SINK_NULL: + break; + + case DL_SINK_PRINTF: + { + char msg_formated[8048] = {0}; + + if (NULL != msg && level != DL_LOADED_LIBRARY) + { + vsnprintf(msg_formated, sizeof(msg_formated)/sizeof(msg_formated[0]), msg, argptr); + //TODO: improve this , add opcode handling + printf("%s %s", CODE_TO_STRING(level, LevelStrings), msg_formated); + } + break; + } + + case DL_SINK_IMsgHandler: + { + std::list::iterator it; + + for (it = m_Recepients.begin(); it != m_Recepients.end(); ++it) + { + (*it)->Write(level, opcode, msg, argptr); + } + break; + } + } + } +} + +#if defined(DISPATCHER_LOG_REGISTER_EVENT_PROVIDER) +class ETWHandler : public IMsgHandler +{ +public: + ETWHandler(const wchar_t * guid_str) + : m_bUseFormatter(DISPATCHER_LOG_USE_FORMATING) + , m_EventHandle() + , m_bProviderEnable() + { + GUID rguid = GUID_NULL; + if (FAILED(CLSIDFromString(guid_str, &rguid))) + { + return; + } + + EventRegister(&rguid, NULL, NULL, &m_EventHandle); + + m_bProviderEnable = 0 != EventProviderEnabled(m_EventHandle, 1,0); + } + + ~ETWHandler() + { + if (m_EventHandle) + { + EventUnregister(m_EventHandle); + } + } + + virtual void Write(int level, int opcode, const char * msg, va_list argptr) + { + //event not registered + if (0==m_EventHandle) + { + return; + } + if (!m_bProviderEnable) + { + return; + } + if (level == DL_LOADED_LIBRARY) + { + return; + } + + char msg_formated[1024]; + EVENT_DESCRIPTOR descriptor; + EVENT_DATA_DESCRIPTOR data_descriptor; + + EventDescZero(&descriptor); + + descriptor.Opcode = (UCHAR)opcode; + descriptor.Level = (UCHAR)level; + + if (m_bUseFormatter) + { + if (NULL != msg) + { + vsnprintf(msg_formated, sizeof (msg_formated) / sizeof (msg_formated[0]), msg, argptr); + EventDataDescCreate(&data_descriptor, msg_formated, (ULONG)(strlen(msg_formated) + 1)); + }else + { + EventDataDescCreate(&data_descriptor, NULL, 0); + } + }else + { + //TODO: non formated events supports under zbb + } + + EventWrite(m_EventHandle, &descriptor, 1, &data_descriptor); + } + +protected: + + //we may not use formatter in some cases described in dispatch_log macro + //it significantly increases performance by eliminating any vsprintf operations + bool m_bUseFormatter; + //consumer is attached, dispatcher trace to reduce formating overhead + //submits event only if consumer attached + bool m_bProviderEnable; + REGHANDLE m_EventHandle; +}; +// + + +IMsgHandler *ETWHandlerFactory::GetSink(const wchar_t* sguid) +{ + _storage_type::iterator it; + it = m_storage.find(sguid); + if (it == m_storage.end()) + { + ETWHandler * handler = new ETWHandler(sguid); + _storage_type::_Pairib it_bool = m_storage.insert(_storage_type::value_type(sguid, handler)); + it = it_bool.first; + } + + return it->second; +} + +ETWHandlerFactory::~ETWHandlerFactory() +{ + for each(_storage_type::value_type val in m_storage) + { + delete val.second; + } +} + +class EventRegistrator : public IMsgHandler +{ + const wchar_t * m_sguid; +public: + EventRegistrator(const wchar_t* sguid = DISPATCHER_LOG_EVENT_GUID) + :m_sguid(sguid) + { + DispatchLog::get().AttachSink( DL_SINK_IMsgHandler + , this); + } + + virtual void Write(int level, int opcode, const char * msg, va_list argptr) + { + //we cannot call attach sink since we may have been called from iteration + //we axchanging preserve that placeholding + IMsgHandler * pSink = NULL; + DispatchLog::get().ExchangeSink(DL_SINK_IMsgHandler, + this, + pSink = ETWHandlerFactory::get().GetSink(m_sguid)); + //need to call only once here all next calls will be done inside dispatcherlog + if (NULL != pSink) + { + pSink->Write(level, opcode, msg, argptr); + } + } +}; +#endif + +template +class SinkRegistrator +{ +}; + +#if defined(DISPATCHER_LOG_REGISTER_EVENT_PROVIDER) +template <> +class SinkRegistrator +{ +public: + SinkRegistrator(const wchar_t* sguid = DISPATCHER_LOG_EVENT_GUID) + { + DispatchLog::get().AttachSink( DL_SINK_IMsgHandler + , ETWHandlerFactory::get().GetSink(sguid)); + } +}; +#endif + +#if defined(DISPATCHER_LOG_REGISTER_FILE_WRITER) +template <> +class SinkRegistrator +{ +public: + SinkRegistrator() + { + DispatchLog::get().AttachSink( DL_SINK_IMsgHandler, &FileSink::get(DISPACTHER_LOG_FW_PATH)); + } +}; + +void FileSink::Write(int level, int /*opcode*/, const char * msg, va_list argptr) +{ + if (NULL != m_hdl && NULL != msg) + { + fprintf(m_hdl, "%s", CODE_TO_STRING(level, LevelStrings)); + vfprintf(m_hdl, msg, argptr); + } +} +#endif + +////////////////////////////////////////////////////////////////////////// +//singletons initialization section + + +#ifdef DISPATCHER_LOG_REGISTER_EVENT_PROVIDER + static SinkRegistrator g_registrator1; +#endif + + +#ifdef DISPATCHER_LOG_REGISTER_FILE_WRITER + static SinkRegistrator g_registrator2; +#endif + + +#endif//(MFX_DISPATCHER_LOG) \ No newline at end of file diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dxva2_device.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dxva2_device.cpp new file mode 100644 index 0000000..95bcde7 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_dxva2_device.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_function_table.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_function_table.cpp new file mode 100644 index 0000000..e9c1f7b --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_function_table.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "mfx_dispatcher.h" + +// +// implement a table with functions names +// + +#undef FUNCTION +#define FUNCTION(return_value, func_name, formal_param_list, actual_param_list) \ + {#func_name, API_VERSION}, + +const +FUNCTION_DESCRIPTION APIFunc[eVideoFuncTotal] = +{ + {"MFXInit", {{0, 1}}}, + {"MFXClose", {{0, 1}}}, + {"MFXQueryIMPL", {{0, 1}}}, + {"MFXQueryVersion", {{0, 1}}}, + + {"MFXJoinSession", {{1, 1}}}, + {"MFXDisjoinSession", {{1, 1}}}, + {"MFXCloneSession", {{1, 1}}}, + {"MFXSetPriority", {{1, 1}}}, + {"MFXGetPriority", {{1, 1}}}, + + {"MFXInitEx", {{1, 14}}}, + +#include "mfx_exposed_functions_list.h" +}; + +const +FUNCTION_DESCRIPTION APIAudioFunc[eAudioFuncTotal] = +{ + {"MFXInit", {{8, 1}}}, + {"MFXClose", {{8, 1}}}, + {"MFXQueryIMPL", {{8, 1}}}, + {"MFXQueryVersion", {{8, 1}}}, + + {"MFXJoinSession", {{8, 1}}}, + {"MFXDisjoinSession", {{8, 1}}}, + {"MFXCloneSession", {{8, 1}}}, + {"MFXSetPriority", {{8, 1}}}, + {"MFXGetPriority", {{8, 1}}}, + +#include "mfxaudio_exposed_functions_list.h" +}; + +// static section of the file +namespace +{ + +// +// declare pseudo-functions. +// they are used as default values for call-tables. +// + +mfxStatus pseudoMFXInit(mfxIMPL impl, mfxVersion *ver, mfxSession *session) +{ + // touch unreferenced parameters + impl = impl; + ver = ver; + session = session; + + return MFX_ERR_UNKNOWN; + +} // mfxStatus pseudoMFXInit(mfxIMPL impl, mfxVersion *ver, mfxSession *session) + +mfxStatus pseudoMFXClose(mfxSession session) +{ + // touch unreferenced parameters + session = session; + + return MFX_ERR_UNKNOWN; + +} // mfxStatus pseudoMFXClose(mfxSession session) + +mfxStatus pseudoMFXJoinSession(mfxSession session, mfxSession child_session) +{ + // touch unreferenced parameters + session = session; + child_session = child_session; + + return MFX_ERR_UNKNOWN; + +} // mfxStatus pseudoMFXJoinSession(mfxSession session, mfxSession child_session) + +mfxStatus pseudoMFXCloneSession(mfxSession session, mfxSession *clone) +{ + // touch unreferenced parameters + session = session; + clone = clone; + + return MFX_ERR_UNKNOWN; + +} // mfxStatus pseudoMFXCloneSession(mfxSession session, mfxSession *clone) + +void SuppressWarnings(...) +{ + // this functions is suppose to suppress warnings. + // Actually it does nothing. + +} // void SuppressWarnings(...) + +#undef FUNCTION +#define FUNCTION(return_value, func_name, formal_param_list, actual_param_list) \ +return_value pseudo##func_name formal_param_list \ +{ \ + SuppressWarnings actual_param_list; \ + return MFX_ERR_UNKNOWN; \ +} + +#include "mfx_exposed_functions_list.h" + +} // namespace diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_library_iterator.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_library_iterator.cpp new file mode 100644 index 0000000..b374ae5 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_library_iterator.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_library_iterator_linux.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_library_iterator_linux.cpp new file mode 100644 index 0000000..cd1b6e9 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_library_iterator_linux.cpp @@ -0,0 +1,373 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define MFX_PCI_DIR "/sys/bus/pci/devices" +#define MFX_PCI_DISPLAY_CONTROLLER_CLASS 0x03 + +#if defined(LINUX64) + static const char mfx_so_hw_base_name[] = "libmfxhw64-p.so"; + static const char mfx_so_sw_base_name[] = "libmfxsw64-p.so"; +#else + static const char mfx_so_hw_base_name[] = "libmfxhw32-p.so"; + static const char mfx_so_sw_base_name[] = "libmfxsw32-p.so"; +#endif + + +static int mfx_dir_filter(const struct dirent* dir_ent) +{ + if (!dir_ent) return 0; + if (!strcmp(dir_ent->d_name, ".")) return 0; + if (!strcmp(dir_ent->d_name, "..")) return 0; + return 1; +} + +typedef int (*fsort)(const struct dirent**, const struct dirent**); + +static mfxU32 mfx_init_adapters(struct mfx_disp_adapters** p_adapters) +{ + mfxU32 adapters_num = 0; + int i = 0; + struct mfx_disp_adapters* adapters = NULL; + struct dirent** dir_entries = NULL; + int entries_num = scandir(MFX_PCI_DIR, &dir_entries, mfx_dir_filter, (fsort)alphasort); + + // sizeof(MFX_PCI_DIR) = 20, sizeof(dirent::d_name) <= 256, sizeof("class"|"vendor"|"device") = 6 + char file_name[300] = {}; + // sizeof("0xzzzzzz") = 8 + char str[16] = {0}; + FILE* file = NULL; + + for (i = 0; i < entries_num; ++i) + { + long int class_id = 0, vendor_id = 0, device_id = 0; + + if (!dir_entries[i]) continue; + // obtaining device class id + snprintf(file_name, sizeof(file_name)/sizeof(file_name[0]), "%s/%s/%s", MFX_PCI_DIR, dir_entries[i]->d_name, "class"); + file = fopen(file_name, "r"); + if (file) + { + if (fgets(str, sizeof(str), file)) + { + class_id = strtol(str, NULL, 16); + } + fclose(file); + + if (MFX_PCI_DISPLAY_CONTROLLER_CLASS == (class_id >> 16)) + { + // obtaining device vendor id + snprintf(file_name, sizeof(file_name)/sizeof(file_name[0]), "%s/%s/%s", MFX_PCI_DIR, dir_entries[i]->d_name, "vendor"); + file = fopen(file_name, "r"); + if (file) + { + if (fgets(str, sizeof(str), file)) + { + vendor_id = strtol(str, NULL, 16); + } + fclose(file); + } + // obtaining device id + snprintf(file_name, sizeof(file_name)/sizeof(file_name[0]), "%s/%s/%s", MFX_PCI_DIR, dir_entries[i]->d_name, "device"); + file = fopen(file_name, "r"); + if (file) + { + if (fgets(str, sizeof(str), file)) + { + device_id = strtol(str, NULL, 16); + } + fclose(file); + } + // adding valid adaptor to the list + if (vendor_id && device_id) + { + struct mfx_disp_adapters* tmp_adapters = NULL; + + tmp_adapters = (mfx_disp_adapters*)realloc(adapters, + (adapters_num+1)*sizeof(struct mfx_disp_adapters)); + + if (tmp_adapters) + { + adapters = tmp_adapters; + adapters[adapters_num].vendor_id = vendor_id; + adapters[adapters_num].device_id = device_id; + ++adapters_num; + } + } + } + } + free(dir_entries[i]); + } + if (entries_num) free(dir_entries); + if (p_adapters) *p_adapters = adapters; + return adapters_num; +} + +static mfxU32 mfx_list_libraries(const char* path, bool search_hw, struct mfx_libs** p_libs) +{ + mfxU32 libs_num = 0; + size_t len = 0; + int i = 0; + struct mfx_libs* libs = NULL; + struct dirent** dir_entries = NULL; + int entries_num = scandir(path, &dir_entries, mfx_dir_filter, (fsort)alphasort); + + for (i = 0; i < entries_num; ++i) + { + unsigned long int major = 0, minor = 0; + char* p_major = NULL; + char* p_minor = NULL; + char* p = NULL; + bool b_skip = false; + + if (!dir_entries[i]) continue; + + len = strlen(dir_entries[i]->d_name); + if (len < MFX_MIN_REAL_LIBNAME) goto skip; + if (len > MFX_MAX_REAL_LIBNAME) goto skip; + + if (search_hw) + { + if (strncmp(dir_entries[i]->d_name, mfx_so_hw_base_name, MFX_SO_BASE_NAME_LEN)) goto skip; + } + else + { + if (strncmp(dir_entries[i]->d_name, mfx_so_sw_base_name, MFX_SO_BASE_NAME_LEN)) goto skip; + } + + for (p = &(dir_entries[i]->d_name[MFX_SO_BASE_NAME_LEN]); !b_skip && *p; ++p) + { + if ('.' == *p) + { + if (!p_major) p_major = p; + else if (!p_minor) p_minor = p; + else b_skip = true; + } + else if (!strchr("0123456789", *p)) + { + b_skip = true; + } + } + if (b_skip) goto skip; + + if (!p_major || !p_minor) goto skip; + if (p_major != &(dir_entries[i]->d_name[MFX_SO_BASE_NAME_LEN])) goto skip; + ++p_major; + if (p_major == p_minor) goto skip; + ++p_minor; + if (!(*p_minor)) goto skip; + + major = strtoul(p_major, NULL, 10); + minor = strtoul(p_minor, NULL, 10); + if ((major <= 0xFFFF) && (minor <= 0xFFFF)) + { + struct mfx_libs* tmp_libs = NULL; + tmp_libs = (mfx_libs*)realloc(libs, + (libs_num+1)*sizeof(struct mfx_libs)); + if (tmp_libs) + { + libs = tmp_libs; + strncpy(libs[libs_num].name, dir_entries[i]->d_name, MFX_MAX_REAL_LIBNAME); + libs[libs_num].name[MFX_MAX_REAL_LIBNAME] = 0; + libs[libs_num].version.Major = (mfxU16)major; + libs[libs_num].version.Minor = (mfxU16)minor; + ++libs_num; + } + } + + skip: + free(dir_entries[i]); + } + if (entries_num) free(dir_entries); + if (p_libs) *p_libs = libs; + return libs_num; +} + +namespace MFX +{ + +mfxStatus SelectImplementationType(const mfxU32 adapterNum, mfxIMPL *pImplInterface, mfxU32 *pVendorID, mfxU32 *pDeviceID) +{ + mfx_disp_adapters* adapters = NULL; + unsigned int adapters_num = mfx_init_adapters(&adapters); + if (pVendorID && pDeviceID && adapterNum < adapters_num) + { + *pVendorID = adapters[adapterNum].vendor_id; + *pDeviceID = adapters[adapterNum].device_id; + } + if (adapters_num) free(adapters); + + if (adapterNum >= adapters_num) + return MFX_ERR_UNSUPPORTED; + + if ((*pImplInterface != MFX_IMPL_VIA_ANY) && + (*pImplInterface != MFX_IMPL_VIA_VAAPI) ) + return MFX_ERR_UNSUPPORTED; + + *pImplInterface = MFX_IMPL_VIA_VAAPI; + return MFX_ERR_NONE; +} + +MFXLibraryIterator::MFXLibraryIterator(void) + : m_implType(MFX_LIB_PSEUDO) + , m_implInterface(MFX_IMPL_UNSUPPORTED) + , m_vendorID(0) + , m_deviceID(0) + , m_bIsSubKeyValid(false) + , m_StorageID(0) + , m_lastLibIndex(-1) + , m_adapters(NULL) + , m_selected_adapter(0) + , m_libs_num(0) + , m_libs(NULL) +{ + m_SubKeyName[0] = 0; + m_path[0] = 0; + + m_adapters_num = mfx_init_adapters(&m_adapters); +} + +MFXLibraryIterator::~MFXLibraryIterator(void) +{ + Release(); + if (m_adapters_num) free(m_adapters); +} + +void MFXLibraryIterator::Release(void) +{ + m_implType = MFX_LIB_PSEUDO; + + m_vendorID = 0; + m_deviceID = 0; + + m_lastLibIndex = -1; + if (m_libs) + { + free(m_libs); + m_libs = NULL; + } + m_libs_num = 0; +} + +mfxStatus MFXLibraryIterator::Init(eMfxImplType implType, mfxIMPL impl, const mfxU32 adapter_num, int storageID) +{ + // release the object before initialization + Release(); + + // check error(s) + if (MFX_LIB_HARDWARE == implType) + { + if (!m_adapters_num || (adapter_num >= m_adapters_num)) + { + return MFX_ERR_UNSUPPORTED; + } + m_selected_adapter = adapter_num; + m_vendorID = m_adapters[m_selected_adapter].vendor_id; + m_deviceID = m_adapters[m_selected_adapter].device_id; + } + else if (MFX_LIB_SOFTWARE != implType) + { + return MFX_ERR_UNSUPPORTED; + } + if (MFX_STORAGE_ID_OPT != storageID) + { + return MFX_ERR_UNSUPPORTED; + } + + // set the required library's implementation type + m_implType = implType; + + snprintf(m_path, sizeof(m_path)/sizeof(m_path[0]), + "%s", MFX_MODULES_DIR); + + m_libs_num = mfx_list_libraries(m_path, (MFX_LIB_HARDWARE == implType), &m_libs); + + if (!m_libs_num) + { + Release(); + return MFX_ERR_UNSUPPORTED; + } + + return MFX_ERR_NONE; +} + +mfxStatus MFXLibraryIterator::SelectDLLVersion(char *pPath, size_t pathSize, + eMfxImplType* pImplType, mfxVersion minVersion) +{ + if (m_lastLibIndex < 0) + { + for (int i = m_libs_num - 1; i >= 0; i--) + { + if (m_libs[i].version.Major == minVersion.Major && m_libs[i].version.Minor >= minVersion.Minor) + { + m_lastLibIndex = i; + break; + } + } + } + else + m_lastLibIndex--; + + if (m_lastLibIndex < 0) + return MFX_ERR_NOT_FOUND; + + if (m_libs[m_lastLibIndex].version.Major != minVersion.Major || + m_libs[m_lastLibIndex].version.Minor < minVersion.Minor) + { + m_lastLibIndex = -1; + return MFX_ERR_NOT_FOUND; + } + + snprintf(pPath, pathSize, "%s/%s", m_path, m_libs[m_lastLibIndex].name); + + if (pImplType) *pImplType = (!m_vendorID && !m_deviceID)? MFX_LIB_SOFTWARE: MFX_LIB_HARDWARE; + + return MFX_ERR_NONE; +} + +mfxIMPL MFXLibraryIterator::GetImplementationType() +{ + if (m_selected_adapter < 0 || static_cast(m_selected_adapter) >= m_adapters_num) + return MFX_ERR_UNSUPPORTED; + + return MFX_IMPL_VIA_VAAPI; +} + +bool MFXLibraryIterator::GetSubKeyName(msdk_disp_char *subKeyName, size_t length) const +{ + return false; +} + +} // namespace MFX + diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_dll.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_dll.cpp new file mode 100644 index 0000000..95bcde7 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_dll.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_dll_linux.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_dll_linux.cpp new file mode 100644 index 0000000..1c05d68 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_dll_linux.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +#include "mfx_dispatcher.h" +#include +#include + +#if defined(LINUX64) +const msdk_disp_char * defaultDLLName[2] = {"libmfxhw64.so", + "libmfxsw64.so"}; +const msdk_disp_char * defaultAudioDLLName[2] = {"libmfxaudiosw64.so", + "libmfxaudiosw64.so"}; + +const msdk_disp_char * defaultPluginDLLName[2] = {"libmfxplugin64_hw.so", + "libmfxplugin64_sw.so"}; + + +#else // for Linux32 and Android +const msdk_disp_char * defaultDLLName[2] = {"libmfxhw32.so", + "libmfxsw32.so"}; +const msdk_disp_char * defaultAudioDLLName[2] = {"libmfxaudiosw32.so", + "libmfxaudiosw32.so"}; + +const msdk_disp_char * defaultPluginDLLName[2] = {"libmfxplugin32_hw.so", + "libmfxplugin32_sw.so"}; +#endif + +namespace MFX +{ + +mfxStatus mfx_get_default_dll_name(msdk_disp_char *pPath, size_t /*pathSize*/, eMfxImplType implType) +{ + strcpy(pPath, defaultDLLName[implType & 1]); + + return MFX_ERR_NONE; + +} // mfxStatus GetDefaultDLLName(wchar_t *pPath, size_t pathSize, eMfxImplType implType) + + +mfxStatus mfx_get_default_plugin_name(msdk_disp_char *pPath, size_t pathSize, eMfxImplType implType) +{ + strcpy(pPath, defaultPluginDLLName[implType & 1]); + + return MFX_ERR_NONE; +} + + +mfxStatus mfx_get_default_audio_dll_name(msdk_disp_char *pPath, size_t /*pathSize*/, eMfxImplType implType) +{ + strcpy(pPath, defaultAudioDLLName[implType & 1]); + + return MFX_ERR_NONE; + +} // mfxStatus GetDefaultAudioDLLName(wchar_t *pPath, size_t pathSize, eMfxImplType implType) + +mfxModuleHandle mfx_dll_load(const msdk_disp_char *pFileName) +{ + mfxModuleHandle hModule = (mfxModuleHandle) 0; + + // check error(s) + if (NULL == pFileName) + { + return NULL; + } + // load the module + hModule = dlopen(pFileName, RTLD_LOCAL|RTLD_NOW); + + return hModule; +} // mfxModuleHandle mfx_dll_load(const wchar_t *pFileName) + +mfxFunctionPointer mfx_dll_get_addr(mfxModuleHandle handle, const char *pFunctionName) +{ + if (NULL == handle) + { + return NULL; + } + + mfxFunctionPointer addr = (mfxFunctionPointer) dlsym(handle, pFunctionName); + if (!addr) + { + return NULL; + } + + return addr; +} // mfxFunctionPointer mfx_dll_get_addr(mfxModuleHandle handle, const char *pFunctionName) + +bool mfx_dll_free(mfxModuleHandle handle) +{ + if (NULL == handle) + { + return true; + } + dlclose(handle); + + return true; +} // bool mfx_dll_free(mfxModuleHandle handle) + +mfxModuleHandle mfx_get_dll_handle(const msdk_disp_char *pFileName) { + return mfx_dll_load(pFileName); +} + +} // namespace MFX + diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_plugin.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_plugin.cpp new file mode 100644 index 0000000..74ec154 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_load_plugin.cpp @@ -0,0 +1,453 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "mfx_load_plugin.h" +#include "mfx_load_dll.h" +#include "mfx_dispatcher_log.h" + +#define TRACE_PLUGIN_ERROR(str, ...) DISPATCHER_LOG_ERROR((("[PLUGIN]: " str), __VA_ARGS__)) +#define TRACE_PLUGIN_INFO(str, ...) DISPATCHER_LOG_INFO((("[PLUGIN]: " str), __VA_ARGS__)) + +#define CREATE_PLUGIN_FNC "CreatePlugin" + +MFX::PluginModule::PluginModule() + : mHmodule() + , mCreatePluginPtr() + , mPath() +{ +} + +MFX::PluginModule::PluginModule(const PluginModule & that) + : mHmodule(mfx_dll_load(that.mPath)) + , mCreatePluginPtr(that.mCreatePluginPtr) +{ + msdk_disp_char_cpy_s(mPath, sizeof(mPath) / sizeof(*mPath), that.mPath); +} + +MFX::PluginModule & MFX::PluginModule::operator = (const MFX::PluginModule & that) +{ + if (this != &that) + { + Tidy(); + mHmodule = mfx_dll_load(that.mPath); + mCreatePluginPtr = that.mCreatePluginPtr; + msdk_disp_char_cpy_s(mPath, sizeof(mPath) / sizeof(*mPath), that.mPath); + } + return *this; +} + +MFX::PluginModule::PluginModule(const msdk_disp_char * path) + : mCreatePluginPtr() +{ + mHmodule = mfx_dll_load(path); + if (NULL == mHmodule) { + TRACE_PLUGIN_ERROR("Cannot load module: %S\n", MSDK2WIDE(path)); + return ; + } + TRACE_PLUGIN_INFO("Plugin loaded at: %S\n", MSDK2WIDE(path)); + + mCreatePluginPtr = (CreatePluginPtr_t)mfx_dll_get_addr(mHmodule, CREATE_PLUGIN_FNC); + if (NULL == mCreatePluginPtr) { + TRACE_PLUGIN_ERROR("Cannot get procedure address: %s\n", CREATE_PLUGIN_FNC); + return ; + } + + msdk_disp_char_cpy_s(mPath, sizeof(mPath) / sizeof(*mPath), path); +} + +bool MFX::PluginModule::Create( mfxPluginUID uid, mfxPlugin& plg) +{ + bool result = false; + if (mCreatePluginPtr) + { + mfxStatus mfxResult = mCreatePluginPtr(uid, &plg); + result = (MFX_ERR_NONE == mfxResult); + if (!result) { + TRACE_PLUGIN_ERROR("\"%S::%s\" returned %d\n", MSDK2WIDE(mPath), CREATE_PLUGIN_FNC, mfxResult); + } else { + TRACE_PLUGIN_INFO("\"%S::%s\" SUCCEED\n", MSDK2WIDE(mPath), CREATE_PLUGIN_FNC); + } + } + return result; +} + +void MFX::PluginModule::Tidy() +{ + mfx_dll_free(mHmodule); + mCreatePluginPtr = NULL; + mHmodule = NULL; +} + +MFX::PluginModule::~PluginModule(void) +{ + Tidy(); +} + +#if !defined(MEDIASDK_UWP_PROCTABLE) + +bool MFX::MFXPluginFactory::RunVerification( const mfxPlugin & plg, const PluginDescriptionRecord &dsc, mfxPluginParam &pluginParams) +{ + if (plg.PluginInit == 0) + { + TRACE_PLUGIN_ERROR("plg->PluginInit = 0\n", 0); + return false; + } + if (plg.PluginClose == 0) + { + TRACE_PLUGIN_ERROR("plg->PluginClose = 0\n", 0); + return false; + } + if (plg.GetPluginParam == 0) + { + TRACE_PLUGIN_ERROR("plg->GetPluginParam = 0\n", 0); + return false; + } + + if (plg.Execute == 0) + { + TRACE_PLUGIN_ERROR("plg->Execute = 0\n", 0); + return false; + } + if (plg.FreeResources == 0) + { + TRACE_PLUGIN_ERROR("plg->FreeResources = 0\n", 0); + return false; + } + + mfxStatus sts = plg.GetPluginParam(plg.pthis, &pluginParams); + if (sts != MFX_ERR_NONE) + { + TRACE_PLUGIN_ERROR("plg->GetPluginParam() returned %d\n", sts); + return false; + } + + if (dsc.Default) + { + // for default plugins there is no description, dsc.APIVersion, dsc.PluginVersion and dsc.PluginUID were set by dispatcher + // dsc.PluginVersion == requested plugin version (parameter of MFXVideoUSER_Load); dsc.APIVersion == loaded library API + if (dsc.PluginVersion > pluginParams.PluginVersion) + { + TRACE_PLUGIN_ERROR("plg->GetPluginParam() returned PluginVersion=%d, but it is smaller than requested : %d\n", pluginParams.PluginVersion, dsc.PluginVersion); + return false; + } + } + else + { + if (!dsc.onlyVersionRegistered && pluginParams.CodecId != dsc.CodecId) + { + TRACE_PLUGIN_ERROR("plg->GetPluginParam() returned CodecId=" MFXFOURCCTYPE()", but registration has CodecId=" MFXFOURCCTYPE()"\n" + , MFXU32TOFOURCC(pluginParams.CodecId), MFXU32TOFOURCC(dsc.CodecId)); + return false; + } + + if (!dsc.onlyVersionRegistered && pluginParams.Type != dsc.Type) + { + TRACE_PLUGIN_ERROR("plg->GetPluginParam() returned Type=%d, but registration has Type=%d\n", pluginParams.Type, dsc.Type); + return false; + } + + if (pluginParams.PluginUID != dsc.PluginUID) + { + TRACE_PLUGIN_ERROR("plg->GetPluginParam() returned UID=" MFXGUIDTYPE()", but registration has UID=" MFXGUIDTYPE()"\n" + , MFXGUIDTOHEX(&pluginParams.PluginUID), MFXGUIDTOHEX(&dsc.PluginUID)); + return false; + } + + if (pluginParams.PluginVersion != dsc.PluginVersion) + { + TRACE_PLUGIN_ERROR("plg->GetPluginParam() returned PluginVersion=%d, but registration has PlgVer=%d\n", pluginParams.PluginVersion, dsc.PluginVersion); + return false; + } + + if (pluginParams.APIVersion.Version != dsc.APIVersion.Version) + { + TRACE_PLUGIN_ERROR("plg->GetPluginParam() returned APIVersion=%d.%d, but registration has APIVer=%d.%d\n" + , pluginParams.APIVersion.Major, pluginParams.APIVersion.Minor + , dsc.APIVersion.Major, dsc.APIVersion.Minor); + return false; + } + } + + switch(pluginParams.Type) + { + case MFX_PLUGINTYPE_VIDEO_DECODE: + case MFX_PLUGINTYPE_VIDEO_ENCODE: + case MFX_PLUGINTYPE_VIDEO_VPP: + { + TRACE_PLUGIN_INFO("plugin type= %d\n", pluginParams.Type); + if (plg.Video == 0) + { + TRACE_PLUGIN_ERROR("plg->Video = 0\n", 0); + return false; + } + + if (!VerifyCodecCommon(*plg.Video)) + return false; + break; + } + } + + switch(pluginParams.Type) + { + case MFX_PLUGINTYPE_VIDEO_DECODE: + return VerifyDecoder(*plg.Video); + case MFX_PLUGINTYPE_AUDIO_DECODE: + return VerifyAudioDecoder(*plg.Audio); + case MFX_PLUGINTYPE_VIDEO_ENCODE: + return VerifyEncoder(*plg.Video); + case MFX_PLUGINTYPE_AUDIO_ENCODE: + return VerifyAudioEncoder(*plg.Audio); + case MFX_PLUGINTYPE_VIDEO_VPP: + return VerifyVpp(*plg.Video); + case MFX_PLUGINTYPE_VIDEO_ENC: + return VerifyEnc(*plg.Video); + default: + { + TRACE_PLUGIN_ERROR("unsupported plugin type: %d\n", pluginParams.Type); + return false; + } + } +} + +bool MFX::MFXPluginFactory::VerifyVpp( const mfxVideoCodecPlugin &vpp ) +{ + if (vpp.VPPFrameSubmit == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->VPPFrameSubmit = 0\n", 0); + return false; + } + + return true; + +} + +bool MFX::MFXPluginFactory::VerifyEncoder( const mfxVideoCodecPlugin &encoder ) +{ + if (encoder.EncodeFrameSubmit == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->EncodeFrameSubmit = 0\n", 0); + return false; + } + + return true; +} + +bool MFX::MFXPluginFactory::VerifyAudioEncoder( const mfxAudioCodecPlugin &encoder ) +{ + if (encoder.EncodeFrameSubmit == 0) + { + TRACE_PLUGIN_ERROR("plg->Audio->EncodeFrameSubmit = 0\n", 0); + return false; + } + + return true; +} + +bool MFX::MFXPluginFactory::VerifyEnc( const mfxVideoCodecPlugin &videoEnc ) +{ + if (videoEnc.ENCFrameSubmit == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->EncodeFrameSubmit = 0\n", 0); + return false; + } + + return true; +} + +bool MFX::MFXPluginFactory::VerifyDecoder( const mfxVideoCodecPlugin &decoder ) +{ + if (decoder.DecodeHeader == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->DecodeHeader = 0\n", 0); + return false; + } + if (decoder.GetPayload == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->GetPayload = 0\n", 0); + return false; + } + if (decoder.DecodeFrameSubmit == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->DecodeFrameSubmit = 0\n", 0); + return false; + } + + return true; +} + +bool MFX::MFXPluginFactory::VerifyAudioDecoder( const mfxAudioCodecPlugin &decoder ) +{ + if (decoder.DecodeHeader == 0) + { + TRACE_PLUGIN_ERROR("plg->Audio->DecodeHeader = 0\n", 0); + return false; + } +// if (decoder.GetPayload == 0) + { + // TRACE_PLUGIN_ERROR("plg->Audio->GetPayload = 0\n", 0); + // return false; + } + if (decoder.DecodeFrameSubmit == 0) + { + TRACE_PLUGIN_ERROR("plg->Audio->DecodeFrameSubmit = 0\n", 0); + return false; + } + + return true; +} + +bool MFX::MFXPluginFactory::VerifyCodecCommon( const mfxVideoCodecPlugin & videoCodec ) +{ + if (videoCodec.Query == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->Query = 0\n", 0); + return false; + } + //todo: remove + if (videoCodec.Query == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->Query = 0\n", 0); + return false; + } + if (videoCodec.QueryIOSurf == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->QueryIOSurf = 0\n", 0); + return false; + } + if (videoCodec.Init == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->Init = 0\n", 0); + return false; + } + if (videoCodec.Reset == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->Reset = 0\n", 0); + return false; + } + if (videoCodec.Close == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->Close = 0\n", 0); + return false; + } + if (videoCodec.GetVideoParam == 0) + { + TRACE_PLUGIN_ERROR("plg->Video->GetVideoParam = 0\n", 0); + return false; + } + + return true; +} + +mfxStatus MFX::MFXPluginFactory::Create(const PluginDescriptionRecord & rec) +{ + PluginModule plgModule(rec.sPath); + mfxPlugin plg = {}; + mfxPluginParam plgParams; + + if (!plgModule.Create(rec.PluginUID, plg)) + { + return MFX_ERR_UNKNOWN; + } + + if (!RunVerification(plg, rec, plgParams)) + { + //will do not call plugin close since it is not safe to do that until structure is corrected + return MFX_ERR_UNKNOWN; + } + + + if (rec.Type == MFX_PLUGINTYPE_AUDIO_DECODE || + rec.Type == MFX_PLUGINTYPE_AUDIO_ENCODE) + { + mfxStatus sts = MFXAudioUSER_Register(mSession, plgParams.Type, &plg); + if (MFX_ERR_NONE != sts) + { + TRACE_PLUGIN_ERROR(" MFXAudioUSER_Register returned %d\n", sts); + return sts; + } + } + else + { + mfxStatus sts = MFXVideoUSER_Register(mSession, plgParams.Type, &plg); + if (MFX_ERR_NONE != sts) + { + TRACE_PLUGIN_ERROR(" MFXVideoUSER_Register returned %d\n", sts); + return sts; + } + } + + mPlugins.push_back(FactoryRecord(plgParams, plgModule, plg)); + + return MFX_ERR_NONE; +} + +MFX::MFXPluginFactory::~MFXPluginFactory() +{ + Close(); +} + +MFX::MFXPluginFactory::MFXPluginFactory( mfxSession session ) +{ + mSession = session; + nPlugins = 0; +} + +bool MFX::MFXPluginFactory::Destroy( const mfxPluginUID & uidToDestroy) +{ + for (MFXVector::iterator i = mPlugins.begin(); i!= mPlugins.end(); i++) + { + if (i->plgParams.PluginUID == uidToDestroy) + { + DestroyPlugin(*i); + //dll unload should happen here + //todo: check that dll_free fail is traced + mPlugins.erase(i); + return true; + } + } + return false; +} + +void MFX::MFXPluginFactory::Close() +{ + for (MFXVector::iterator i = mPlugins.begin(); i!= mPlugins.end(); i++) + { + DestroyPlugin(*i); + } + mPlugins.clear(); +} + +void MFX::MFXPluginFactory::DestroyPlugin( FactoryRecord & record) +{ + mfxStatus sts; + if (record.plgParams.Type == MFX_PLUGINTYPE_AUDIO_DECODE || + record.plgParams.Type == MFX_PLUGINTYPE_AUDIO_ENCODE) + { + sts = MFXAudioUSER_Unregister(mSession, record.plgParams.Type); + TRACE_PLUGIN_INFO(" MFXAudioUSER_Unregister for Type=%d, returned %d\n", record.plgParams.Type, sts); + } + else + { + sts = MFXVideoUSER_Unregister(mSession, record.plgParams.Type); + TRACE_PLUGIN_INFO(" MFXVideoUSER_Unregister for Type=%d, returned %d\n", record.plgParams.Type, sts); + } +} + +#endif //!defined(MEDIASDK_UWP_PROCTABLE) diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_cfg_parser.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_cfg_parser.cpp new file mode 100644 index 0000000..3a3a6fc --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_cfg_parser.cpp @@ -0,0 +1,374 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +#include "mfx_plugin_cfg_parser.h" +#include "mfx_dispatcher_log.h" +#include +#include + +const int guidLen = 16; + +// In-place strip trailing whitespace chars +static char* Strip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace(*--p)) + { + *p = 0; + } + return s; +} + +// Search for the first non-whitespace char +static char* SkipWhitespace(char* s) +{ + while (*s && isspace(*s)) + { + s++; + } + return s; +} + +// Return pointer to first char c or ';' comment in given string, or pointer to +// null at end of string if neither found. ';' must be prefixed by a whitespace +// character to register as a comment. +static char* FindCharOrComment(char* s, char c) +{ + int whitespaceFound = 0; + while (*s && *s != c && !(whitespaceFound && *s == ';')) + { + whitespaceFound = isspace(*s); + s++; + } + return s; +} + +// Version of strncpy that ensures dest (size bytes) is null-terminated. +static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy(dest, src, size); + dest[size - 1] = 0; + return dest; +} + +enum +{ + MAX_SECTION = 4096 +}; + +namespace MFX +{ + +bool parseGUID(const char* src, mfxU8* guid) +{ + mfxU32 p[guidLen]; + int res = sscanf(src, + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + p, p + 1, p + 2, p + 3, p + 4, p + 5, p + 6, p + 7, + p + 8, p + 9, p + 10, p + 11, p + 12, p + 13, p + 14, p + 15); + if (res != guidLen) + return false; + + for (int i = 0; i < guidLen; i++) + guid[i] = (mfxU8)p[i]; + return true; +} + +PluginConfigParser::PluginConfigParser(const char * name) + : sectionStart() +{ + cfgFile = fopen(name, "rt"); +} + +PluginConfigParser::~PluginConfigParser() +{ + if (cfgFile) + { + fclose(cfgFile); + } +} + +// Returns current section name if any +bool PluginConfigParser::GetCurrentPluginName(char * pluginName, int nChars) +{ + if (!cfgFile) + return false; + + char line[MAX_PLUGIN_NAME]; + char section[MAX_SECTION] = ""; + bool foundSection = false; + char * start; + char * end; + + if (fgets(line, MAX_PLUGIN_NAME, cfgFile)) + { + start = SkipWhitespace(Strip(line)); + if (*start == '[') + { + // A "[section]" line + end = FindCharOrComment(start + 1, ']'); + if (*end == ']') { + *end = '\0'; + strncpy0(pluginName, start + 1, nChars); + foundSection = true; + } + } + } + fsetpos(cfgFile, §ionStart); + + return foundSection; +} + +// Tries to advance to the next section in config file +bool PluginConfigParser::AdvanceToNextPlugin() +{ + if (!cfgFile) + return false; + + char line[MAX_PLUGIN_NAME]; + char section[MAX_SECTION] = ""; + bool foundSection = false; + char * start; + char * end; + + fgetpos(cfgFile, §ionStart); + // advance one line from current section + if (!fgets(line, MAX_PLUGIN_NAME, cfgFile)) + return false; + + fpos_t lastReadLine = sectionStart; + while (!foundSection && fgets(line, MAX_PLUGIN_NAME, cfgFile)) + { + start = SkipWhitespace(Strip(line)); + + if (*start == '[') + { + // A "[section]" line + end = FindCharOrComment(start + 1, ']'); + if (*end == ']') { + foundSection = true; + sectionStart = lastReadLine; + } + } + fgetpos(cfgFile, &lastReadLine); + } + + fsetpos(cfgFile, §ionStart); + return foundSection; +} + +// Return to first ini file section +bool PluginConfigParser::Rewind() +{ + if (!cfgFile) + return false; + + fseek(cfgFile, 0, SEEK_SET); + fgetpos(cfgFile, §ionStart); + + return true; +} + +// Enumerates sections in currect file +int PluginConfigParser::GetPluginCount() +{ + if (!cfgFile) + return -1; + + Rewind(); + + int counter = 0; + + do + { + counter++; + } while (AdvanceToNextPlugin()); + + // special case - plugin.cfg without section header + if (counter == 0) + { + int size = fseek(cfgFile, 0, SEEK_END); + if (size > 0) + counter = 1; + } + + Rewind(); + + return counter; +} + + +bool PluginConfigParser::ParseSingleParameter(const char * name, char * value, PluginDescriptionRecord & dst, mfxU32 & parsedFields) +{ + if (0 == strcmp(name, "Type")) + { + dst.Type = atoi(value); + parsedFields |= PARSED_TYPE; + return true; + } + if (0 == strcmp(name, "CodecID")) + { + const int fourccLen = 4; + if (strlen(value) == 0 || strlen(value) > fourccLen) + return false; + + dst.CodecId = MFX_MAKEFOURCC(' ',' ',' ',' '); + char *codecID = reinterpret_cast(&dst.CodecId); + for (size_t i = 0; i < strlen(value); i++) + codecID[i] = value[i]; + + parsedFields |= PARSED_CODEC_ID; + return true; + } + if (0 == strcmp(name, "GUID")) + { + if (!parseGUID(value, dst.PluginUID.Data)) + return false; + + parsedFields |= PARSED_UID; + return true; + } + if (0 == strcmp(name, "Path") || +#ifdef LINUX64 + 0 == strcmp(name, "FileName64")) +#else + 0 == strcmp(name, "FileName32")) +#endif + { + // strip quotes + const int lastCharIndex = strlen(value) - 1; + if (value[0] == '"' && value[lastCharIndex] == '"') + { + value[lastCharIndex] = '\0'; + value = value + 1; + } + if (strlen(dst.sPath) + strlen("/") + strlen(value) >= MAX_PLUGIN_PATH) + return false; + strcpy(dst.sPath + strlen(dst.sPath), "/"); + strcpy(dst.sPath + strlen(dst.sPath), value); + parsedFields |= PARSED_PATH; + return true; + } + if (0 == strcmp(name, "Default")) + { + dst.Default = (0 != atoi(value)); + parsedFields |= PARSED_DEFAULT; + return true; + } + if (0 == strcmp(name, "PluginVersion")) + { + dst.PluginVersion = atoi(value); + parsedFields |= PARSED_VERSION; + return true; + } + if (0 == strcmp(name, "APIVersion")) + { + mfxU32 APIVersion = atoi(value); + dst.APIVersion.Minor = static_cast (APIVersion & 0xff); + dst.APIVersion.Major = static_cast (APIVersion >> 8); + parsedFields |= PARSED_API_VERSION; + return true; + } + + return false; +} + +bool PluginConfigParser::ParsePluginParams(PluginDescriptionRecord & dst, mfxU32 & parsedFields) +{ + if (!cfgFile) + return false; + + char line[MAX_PLUGIN_NAME]; + + char* start; + char* end; + char* name; + char* value; + bool error = false; + + int parsedHeaders = 0; + fgetpos(cfgFile, §ionStart); + + // Scan through file line by line + while (fgets(line, MAX_PLUGIN_NAME, cfgFile)) + { + start = SkipWhitespace(Strip(line)); + + if (*start == ';' || *start == '#') + { + // Allow '#' and ';' comments at start of line + } + else if (*start == '[') + { + if (++parsedHeaders == 1) + { + // no interest in section header here + continue; + } + else + { + // we found next header + break; + } + } + else if (*start && *start != ';') + { + // do not allow header in the middle of plugin description + parsedHeaders = 1; + // Not a comment, must be a name[=:]value pair + end = FindCharOrComment(start, '='); + if (*end != '=') + { + end = FindCharOrComment(start, ':'); + } + if (*end == '=' || *end == ':') + { + *end = 0; + name = Strip(start); + value = SkipWhitespace(end + 1); + end = FindCharOrComment(value, 0); + if (*end == ';') + { + *end = 0; + } + Strip(value); + + // Valid name[=:]value pair found, call handler + ParseSingleParameter(name, value, dst, parsedFields); + } + else if (!error) + { + // No '=' or ':' found on name[=:]value line + error = true; + } + } + // Store section start for next iteration + // fgetpos(cfgFile, §ionStart); + } + + // restore previous position in file + fsetpos(cfgFile, §ionStart); + + return !error && (parsedFields != 0); +} + +} // namespace MFX + diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_hive.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_hive.cpp new file mode 100644 index 0000000..95bcde7 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_hive.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_hive_linux.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_hive_linux.cpp new file mode 100644 index 0000000..24417bb --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_plugin_hive_linux.cpp @@ -0,0 +1,391 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +#include "mfx_plugin_hive.h" +#include "mfx_library_iterator.h" +#include "mfx_dispatcher.h" +#include "mfx_dispatcher_log.h" +#include "mfx_load_dll.h" +#include "mfx_plugin_cfg_parser.h" + +#include +#include +#include +#include +#include +#include + +#define TRACE_HIVE_ERROR(str, ...) DISPATCHER_LOG_ERROR((("[HIVE]: " str), __VA_ARGS__)) +#define TRACE_HIVE_INFO(str, ...) DISPATCHER_LOG_INFO((("[HIVE]: " str), __VA_ARGS__)) +#define TRACE_HIVE_WRN(str, ...) DISPATCHER_LOG_WRN((("[HIVE]: " str), __VA_ARGS__)) + +namespace +{ +#ifdef ANDROID + +#if (MFX_ANDROID_VERSION >= MFX_O) + const char rootPluginPath[] = "/vendor/etc/mediasdk_plugins.cfg"; +#else + const char rootPluginPath[] = "/etc/mediasdk_plugins.cfg"; +#endif + +#else + const char rootPluginPath[] = MFX_PLUGINS_DIR"/plugins.cfg"; +#endif + //const wchar_t rootDispatchPath[] = L"Software\\Intel\\MediaSDK\\Dispatch"; + const char pluginSubkey[] = "Plugin"; + const char TypeKeyName[] = "Type"; + const char CodecIDKeyName[] = "CodecID"; + const char GUIDKeyName[] = "GUID"; + const char PathKeyName[] = "Path"; + const char DefaultKeyName[] = "Default"; + const char PlgVerKeyName[] = "PluginVersion"; + const char APIVerKeyName[] = "APIVersion"; +} + +namespace +{ +#ifdef LINXU64 + const char pluginFileName[] = "FileName64"; +#else + const char pluginFileName[] = "FileName32"; +#endif // LINXU64 + //do not allow store plugin in different hierarchy + const char pluginFileNameRestrictedCharacters[] = "\\/"; + const char pluginCfgFileName[] = "plugin.cfg"; + const char pluginSearchPattern[] = "????????????????????????????????"; + const mfxU32 pluginCfgFileNameLen = 10; + const mfxU32 pluginDirNameLen = 32; + const mfxU32 defaultPluginNameLen = 25; + const mfxU32 charsPermfxU8 = 2; + const mfxU32 slashLen = 1; + enum + { + MAX_PLUGIN_FILE_LINE = 4096 + }; +} + + +#define alignStr() "%-14S" + +namespace MFX +{ + +static bool isFieldMissed(mfxU32 parseMask, mfxU32 reqMask, mfxU32 field) +{ + return !(((parseMask & field) != 0) || ((reqMask & field) == 0)); // !( reqMask & field => parseMask & field ) +} + +static bool CheckPluginRecord(PluginDescriptionRecord & descriptionRecord, mfxU32 foundFields, mfxU32 requiredFields) +{ + if (isFieldMissed(foundFields, requiredFields, PluginConfigParser::PARSED_TYPE)) + { + return false; + } + TRACE_HIVE_INFO(alignStr()" : %d\n", TypeKeyName, descriptionRecord.Type); + + if (isFieldMissed(foundFields, requiredFields, PluginConfigParser::PARSED_CODEC_ID)) + { + TRACE_HIVE_INFO(alignStr()" : " MFXFOURCCTYPE()" \n", CodecIDKeyName, MFXU32TOFOURCC(descriptionRecord.CodecId)); + } + else + { + TRACE_HIVE_INFO(alignStr()" : \n", CodecIDKeyName, "NOT REGISTERED"); + } + + if (isFieldMissed(foundFields, requiredFields, PluginConfigParser::PARSED_UID)) + { + return false; + } + TRACE_HIVE_INFO(alignStr()" : " MFXGUIDTYPE()"\n", GUIDKeyName, MFXGUIDTOHEX(&descriptionRecord.PluginUID)); + + if (isFieldMissed(foundFields, requiredFields, PluginConfigParser::PARSED_PATH)) + { + TRACE_HIVE_WRN("no value for : %s\n", PathKeyName); + return false; + } + TRACE_HIVE_INFO(alignStr()" : %s\n", PathKeyName, descriptionRecord.sPath); + + if (isFieldMissed(foundFields, requiredFields, PluginConfigParser::PARSED_DEFAULT)) + { + return false; + } + TRACE_HIVE_INFO(alignStr()" : %s\n", DefaultKeyName, descriptionRecord.Default ? "true" : "false"); + + if (isFieldMissed(foundFields, requiredFields, PluginConfigParser::PARSED_VERSION)) + { + TRACE_HIVE_ERROR(alignStr()" : %d, which is invalid\n", PlgVerKeyName, descriptionRecord.PluginVersion); + return false; + } + TRACE_HIVE_INFO(alignStr()" : %d\n", PlgVerKeyName, descriptionRecord.PluginVersion); + + if (isFieldMissed(foundFields, requiredFields, PluginConfigParser::PARSED_API_VERSION)) + { + TRACE_HIVE_ERROR(alignStr()" : %d.%d, which is invalid\n", APIVerKeyName, descriptionRecord.APIVersion.Major, descriptionRecord.APIVersion.Minor); + return false; + } + TRACE_HIVE_INFO(alignStr()" : %d.%d\n", APIVerKeyName, descriptionRecord.APIVersion.Major, descriptionRecord.APIVersion.Minor); + + return true; +} + +MFXPluginsInHive::MFXPluginsInHive(int, const msdk_disp_char* msdkLibSubKey, mfxVersion currentAPIVersion) + : MFXPluginStorageBase(currentAPIVersion) +{ + PluginConfigParser parser(rootPluginPath); + int numPlugins = parser.GetPluginCount(); + + if (numPlugins < 0) + { + TRACE_HIVE_ERROR("no plugin records found in %s\n", rootPluginPath); + return; + } + + try + { + resize(numPlugins); + } + catch (...) { + TRACE_HIVE_ERROR("new PluginDescriptionRecord[%d] threw an exception: \n", numPlugins); + return; + } + + for (int index = 0; index < numPlugins; index++, parser.AdvanceToNextPlugin()) + { + PluginDescriptionRecord descriptionRecord; + try + { + char pluginName[MAX_PLUGIN_NAME]; + bool nameRes = parser.GetCurrentPluginName(pluginName); + + mfxU32 foundFields = 0; + + bool infoRes = parser.ParsePluginParams(descriptionRecord, foundFields); + if (!nameRes || !infoRes) + { + TRACE_HIVE_WRN("unable to parse section # %d in %s\n", index, rootPluginPath); + continue; + } + TRACE_HIVE_INFO("Found Plugin: %s\n", pluginName); + + mfxU32 reqs = PluginConfigParser::PARSED_VERSION + | PluginConfigParser::PARSED_API_VERSION + | PluginConfigParser::PARSED_PATH + | PluginConfigParser::PARSED_UID; +// | PluginConfigParser::PARSED_NAME +// | PluginConfigParser::PARSED_TYPE +// | PluginConfigParser::PARSED_CODEC_ID +// | PluginConfigParser::PARSED_DEFAULT; + + if (CheckPluginRecord(descriptionRecord, foundFields, reqs)) + { + (*this)[index] = descriptionRecord; + } + else + { + TRACE_HIVE_WRN("Registration of plugin %s found, but missed some fields (mask 0x%x)\n", pluginName, reqs ^ foundFields); + } + } + catch (...) + { + TRACE_HIVE_ERROR("operator[](%d) = descriptionRecord; - threw exception \n", index); + } + } +} + + +static int plugin_name_filter(const struct dirent * name) +{ + if (pluginDirNameLen != strlen(name->d_name)) + return 0; + for (unsigned int i = 0; i < pluginDirNameLen; i++) + { + if (!isxdigit(name->d_name[i])) + return 0; + } + + return 1; +} + + +MFXPluginsInFS::MFXPluginsInFS(mfxVersion currentAPIVersion) + : MFXPluginStorageBase(currentAPIVersion) + , mIsVersionParsed() + , mIsAPIVersionParsed() +{ + char selfName[MAX_PLUGIN_PATH]; + ssize_t nRead = readlink("/proc/self/exe", selfName, sizeof(selfName) - 1); + if (nRead < 0) + { + TRACE_HIVE_ERROR("readlink(\"/proc/self/exe\") reported an error: %d\n", errno); + return; + } + selfName[nRead] = '\0'; + + char *lastSlashPos = strrchr(selfName, '/'); + if (!lastSlashPos) { + lastSlashPos = selfName; + } + mfxU32 executableDirLen = (mfxU32)(lastSlashPos - selfName) + slashLen; + if (executableDirLen + pluginDirNameLen + pluginCfgFileNameLen >= MAX_PLUGIN_PATH) + { + TRACE_HIVE_ERROR("MAX_PLUGIN_PATH which is %d, not enough to locate plugin path\n", MAX_PLUGIN_PATH); + return; + } + // strncpy(lastSlashPos + slashLen, pluginSearchPattern, MAX_PLUGIN_PATH - executableDirLen); + *lastSlashPos = 0; + + + dirent **namelist; + int n = scandir(selfName, &namelist, plugin_name_filter, alphasort); + if (n < 0) + { + TRACE_HIVE_ERROR("Error %d scanning application directory %s\n", errno, selfName); + } + else + { + for (int i = 0; i < n; i++) + { + PluginDescriptionRecord descriptionRecord; + descriptionRecord.onlyVersionRegistered = true; + char cfgName[MAX_PLUGIN_PATH]; + snprintf(cfgName, sizeof(cfgName), "%.512s/%.512s/%s", selfName, namelist[i]->d_name, pluginCfgFileName); + if ( strlen(selfName) + strlen("/") + strlen(namelist[i]->d_name) >= MAX_PLUGIN_PATH) + { + TRACE_HIVE_ERROR("buffer of MAX_PLUGIN_PATH characters which is %d, not enough to store plugin directory path: %s/%s\n", + MAX_PLUGIN_PATH, selfName, namelist[i]->d_name); + } + + strcpy(descriptionRecord.sPath, selfName); + strcpy(descriptionRecord.sPath + strlen(descriptionRecord.sPath), "/"); + strcpy(descriptionRecord.sPath + strlen(descriptionRecord.sPath), namelist[i]->d_name); + + if (!parseGUID(namelist[i]->d_name, descriptionRecord.PluginUID.Data)) + { + TRACE_HIVE_ERROR("directory name %s is not valid guid\n", namelist[i]->d_name); + continue; + } + free(namelist[i]); + + PluginConfigParser parser(cfgName); + int numPlugins = parser.GetPluginCount(); + + if (numPlugins < 0) + { + TRACE_HIVE_ERROR("no plugin records found in %s\n", cfgName); + continue; + } + if (numPlugins > 1) + { + TRACE_HIVE_ERROR("too many plugin records found in %s\n", cfgName); + continue; + } + + + try + { + char pluginName[MAX_PLUGIN_NAME]; + bool nameRes = parser.GetCurrentPluginName(pluginName); + if (!nameRes) + { + TRACE_HIVE_WRN("unable to parse plugin name from %s\n", cfgName); + } + + mfxU32 foundFields = PluginConfigParser::PARSED_UID; + + bool infoRes = parser.ParsePluginParams(descriptionRecord, foundFields); + if (!infoRes) + { + TRACE_HIVE_WRN("unable to parse plugin information in %s\n", cfgName); + continue; + } + TRACE_HIVE_INFO("Found Plugin: %s\n", pluginName); + + mfxU32 reqs = PluginConfigParser::PARSED_VERSION + | PluginConfigParser::PARSED_API_VERSION + | PluginConfigParser::PARSED_PATH + | PluginConfigParser::PARSED_UID; + + if (CheckPluginRecord(descriptionRecord, foundFields, reqs)) + { + push_back(descriptionRecord); + } + } + catch (...) + { + TRACE_HIVE_ERROR("push_back(descriptionRecord) - threw exception \n", index); + } + + } + free(namelist); + } +} + +MFXDefaultPlugins::MFXDefaultPlugins(mfxVersion currentAPIVersion, MFX_DISP_HANDLE * hdl, int implType) + : MFXPluginStorageBase(currentAPIVersion) +{ + msdk_disp_char libModuleName[MAX_PLUGIN_PATH]; + + Dl_info DlInfo; + int nRet; + + if ((nRet = dladdr((const void *)hdl->callTable[eMFXInit], &DlInfo)) == 0) + { + TRACE_HIVE_ERROR("dladdr() reported an error: %s\n", dlerror()); + return; + } + msdk_disp_char_cpy_s(libModuleName, sizeof(libModuleName), DlInfo.dli_fname); + + msdk_disp_char *lastSlashPos = strrchr(libModuleName, '/'); + if (!lastSlashPos) { + lastSlashPos = libModuleName; + } + mfxU32 executableDirLen = (mfxU32)(lastSlashPos - libModuleName) + slashLen; + if (executableDirLen + defaultPluginNameLen >= MAX_PLUGIN_PATH) + { + TRACE_HIVE_ERROR("MAX_PLUGIN_PATH which is %d, not enough to locate plugin path\n", MAX_PLUGIN_PATH); + return; + } + + mfx_get_default_plugin_name(lastSlashPos + slashLen, MAX_PLUGIN_PATH - executableDirLen, (eMfxImplType)implType); + + struct stat buffer; + if (stat (libModuleName, &buffer) == 0) + { + // add single default plugin description + PluginDescriptionRecord descriptionRecord; + descriptionRecord.APIVersion = currentAPIVersion; + descriptionRecord.Default = true; + + msdk_disp_char_cpy_s(descriptionRecord.sPath + , sizeof(descriptionRecord.sPath) / sizeof(*descriptionRecord.sPath), libModuleName); + + push_back(descriptionRecord); + } + else + { + TRACE_HIVE_INFO("stat() unable to locate default plugin dll named %s\n", libModuleName); + } +} + +} // namespace MFX + + diff --git a/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_win_reg_key.cpp b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_win_reg_key.cpp new file mode 100644 index 0000000..95bcde7 --- /dev/null +++ b/vshampor/deshuffler/msdk_api/opensource/mfx_dispatch/src/mfx_win_reg_key.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 2017 Intel Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + diff --git a/vshampor/deshuffler/sample_common/CMakeLists.txt b/vshampor/deshuffler/sample_common/CMakeLists.txt new file mode 100644 index 0000000..c0e0cf3 --- /dev/null +++ b/vshampor/deshuffler/sample_common/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories ( + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +file(GLOB_RECURSE SRC_LIST "*.cpp") + +add_library(sample_common STATIC ${SRC_LIST}) \ No newline at end of file diff --git a/vshampor/deshuffler/sample_common/include/abstract_splitter.h b/vshampor/deshuffler/sample_common/include/abstract_splitter.h new file mode 100644 index 0000000..47cf74d --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/abstract_splitter.h @@ -0,0 +1,72 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef _ABSTRACT_SPL_H__ +#define _ABSTRACT_SPL_H__ + +#include "mfxstructures.h" +#include "vm/strings_defs.h" + +enum SliceTypeCode { + TYPE_I = 0, + TYPE_P = 1, + TYPE_B = 2, + TYPE_SKIP=3, + TYPE_UNKNOWN=4 +}; + +struct SliceSplitterInfo +{ + mfxU32 DataOffset; + mfxU32 DataLength; + mfxU32 HeaderLength; + SliceTypeCode SliceType; +}; + +struct FrameSplitterInfo +{ + SliceSplitterInfo * Slice; // array + mfxU32 SliceNum; + mfxU32 FirstFieldSliceNum; + + mfxU8 * Data; // including data of slices + mfxU32 DataLength; + mfxU64 TimeStamp; +}; + +class AbstractSplitter +{ +public: + + AbstractSplitter() + {} + + virtual ~AbstractSplitter() + {} + + virtual mfxStatus Reset() = 0; + + virtual mfxStatus GetFrame(mfxBitstream * bs_in, FrameSplitterInfo ** frame) = 0; + + virtual mfxStatus PostProcessing(FrameSplitterInfo *frame, mfxU32 sliceNum) = 0; + + virtual void ResetCurrentState() = 0; +}; + +#endif // _ABSTRACT_SPL_H__ diff --git a/vshampor/deshuffler/sample_common/include/avc_bitstream.h b/vshampor/deshuffler/sample_common/include/avc_bitstream.h new file mode 100644 index 0000000..f84980f --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/avc_bitstream.h @@ -0,0 +1,232 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ +#ifndef __AVC_BITSTREAM_H_ +#define __AVC_BITSTREAM_H_ + +#include "mfxstructures.h" +#include "avc_structures.h" +#include "avc_headers.h" + +namespace ProtectedLibrary +{ + +#define AVCPeek1Bit(current_data, offset) \ + ((current_data[0] >> (offset)) & 1) + +#define AVCDrop1Bit(current_data, offset) \ +{ \ + offset -= 1; \ + if (offset < 0) \ + { \ + offset = 31; \ + current_data += 1; \ + } \ +} + +// NAL unit definitions +enum +{ + NAL_STORAGE_IDC_BITS = 0x60, + NAL_UNITTYPE_BITS = 0x1f +}; + +class AVCBaseBitstream +{ +public: + + AVCBaseBitstream(); + AVCBaseBitstream(mfxU8 * const pb, const mfxU32 maxsize); + virtual ~AVCBaseBitstream(); + + // Reset the bitstream with new data pointer + void Reset(mfxU8 * const pb, mfxU32 maxsize); + void Reset(mfxU8 * const pb, mfxI32 offset, mfxU32 maxsize); + + inline mfxU32 GetBits(mfxU32 nbits); + + // Read one VLC mfxI32 or mfxU32 value from bitstream + mfxI32 GetVLCElement(bool bIsSigned); + + // Reads one bit from the buffer. + inline mfxU32 Get1Bit(); + + // Check amount of data + bool More_RBSP_Data(); + + inline mfxU32 BytesDecoded(); + + inline mfxU32 BitsDecoded(); + + inline mfxU32 BytesLeft(); + + mfxStatus GetNALUnitType(NAL_Unit_Type &uNALUnitType, mfxU8 &uNALStorageIDC); + void AlignPointerRight(void); + +protected: + mfxU32 *m_pbs; // pointer to the current position of the buffer. + mfxI32 m_bitOffset; // the bit position (0 to 31) in the dword pointed by m_pbs. + mfxU32 *m_pbsBase; // pointer to the first byte of the buffer. + mfxU32 m_maxBsSize; // maximum buffer size in bytes. +}; + +class AVCHeadersBitstream : public AVCBaseBitstream +{ +public: + + AVCHeadersBitstream(); + AVCHeadersBitstream(mfxU8 * const pb, const mfxU32 maxsize); + + + // Decode sequence parameter set + mfxStatus GetSequenceParamSet(AVCSeqParamSet *sps); + // Decode sequence parameter set extension + mfxStatus GetSequenceParamSetExtension(AVCSeqParamSetExtension *sps_ex); + + // Decoding picture's parameter set functions + mfxStatus GetPictureParamSetPart1(AVCPicParamSet *pps); + mfxStatus GetPictureParamSetPart2(AVCPicParamSet *pps, const AVCSeqParamSet *sps); + + mfxStatus GetSliceHeaderPart1(AVCSliceHeader *pSliceHeader); + // Decoding slice header functions + mfxStatus GetSliceHeaderPart2(AVCSliceHeader *hdr, // slice header read goes here + const AVCPicParamSet *pps, + const AVCSeqParamSet *sps); // from slice header NAL unit + + mfxStatus GetSliceHeaderPart3(AVCSliceHeader *hdr, // slice header read goes here + PredWeightTable *pPredWeight_L0, // L0 weight table goes here + PredWeightTable *pPredWeight_L1, // L1 weight table goes here + RefPicListReorderInfo *pReorderInfo_L0, + RefPicListReorderInfo *pReorderInfo_L1, + AdaptiveMarkingInfo *pAdaptiveMarkingInfo, + const AVCPicParamSet *pps, + const AVCSeqParamSet *sps, + mfxU8 NALRef_idc); // from slice header NAL unit + + + mfxStatus GetNalUnitPrefix(AVCNalExtension *pExt, mfxU32 NALRef_idc); + + mfxI32 GetSEI(const HeaderSet & sps, mfxI32 current_sps, AVCSEIPayLoad *spl); + +private: + + mfxStatus GetNalUnitExtension(AVCNalExtension *pExt); + + void GetScalingList4x4(AVCScalingList4x4 *scl, mfxU8 *def, mfxU8 *scl_type); + void GetScalingList8x8(AVCScalingList8x8 *scl, mfxU8 *def, mfxU8 *scl_type); + + mfxI32 GetSEIPayload(const HeaderSet & sps, mfxI32 current_sps, AVCSEIPayLoad *spl); + mfxI32 recovery_point(const HeaderSet & sps, mfxI32 current_sps, AVCSEIPayLoad *spl); + mfxI32 reserved_sei_message(const HeaderSet & sps, mfxI32 current_sps, AVCSEIPayLoad *spl); + + mfxStatus GetVUIParam(AVCSeqParamSet *sps); + mfxStatus GetHRDParam(AVCSeqParamSet *sps); +}; + + +void SetDefaultScalingLists(AVCSeqParamSet * sps); + +extern const mfxU32 bits_data[]; + + +#define _avcGetBits(current_data, offset, nbits, data) \ +{ \ + mfxU32 x; \ + \ + SAMPLE_ASSERT((nbits) > 0 && (nbits) <= 32); \ + SAMPLE_ASSERT(offset >= 0 && offset <= 31); \ + \ + offset -= (nbits); \ + \ + if (offset >= 0) \ + { \ + x = current_data[0] >> (offset + 1); \ + } \ + else \ + { \ + offset += 32; \ + \ + x = current_data[1] >> (offset); \ + x >>= 1; \ + x += current_data[0] << (31 - offset); \ + current_data++; \ + } \ + \ + SAMPLE_ASSERT(offset >= 0 && offset <= 31); \ + \ + (data) = x & bits_data[nbits]; \ +} + +#define avcGetBits1(current_data, offset, data) \ +{ \ + data = ((current_data[0] >> (offset)) & 1); \ + offset -= 1; \ + if (offset < 0) \ + { \ + offset = 31; \ + current_data += 1; \ + } \ +} + +#define avcUngetNBits(current_data, offset, nbits) \ +{ \ + SAMPLE_ASSERT(offset >= 0 && offset <= 31); \ + \ + offset += (nbits); \ + if (offset > 31) \ + { \ + offset -= 32; \ + current_data--; \ + } \ + \ + SAMPLE_ASSERT(offset >= 0 && offset <= 31); \ +} + +#define avcGetNBits( current_data, offset, nbits, data) \ + _avcGetBits(current_data, offset, nbits, data); + +inline mfxU32 AVCBaseBitstream::GetBits(mfxU32 nbits) +{ + mfxU32 w, n = nbits; + + avcGetNBits(m_pbs, m_bitOffset, n, w); + return w; +} + +inline mfxU32 AVCBaseBitstream::Get1Bit() +{ + mfxU32 w; + avcGetBits1(m_pbs, m_bitOffset, w); + return w; + +} // AVCBitstream::Get1Bit() + +inline mfxU32 AVCBaseBitstream::BytesDecoded() +{ + return static_cast((mfxU8*)m_pbs - (mfxU8*)m_pbsBase) + + ((31 - m_bitOffset) >> 3); +} + +inline mfxU32 AVCBaseBitstream::BytesLeft() +{ + return ((mfxI32)m_maxBsSize - (mfxI32) BytesDecoded()); +} + +} // namespace ProtectedLibrary + +#endif // __AVC_BITSTREAM_H_ diff --git a/vshampor/deshuffler/sample_common/include/avc_headers.h b/vshampor/deshuffler/sample_common/include/avc_headers.h new file mode 100644 index 0000000..0bad30a --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/avc_headers.h @@ -0,0 +1,161 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __AVC_HEADERS_H +#define __AVC_HEADERS_H + +#include "avc_structures.h" +#include + +namespace ProtectedLibrary +{ + +template +class HeaderSet +{ +public: + + HeaderSet() + : m_currentID(-1) + { + } + + ~HeaderSet() + { + Reset(); + } + + void AddHeader(T* hdr) + { + mfxU32 id = hdr->GetID(); + if (id >= m_header.size()) + { + m_header.resize(id + 1); + } + + if (m_header[id]) + { + delete m_header[id]; + m_header[id]=0; + } + + m_header[id] = new T(); + *(m_header[id]) = *hdr; + } + + T * GetHeader(mfxU32 id) + { + if (id >= m_header.size()) + return 0; + + return m_header[id]; + } + + void RemoveHeader(mfxU32 id) + { + if (id >= m_header.size()) + return; + + delete m_header[id]; + m_header[id] = 0; + } + + void RemoveHeader(T * hdr) + { + if (!hdr) + return; + + RemoveHeader(hdr->GetID()); + } + + const T * GetHeader(mfxU32 id) const + { + if (id >= m_header.size()) + return 0; + + return m_header[id]; + } + + void Reset() + { + for (mfxU32 i = 0; i < m_header.size(); i++) + { + delete m_header[i]; + m_header[i]=0; + } + } + + void SetCurrentID(mfxU32 id) + { + m_currentID = id; + } + + mfxI32 GetCurrrentID() const + { + return m_currentID; + } + + T * GetCurrentHeader() + { + if (m_currentID == -1) + return 0; + + return GetHeader(m_currentID); + } + + const T * GetCurrentHeader() const + { + if (m_currentID == -1) + return 0; + + return GetHeader(m_currentID); + } + +private: + std::vector m_header; + mfxI32 m_currentID; +}; + +/****************************************************************************************************/ +// Headers stuff +/****************************************************************************************************/ +class AVCHeaders +{ +public: + + void Reset() + { + m_SeqParams.Reset(); + m_SeqExParams.Reset(); + m_SeqParamsMvcExt.Reset(); + m_PicParams.Reset(); + m_SEIParams.Reset(); + } + + HeaderSet m_SeqParams; + HeaderSet m_SeqExParams; + HeaderSet m_SeqParamsMvcExt; + HeaderSet m_PicParams; + HeaderSet m_SEIParams; + AVCNalExtension m_nalExtension; +}; + +} //namespace ProtectedLibrary + +#endif // __AVC_HEADERS_H diff --git a/vshampor/deshuffler/sample_common/include/avc_nal_spl.h b/vshampor/deshuffler/sample_common/include/avc_nal_spl.h new file mode 100644 index 0000000..5b49fd3 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/avc_nal_spl.h @@ -0,0 +1,101 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ +#ifndef __AVC_NAL_SPL_H +#define __AVC_NAL_SPL_H + +#include +#include "mfxstructures.h" + +namespace ProtectedLibrary +{ + +class BytesSwapper +{ +public: + static void SwapMemory(mfxU8 *pDestination, mfxU32 &nDstSize, mfxU8 *pSource, mfxU32 nSrcSize); +}; + + +class StartCodeIterator +{ +public: + + StartCodeIterator(); + + void Reset(); + + mfxI32 Init(mfxBitstream * source); + + void SetSuggestedSize(mfxU32 size); + + mfxI32 CheckNalUnitType(mfxBitstream * source); + + mfxI32 GetNALUnit(mfxBitstream * source, mfxBitstream * destination); + + mfxI32 EndOfStream(mfxBitstream * destination); + +private: + std::vector m_prev; + mfxU32 m_code; + mfxU64 m_pts; + + mfxU8 * m_pSource; + mfxU32 m_nSourceSize; + + mfxU8 * m_pSourceBase; + mfxU32 m_nSourceBaseSize; + + mfxU32 m_suggestedSize; + + mfxI32 FindStartCode(mfxU8 * (&pb), mfxU32 & size, mfxI32 & startCodeSize); +}; + +class NALUnitSplitter +{ +public: + + NALUnitSplitter(); + + virtual ~NALUnitSplitter(); + + virtual void Init(); + virtual void Release(); + + virtual mfxI32 CheckNalUnitType(mfxBitstream * source); + virtual mfxI32 GetNalUnits(mfxBitstream * source, mfxBitstream * &destination); + + virtual void Reset(); + + virtual void SetSuggestedSize(mfxU32 size) + { + m_pStartCodeIter.SetSuggestedSize(size); + } + +protected: + + StartCodeIterator m_pStartCodeIter; + + mfxBitstream m_bitstream; +}; + +void SwapMemoryAndRemovePreventingBytes(mfxU8 *pDestination, mfxU32 &nDstSize, mfxU8 *pSource, mfxU32 nSrcSize); + +} //namespace ProtectedLibrary + +#endif // __AVC_NAL_SPL_H diff --git a/vshampor/deshuffler/sample_common/include/avc_spl.h b/vshampor/deshuffler/sample_common/include/avc_spl.h new file mode 100644 index 0000000..34e1397 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/avc_spl.h @@ -0,0 +1,142 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef _AVC_SPL_H__ +#define _AVC_SPL_H__ + +#include +#include +#pragma warning(disable : 4201) +#include +#pragma warning(default : 4201) + +#include "abstract_splitter.h" + +#include "avc_bitstream.h" +#include "avc_headers.h" +#include "avc_nal_spl.h" + +namespace ProtectedLibrary +{ + +class AVCSlice : public SliceSplitterInfo +{ +public: + + AVCSlice(); + + AVCSliceHeader * GetSliceHeader(); + + bool IsField() const {return m_sliceHeader.field_pic_flag != 0;} + + mfxI32 RetrievePicParamSetNumber(mfxU8 *pSource, mfxU32 nSourceSize); + + bool DecodeHeader(mfxU8 *pSource, mfxU32 nSourceSize); + + AVCHeadersBitstream *GetBitStream(void){return &m_bitStream;} + + AVCPicParamSet* m_picParamSet; + AVCSeqParamSet* m_seqParamSet; + AVCSeqParamSet* m_seqParamSetMvcEx; + AVCSeqParamSetExtension* m_seqParamSetEx; + + mfxU64 m_dTime; + +protected: + AVCSliceHeader m_sliceHeader; + AVCHeadersBitstream m_bitStream; + + void Reset(); +}; + +class AVCFrameInfo +{ +public: + + AVCFrameInfo(); + + void Reset(); + + AVCSlice * m_slice; + mfxU32 m_index; +}; + +class AVC_Spl : public AbstractSplitter +{ +public: + + AVC_Spl(); + + virtual ~AVC_Spl(); + + virtual mfxStatus Reset(); + + virtual mfxStatus GetFrame(mfxBitstream * bs_in, FrameSplitterInfo ** frame); + + virtual mfxStatus PostProcessing(FrameSplitterInfo *frame, mfxU32 sliceNum); + + void ResetCurrentState(); + +protected: + std::unique_ptr m_pNALSplitter; + + mfxStatus Init(); + + void Close(); + + mfxStatus ProcessNalUnit(mfxI32 nalType, mfxBitstream * destination); + + mfxStatus DecodeHeader(mfxBitstream * nalUnit); + mfxStatus DecodeSEI(mfxBitstream * nalUnit); + AVCSlice * DecodeSliceHeader(mfxBitstream * nalUnit); + mfxStatus AddSlice(AVCSlice * pSlice); + + AVCFrameInfo * GetFreeFrame(); + + mfxU8 * GetMemoryForSwapping(mfxU32 size); + + mfxStatus AddNalUnit(mfxBitstream * nalUnit); + mfxStatus AddSliceNalUnit(mfxBitstream * nalUnit, AVCSlice * pSlice); + bool IsFieldOfOneFrame(AVCFrameInfo * frame, const AVCSliceHeader * slice1, const AVCSliceHeader *slice2); + + bool m_WaitForIDR; + + AVCHeaders m_headers; + std::unique_ptr m_AUInfo; + AVCFrameInfo * m_currentInfo; + AVCSlice * m_pLastSlice; + + mfxBitstream * m_lastNalUnit; + + enum + { + BUFFER_SIZE = 1024 * 1024 + }; + + std::vector m_currentFrame; + std::vector m_swappingMemory; + std::list m_slicesStorage; + + std::vector m_slices; + FrameSplitterInfo m_frame; +}; + +} // namespace ProtectedLibrary + +#endif // _AVC_SPL_H__ diff --git a/vshampor/deshuffler/sample_common/include/avc_structures.h b/vshampor/deshuffler/sample_common/include/avc_structures.h new file mode 100644 index 0000000..831c2d3 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/avc_structures.h @@ -0,0 +1,1104 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __AVC_STRUCTURES_H__ +#define __AVC_STRUCTURES_H__ + +#include +#include +#include +#include "mfxstructures.h" + +namespace ProtectedLibrary +{ + +enum +{ + AVC_PROFILE_BASELINE = 66, + AVC_PROFILE_MAIN = 77, + AVC_PROFILE_SCALABLE_BASELINE = 83, + AVC_PROFILE_SCALABLE_HIGH = 86, + AVC_PROFILE_EXTENDED = 88, + AVC_PROFILE_HIGH = 100, + AVC_PROFILE_HIGH10 = 110, + AVC_PROFILE_MULTIVIEW_HIGH = 118, + AVC_PROFILE_HIGH422 = 122, + AVC_PROFILE_STEREO_HIGH = 128, + AVC_PROFILE_HIGH444 = 144, + AVC_PROFILE_ADVANCED444_INTRA = 166, + AVC_PROFILE_ADVANCED444 = 188 +}; + +enum +{ + AVC_LEVEL_1 = 10, + AVC_LEVEL_11 = 11, + AVC_LEVEL_1b = 11, + AVC_LEVEL_12 = 12, + AVC_LEVEL_13 = 13, + + AVC_LEVEL_2 = 20, + AVC_LEVEL_21 = 21, + AVC_LEVEL_22 = 22, + + AVC_LEVEL_3 = 30, + AVC_LEVEL_31 = 31, + AVC_LEVEL_32 = 32, + + AVC_LEVEL_4 = 40, + AVC_LEVEL_41 = 41, + AVC_LEVEL_42 = 42, + + AVC_LEVEL_5 = 50, + AVC_LEVEL_51 = 51, + AVC_LEVEL_MAX = 51, + + AVC_LEVEL_9 = 9 // for SVC profiles +}; + +// Although the standard allows for a minimum width or height of 4, this +// implementation restricts the minimum value to 32. + +enum // Valid QP range +{ + AVC_QP_MAX = 51, + AVC_QP_MIN = 0 +}; + +enum { + FLD_STRUCTURE = 0, + TOP_FLD_STRUCTURE = 0, + BOTTOM_FLD_STRUCTURE = 1, + FRM_STRUCTURE = 2, + AFRM_STRUCTURE = 3 +}; + +enum DisplayPictureStruct { + DPS_FRAME = 0, + DPS_TOP, // one field + DPS_BOTTOM, // one field + DPS_TOP_BOTTOM, + DPS_BOTTOM_TOP, + DPS_TOP_BOTTOM_TOP, + DPS_BOTTOM_TOP_BOTTOM, + DPS_FRAME_DOUBLING, + DPS_FRAME_TRIPLING +}; + +typedef enum { + NAL_UT_UNSPECIFIED = 0, // Unspecified + NAL_UT_SLICE = 1, // Coded Slice - slice_layer_no_partioning_rbsp + NAL_UT_DPA = 2, // Coded Data partition A - dpa_layer_rbsp + NAL_UT_DPB = 3, // Coded Data partition A - dpa_layer_rbsp + NAL_UT_DPC = 4, // Coded Data partition A - dpa_layer_rbsp + NAL_UT_IDR_SLICE = 5, // Coded Slice of a IDR Picture - slice_layer_no_partioning_rbsp + NAL_UT_SEI = 6, // Supplemental Enhancement Information - sei_rbsp + NAL_UT_SPS = 7, // Sequence Parameter Set - seq_parameter_set_rbsp + NAL_UT_PPS = 8, // Picture Parameter Set - pic_parameter_set_rbsp + NAL_UT_AUD = 9, // Access Unit Delimiter - access_unit_delimiter_rbsp + NAL_END_OF_SEQ = 10, // End of sequence end_of_seq_rbsp() + NAL_END_OF_STREAM = 11, // End of stream end_of_stream_rbsp + NAL_UT_FD = 12, // Filler Data - filler_data_rbsp + NAL_UT_SPS_EX = 13, // Sequence Parameter Set Extension - seq_parameter_set_extension_rbsp + NAL_UNIT_PREFIX = 14, // Prefix NAL unit in scalable extension - prefix_nal_unit_rbsp + NAL_UNIT_SUBSET_SPS = 15, // Subset Sequence Parameter Set - subset_seq_parameter_set_rbsp + NAL_UT_AUXILIARY = 19, // Auxiliary coded picture + NAL_UT_CODED_SLICE_EXTENSION = 20 // Coded slice in scalable extension - slice_layer_in_scalable_extension_rbsp +} NAL_Unit_Type; + +// Note! The Picture Code Type values below are no longer used in the +// core encoder. It only knows about slice types, and whether or not +// the frame is IDR, Reference or Disposable. See enum above. + +enum EnumSliceCodType // Permitted MB Prediction Types +{ // ------------------------------------ + PREDSLICE = 0, // I (Intra), P (Pred) + BPREDSLICE = 1, // I, P, B (BiPred) + INTRASLICE = 2, // I + S_PREDSLICE = 3, // SP (SPred), I + S_INTRASLICE = 4 // SI (SIntra), I +}; + +typedef enum +{ + SEI_BUFFERING_PERIOD_TYPE = 0, + SEI_PIC_TIMING_TYPE = 1, + SEI_PAN_SCAN_RECT_TYPE = 2, + SEI_FILLER_TYPE = 3, + SEI_USER_DATA_REGISTERED_TYPE = 4, + SEI_USER_DATA_UNREGISTERED_TYPE = 5, + SEI_RECOVERY_POINT_TYPE = 6, + SEI_DEC_REF_PIC_MARKING_TYPE = 7, + SEI_SPARE_PIC_TYPE = 8, + SEI_SCENE_INFO_TYPE = 9, + SEI_SUB_SEQ_INFO_TYPE = 10, + SEI_SUB_SEQ_LAYER_TYPE = 11, + SEI_SUB_SEQ_TYPE = 12, + SEI_FULL_FRAME_FREEZE_TYPE = 13, + SEI_FULL_FRAME_FREEZE_RELEASE_TYPE = 14, + SEI_FULL_FRAME_SNAPSHOT_TYPE = 15, + SEI_PROGRESSIVE_REF_SEGMENT_START_TYPE = 16, + SEI_PROGRESSIVE_REF_SEGMENT_END_TYPE = 17, + SEI_MOTION_CONSTRAINED_SG_SET_TYPE = 18, + SEI_RESERVED = 19 +} SEI_TYPE; + + +#define IS_I_SLICE(SliceType) ((SliceType) == INTRASLICE) +#define IS_P_SLICE(SliceType) ((SliceType) == PREDSLICE || (SliceType) == S_PREDSLICE) +#define IS_B_SLICE(SliceType) ((SliceType) == BPREDSLICE) + +enum +{ + MAX_NUM_SEQ_PARAM_SETS = 32, + MAX_NUM_PIC_PARAM_SETS = 256, + + MAX_SLICE_NUM = 128, //INCREASE IF NEEDED OR SET to -1 for adaptive counting (increases memory usage) + MAX_NUM_REF_FRAMES = 32, + + MAX_REF_FRAMES_IN_POC_CYCLE = 256, + + MAX_NUM_SLICE_GROUPS = 8, + MAX_SLICE_GROUP_MAP_TYPE = 6, + + NUM_INTRA_TYPE_ELEMENTS = 16, + + COEFFICIENTS_BUFFER_SIZE = 16 * 51, + + MINIMAL_DATA_SIZE = 4 +}; + +// Possible values for disable_deblocking_filter_idc: +enum DeblockingModes_t +{ + DEBLOCK_FILTER_ON = 0, + DEBLOCK_FILTER_OFF = 1, + DEBLOCK_FILTER_ON_NO_SLICE_EDGES = 2 +}; + +#pragma pack(1) + +struct AVCScalingList4x4 +{ + mfxU8 ScalingListCoeffs[16]; +}; + +struct AVCScalingList8x8 +{ + mfxU8 ScalingListCoeffs[64]; +}; + +struct AVCWholeQPLevelScale4x4 +{ + mfxI16 LevelScaleCoeffs[88]/*since we do not support 422 and 444*/[16]; +}; +struct AVCWholeQPLevelScale8x8 +{ + mfxI16 LevelScaleCoeffs[88]/*since we do not support 422 and 444*/[64]; +}; + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Memory class +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class RefCounter +{ +public: + + RefCounter() : m_refCounter(0) + { + } + + virtual ~RefCounter() + { + } + + void IncrementReference() const + { + m_refCounter++; + } + + void DecrementReference() + { + m_refCounter--; + + if (!m_refCounter) + { + Free(); + } + } + + void ResetRefCounter() {m_refCounter = 0;} + + mfxU32 GetRefCounter() {return m_refCounter;} + +protected: + mutable mfxI32 m_refCounter; + + virtual void Free() + { + } +}; + +class HeapObject : public RefCounter +{ +public: + + virtual ~HeapObject() {} + + virtual void Reset() + { + } + + virtual void Free(); +}; + +#pragma pack() + +#pragma pack(16) + +typedef mfxU32 IntraType; + +// Sequence parameter set structure, corresponding to the H.264 bitstream definition. +struct AVCSeqParamSetBase +{ + mfxU8 profile_idc; // baseline, main, etc. + mfxU8 level_idc; + mfxU8 constrained_set0_flag; + mfxU8 constrained_set1_flag; + mfxU8 constrained_set2_flag; + mfxU8 constrained_set3_flag; + mfxU8 chroma_format_idc; + mfxU8 residual_colour_transform_flag; + mfxU8 bit_depth_luma; + mfxU8 bit_depth_chroma; + mfxU8 qpprime_y_zero_transform_bypass_flag; + mfxU8 type_of_scaling_list_used[8]; + mfxU8 seq_scaling_matrix_present_flag; + AVCScalingList4x4 ScalingLists4x4[6]; + AVCScalingList8x8 ScalingLists8x8[2]; + mfxU8 gaps_in_frame_num_value_allowed_flag; + mfxU8 frame_cropping_flag; + mfxU32 frame_cropping_rect_left_offset; + mfxU32 frame_cropping_rect_right_offset; + mfxU32 frame_cropping_rect_top_offset; + mfxU32 frame_cropping_rect_bottom_offset; + mfxU8 more_than_one_slice_group_allowed_flag; + mfxU8 arbitrary_slice_order_allowed_flag; // If zero, slice order in pictures must + // be in increasing MB address order. + mfxU8 redundant_pictures_allowed_flag; + mfxU8 seq_parameter_set_id; // id of this sequence parameter set + mfxU8 log2_max_frame_num; // Number of bits to hold the frame_num + mfxU8 pic_order_cnt_type; // Picture order counting method + + mfxU8 delta_pic_order_always_zero_flag; // If zero, delta_pic_order_cnt fields are + // present in slice header. + mfxU8 frame_mbs_only_flag; // Nonzero indicates all pictures in sequence + // are coded as frames (not fields). + mfxU8 required_frame_num_update_behavior_flag; + + mfxU8 mb_adaptive_frame_field_flag; // Nonzero indicates frame/field switch + // at macroblock level + mfxU8 direct_8x8_inference_flag; // Direct motion vector derivation method + mfxU8 vui_parameters_present_flag; // Zero indicates default VUI parameters + mfxU32 log2_max_pic_order_cnt_lsb; // Value of MaxPicOrderCntLsb. + mfxI32 offset_for_non_ref_pic; + + mfxI32 offset_for_top_to_bottom_field; // Expected pic order count difference from + // top field to bottom field. + + mfxU32 num_ref_frames_in_pic_order_cnt_cycle; + mfxU32 num_ref_frames; // total number of pics in decoded pic buffer + mfxU32 frame_width_in_mbs; + mfxU32 frame_height_in_mbs; + + // These fields are calculated from values above. They are not written to the bitstream + mfxU32 MaxMbAddress; + mfxU32 MaxPicOrderCntLsb; + // vui part + mfxU8 aspect_ratio_info_present_flag; + mfxU8 aspect_ratio_idc; + mfxU16 sar_width; + mfxU16 sar_height; + mfxU8 overscan_info_present_flag; + mfxU8 overscan_appropriate_flag; + mfxU8 video_signal_type_present_flag; + mfxU8 video_format; + mfxU8 video_full_range_flag; + mfxU8 colour_description_present_flag; + mfxU8 colour_primaries; + mfxU8 transfer_characteristics; + mfxU8 matrix_coefficients; + mfxU8 chroma_loc_info_present_flag; + mfxU8 chroma_sample_loc_type_top_field; + mfxU8 chroma_sample_loc_type_bottom_field; + mfxU8 timing_info_present_flag; + mfxU32 num_units_in_tick; + mfxU32 time_scale; + mfxU8 fixed_frame_rate_flag; + mfxU8 nal_hrd_parameters_present_flag; + mfxU8 vcl_hrd_parameters_present_flag; + mfxU8 low_delay_hrd_flag; + mfxU8 pic_struct_present_flag; + mfxU8 bitstream_restriction_flag; + mfxU8 motion_vectors_over_pic_boundaries_flag; + mfxU8 max_bytes_per_pic_denom; + mfxU8 max_bits_per_mb_denom; + mfxU8 log2_max_mv_length_horizontal; + mfxU8 log2_max_mv_length_vertical; + mfxU8 num_reorder_frames; + mfxU8 max_dec_frame_buffering; + //hrd_parameters + mfxU8 cpb_cnt; + mfxU8 bit_rate_scale; + mfxU8 cpb_size_scale; + mfxU32 bit_rate_value[32]; + mfxU32 cpb_size_value[32]; + mfxU8 cbr_flag[32]; + mfxU8 initial_cpb_removal_delay_length; + mfxU8 cpb_removal_delay_length; + mfxU8 dpb_output_delay_length; + mfxU8 time_offset_length; + + mfxI32 poffset_for_ref_frame[MAX_REF_FRAMES_IN_POC_CYCLE]; // for pic order cnt type 1 + // length num_stored_frames_in_pic_order_cnt_cycle, + void Reset() + { + AVCSeqParamSetBase sps = {0}; + *this = sps; + } + +}; // AVCSeqParamSetBase + +// Sequence parameter set structure, corresponding to the H.264 bitstream definition. +struct AVCSeqParamSet : public HeapObject, public AVCSeqParamSetBase +{ + AVCSeqParamSet() + : HeapObject() + , AVCSeqParamSetBase() + { + Reset(); + } + + ~AVCSeqParamSet() + { + } + + mfxI32 GetID() const + { + return seq_parameter_set_id; + } + + virtual void Reset() + { + AVCSeqParamSetBase::Reset(); + + seq_parameter_set_id = MAX_NUM_SEQ_PARAM_SETS; + + // set some parameters by default + video_format = 5; // unspecified + video_full_range_flag = 0; + colour_primaries = 2; // unspecified + transfer_characteristics = 2; // unspecified + matrix_coefficients = 2; // unspecified + } +}; // AVCSeqParamSet + +// Sequence parameter set extension structure, corresponding to the H.264 bitstream definition. +struct AVCSeqParamSetExtension +{ + mfxU8 seq_parameter_set_id; + mfxU8 aux_format_idc; + mfxU8 bit_depth_aux; + mfxU8 alpha_incr_flag; + mfxU8 alpha_opaque_value; + mfxU8 alpha_transparent_value; + mfxU8 additional_extension_flag; + + AVCSeqParamSetExtension() + { + Reset(); + } + + virtual void Reset() + { + aux_format_idc = 0; + seq_parameter_set_id = MAX_NUM_SEQ_PARAM_SETS; // illegal id + bit_depth_aux = 0; + alpha_incr_flag = 0; + alpha_opaque_value = 0; + alpha_transparent_value = 0; + additional_extension_flag = 0; + } + + mfxI32 GetID() const + { + return seq_parameter_set_id; + } + + virtual ~AVCSeqParamSetExtension(){} + +}; // AVCSeqParamSetExtension + +// Picture parameter set structure, corresponding to the H.264 bitstream definition. +struct AVCPicParamSetBase +{ +// Flexible macroblock order structure, defining the FMO map for a picture +// paramter set. + + struct SliceGroupInfoStruct + { + mfxU8 slice_group_map_type; // 0..6 + + // The additional slice group data depends upon map type + union + { + // type 0 + mfxU32 run_length[MAX_NUM_SLICE_GROUPS]; + + // type 2 + struct + { + mfxU32 top_left[MAX_NUM_SLICE_GROUPS-1]; + mfxU32 bottom_right[MAX_NUM_SLICE_GROUPS-1]; + }t1; + + // types 3-5 + struct + { + mfxU8 slice_group_change_direction_flag; + mfxU32 slice_group_change_rate; + }t2; + + // type 6 + struct + { + mfxU32 pic_size_in_map_units; // number of macroblocks if no field coding + }t3; + }; + + std::vector pSliceGroupIDMap; // Id for each slice group map unit (for t3 struct of) + }; // SliceGroupInfoStruct + + mfxU16 pic_parameter_set_id; // of this picture parameter set + mfxU8 seq_parameter_set_id; // of seq param set used for this pic param set + mfxU8 entropy_coding_mode; // zero: CAVLC, else CABAC + + mfxU8 pic_order_present_flag; // Zero indicates only delta_pic_order_cnt[0] is + // present in slice header; nonzero indicates + // delta_pic_order_cnt[1] is also present. + + mfxU8 weighted_pred_flag; // Nonzero indicates weighted prediction applied to + // P and SP slices + mfxU8 weighted_bipred_idc; // 0: no weighted prediction in B slices + // 1: explicit weighted prediction + // 2: implicit weighted prediction + mfxI8 pic_init_qp; // default QP for I,P,B slices + mfxI8 pic_init_qs; // default QP for SP, SI slices + + mfxI8 chroma_qp_index_offset[2]; // offset to add to QP for chroma + + mfxU8 deblocking_filter_variables_present_flag; // If nonzero, deblock filter params are + // present in the slice header. + mfxU8 constrained_intra_pred_flag; // Nonzero indicates constrained intra mode + + mfxU8 redundant_pic_cnt_present_flag; // Nonzero indicates presence of redundant_pic_cnt + // in slice header + mfxU32 num_slice_groups; // One: no FMO + mfxU32 num_ref_idx_l0_active; // num of ref pics in list 0 used to decode the picture + mfxU32 num_ref_idx_l1_active; // num of ref pics in list 1 used to decode the picture + mfxU8 transform_8x8_mode_flag; + mfxU8 type_of_scaling_list_used[8]; + + AVCScalingList4x4 ScalingLists4x4[6]; + AVCScalingList8x8 ScalingLists8x8[2]; + + // Level Scale addition + AVCWholeQPLevelScale4x4 m_LevelScale4x4[6]; + AVCWholeQPLevelScale8x8 m_LevelScale8x8[2]; + + SliceGroupInfoStruct SliceGroupInfo; // Used only when num_slice_groups > 1 + + void Reset() + { + AVCPicParamSetBase pps = {0}; + *this = pps; + } +}; // AVCPicParamSet + +// Picture parameter set structure, corresponding to the H.264 bitstream definition. +struct AVCPicParamSet : public HeapObject, public AVCPicParamSetBase +{ + AVCPicParamSet() + : AVCPicParamSetBase() + { + Reset(); + } + + void Reset() + { + AVCPicParamSetBase::Reset(); + + pic_parameter_set_id = MAX_NUM_PIC_PARAM_SETS; + seq_parameter_set_id = MAX_NUM_SEQ_PARAM_SETS; + num_slice_groups = 0; + SliceGroupInfo.pSliceGroupIDMap.clear(); + } + + ~AVCPicParamSet() + { + } + + mfxI32 GetID() const + { + return pic_parameter_set_id; + } + +}; // H264PicParamSet + +struct RefPicListReorderInfo +{ + mfxU32 num_entries; // number of currently valid idc,value pairs + mfxU8 reordering_of_pic_nums_idc[MAX_NUM_REF_FRAMES]; + mfxU32 reorder_value[MAX_NUM_REF_FRAMES]; // abs_diff_pic_num or long_term_pic_num +}; + +struct AdaptiveMarkingInfo +{ + mfxU32 num_entries; // number of currently valid mmco,value pairs + mfxU8 mmco[MAX_NUM_REF_FRAMES]; // memory management control operation id + mfxU32 value[MAX_NUM_REF_FRAMES*2]; // operation-dependent data, max 2 per operation +}; + +struct PredWeightTable +{ + mfxU8 luma_weight_flag; // nonzero: luma weight and offset in bitstream + mfxU8 chroma_weight_flag; // nonzero: chroma weight and offset in bitstream + mfxI8 luma_weight; // luma weighting factor + mfxI8 luma_offset; // luma weighting offset + mfxI8 chroma_weight[2]; // chroma weighting factor (Cb,Cr) + mfxI8 chroma_offset[2]; // chroma weighting offset (Cb,Cr) +}; // PredWeightTable + +typedef mfxI32 AVCDecoderMBAddr; +// NAL unit SVC extension structure +struct AVCNalSvcExtension +{ + mfxU8 idr_flag; + mfxU8 priority_id; + mfxU8 no_inter_layer_pred_flag; + mfxU8 dependency_id; + mfxU8 quality_id; + mfxU8 temporal_id; + mfxU8 use_ref_base_pic_flag; + mfxU8 discardable_flag; + mfxU8 output_flag; + + mfxU8 store_ref_base_pic_flag; + mfxU8 adaptive_ref_base_pic_marking_mode_flag; + AdaptiveMarkingInfo adaptiveMarkingInfo; +}; + +// NAL unit SVC extension structure +struct AVCNalMvcExtension +{ + mfxU8 non_idr_flag; + mfxU16 priority_id; + // view_id variable is duplicated to the slice header itself + mfxU16 view_id; + mfxU8 temporal_id; + mfxU8 anchor_pic_flag; + mfxU8 inter_view_flag; + + mfxU8 padding[3]; +}; + +// NAL unit extension structure +struct AVCNalExtension +{ + mfxU8 extension_present; + + // equal to 1 specifies that NAL extension contains SVC related parameters + mfxU8 svc_extension_flag; + + union + { + AVCNalSvcExtension svc; + AVCNalMvcExtension mvc; + }; +}; + +// Slice header structure, corresponding to the H.264 bitstream definition. +struct AVCSliceHeader +{ + // flag equal 1 means that the slice belong to IDR or anchor access unit + mfxU32 IdrPicFlag; + + // specified that NAL unit contains any information accessed from + // the decoding process of other NAL units. + mfxU8 nal_ref_idc; + // specifies the type of RBSP data structure contained in the NAL unit as + // specified in Table 7-1 of h264 standard + NAL_Unit_Type nal_unit_type; + + // NAL unit extension parameters + AVCNalExtension nal_ext; + mfxU32 view_id; + + mfxU16 pic_parameter_set_id; // of pic param set used for this slice + mfxU8 field_pic_flag; // zero: frame picture, else field picture + mfxU8 MbaffFrameFlag; + mfxU8 bottom_field_flag; // zero: top field, else bottom field + mfxU8 direct_spatial_mv_pred_flag; // zero: temporal direct, else spatial direct + mfxU8 num_ref_idx_active_override_flag; // nonzero: use ref_idx_active from slice header + // instead of those from pic param set + mfxU8 no_output_of_prior_pics_flag; // nonzero: remove previously decoded pictures + // from decoded picture buffer + mfxU8 long_term_reference_flag; // How to set MaxLongTermFrameIdx + mfxU32 cabac_init_idc; // CABAC initialization table index (0..2) + mfxU8 adaptive_ref_pic_marking_mode_flag; // Ref pic marking mode of current picture + mfxI32 slice_qp_delta; // to calculate default slice QP + mfxU8 sp_for_switch_flag; // SP slice decoding control + mfxI32 slice_qs_delta; // to calculate default SP,SI slice QS + mfxU32 disable_deblocking_filter_idc; // deblock filter control, 0=filter all edges + mfxI32 slice_alpha_c0_offset; // deblock filter c0, alpha table offset + mfxI32 slice_beta_offset; // deblock filter beta table offset + AVCDecoderMBAddr first_mb_in_slice; + mfxI32 frame_num; + EnumSliceCodType slice_type; + mfxU32 idr_pic_id; // ID of an IDR picture + mfxI32 pic_order_cnt_lsb; // picture order count (mod MaxPicOrderCntLsb) + mfxI32 delta_pic_order_cnt_bottom; // Pic order count difference, top & bottom fields + mfxU32 difference_of_pic_nums; // Ref pic memory mgmt + mfxU32 long_term_pic_num; // Ref pic memory mgmt + mfxU32 long_term_frame_idx; // Ref pic memory mgmt + mfxU32 max_long_term_frame_idx; // Ref pic memory mgmt + mfxI32 delta_pic_order_cnt[2]; // picture order count differences + mfxU32 redundant_pic_cnt; // for redundant slices + mfxI32 num_ref_idx_l0_active; // num of ref pics in list 0 used to decode the slice, + // see num_ref_idx_active_override_flag + mfxI32 num_ref_idx_l1_active; // num of ref pics in list 1 used to decode the slice + // see num_ref_idx_active_override_flag + mfxU32 slice_group_change_cycle; // for FMO + mfxU8 luma_log2_weight_denom; // luma weighting denominator + mfxU8 chroma_log2_weight_denom; // chroma weighting denominator + + bool is_auxiliary; +}; // AVCSliceHeader + + +struct AVCSEIPayLoadBase +{ + SEI_TYPE payLoadType; + mfxU32 payLoadSize; + + union SEIMessages + { + struct BufferingPeriod + { + mfxU32 initial_cbp_removal_delay[2][16]; + mfxU32 initial_cbp_removal_delay_offset[2][16]; + }buffering_period; + + struct PicTiming + { + mfxU32 cbp_removal_delay; + mfxU32 dpb_ouput_delay; + DisplayPictureStruct pic_struct; + mfxU8 clock_timestamp_flag[16]; + struct ClockTimestamps + { + mfxU8 ct_type; + mfxU8 nunit_field_based_flag; + mfxU8 counting_type; + mfxU8 full_timestamp_flag; + mfxU8 discontinuity_flag; + mfxU8 cnt_dropped_flag; + mfxU8 n_frames; + mfxU8 seconds_value; + mfxU8 minutes_value; + mfxU8 hours_value; + mfxU8 time_offset; + }clock_timestamps[16]; + }pic_timing; + + struct PanScanRect + { + mfxU8 pan_scan_rect_id; + mfxU8 pan_scan_rect_cancel_flag; + mfxU8 pan_scan_cnt; + mfxU32 pan_scan_rect_left_offset[32]; + mfxU32 pan_scan_rect_right_offset[32]; + mfxU32 pan_scan_rect_top_offset[32]; + mfxU32 pan_scan_rect_bottom_offset[32]; + mfxU8 pan_scan_rect_repetition_period; + }pan_scan_rect; + + struct UserDataRegistered + { + mfxU8 itu_t_t35_country_code; + mfxU8 itu_t_t35_country_code_extension_byte; + } user_data_registered; + + struct RecoveryPoint + { + mfxU8 recovery_frame_cnt; + mfxU8 exact_match_flag; + mfxU8 broken_link_flag; + mfxU8 changing_slice_group_idc; + }recovery_point; + + struct DecRefPicMarkingRepetition + { + mfxU8 original_idr_flag; + mfxU8 original_frame_num; + mfxU8 original_field_pic_flag; + mfxU8 original_bottom_field_flag; + mfxU8 long_term_reference_flag; + AdaptiveMarkingInfo adaptiveMarkingInfo; + }dec_ref_pic_marking_repetition; + + struct SparePic + { + mfxU32 target_frame_num; + mfxU8 spare_field_flag; + mfxU8 target_bottom_field_flag; + mfxU8 num_spare_pics; + mfxU8 delta_spare_frame_num[16]; + mfxU8 spare_bottom_field_flag[16]; + mfxU8 spare_area_idc[16]; + mfxU8 *spare_unit_flag[16]; + mfxU8 *zero_run_length[16]; + }spare_pic; + + struct SceneInfo + { + mfxU8 scene_info_present_flag; + mfxU8 scene_id; + mfxU8 scene_transition_type; + mfxU8 second_scene_id; + }scene_info; + + struct SubSeqInfo + { + mfxU8 sub_seq_layer_num; + mfxU8 sub_seq_id; + mfxU8 first_ref_pic_flag; + mfxU8 leading_non_ref_pic_flag; + mfxU8 last_pic_flag; + mfxU8 sub_seq_frame_num_flag; + mfxU8 sub_seq_frame_num; + }sub_seq_info; + + struct SubSeqLayerCharacteristics + { + mfxU8 num_sub_seq_layers; + mfxU8 accurate_statistics_flag[16]; + mfxU16 average_bit_rate[16]; + mfxU16 average_frame_rate[16]; + }sub_seq_layer_characteristics; + + struct SubSeqCharacteristics + { + mfxU8 sub_seq_layer_num; + mfxU8 sub_seq_id; + mfxU8 duration_flag; + mfxU8 sub_seq_duration; + mfxU8 average_rate_flag; + mfxU8 accurate_statistics_flag; + mfxU16 average_bit_rate; + mfxU16 average_frame_rate; + mfxU8 num_referenced_subseqs; + mfxU8 ref_sub_seq_layer_num[16]; + mfxU8 ref_sub_seq_id[16]; + mfxU8 ref_sub_seq_direction[16]; + }sub_seq_characteristics; + + struct FullFrameFreeze + { + mfxU32 full_frame_freeze_repetition_period; + }full_frame_freeze; + + struct FullFrameSnapshot + { + mfxU8 snapshot_id; + }full_frame_snapshot; + + struct ProgressiveRefinementSegmentStart + { + mfxU8 progressive_refinement_id; + mfxU8 num_refinement_steps; + }progressive_refinement_segment_start; + + struct MotionConstrainedSliceGroupSet + { + mfxU8 num_slice_groups_in_set; + mfxU8 slice_group_id[8]; + mfxU8 exact_sample_value_match_flag; + mfxU8 pan_scan_rect_flag; + mfxU8 pan_scan_rect_id; + }motion_constrained_slice_group_set; + + struct FilmGrainCharacteristics + { + mfxU8 film_grain_characteristics_cancel_flag; + mfxU8 model_id; + mfxU8 separate_colour_description_present_flag; + mfxU8 film_grain_bit_depth_luma; + mfxU8 film_grain_bit_depth_chroma; + mfxU8 film_grain_full_range_flag; + mfxU8 film_grain_colour_primaries; + mfxU8 film_grain_transfer_characteristics; + mfxU8 film_grain_matrix_coefficients; + mfxU8 blending_mode_id; + mfxU8 log2_scale_factor; + mfxU8 comp_model_present_flag[3]; + mfxU8 num_intensity_intervals[3]; + mfxU8 num_model_values[3]; + mfxU8 intensity_interval_lower_bound[3][256]; + mfxU8 intensity_interval_upper_bound[3][256]; + mfxU8 comp_model_value[3][3][256]; + mfxU8 film_grain_characteristics_repetition_period; + }film_grain_characteristics; + + struct DeblockingFilterDisplayPreference + { + mfxU8 deblocking_display_preference_cancel_flag; + mfxU8 display_prior_to_deblocking_preferred_flag; + mfxU8 dec_frame_buffering_constraint_flag; + mfxU8 deblocking_display_preference_repetition_period; + }deblocking_filter_display_preference; + + struct StereoVideoInfo + { + mfxU8 field_views_flag; + mfxU8 top_field_is_left_view_flag; + mfxU8 current_frame_is_left_view_flag; + mfxU8 next_frame_is_second_view_flag; + mfxU8 left_view_self_contained_flag; + mfxU8 right_view_self_contained_flag; + }stereo_video_info; + + }SEI_messages; + + void Reset() + { + memset(this, 0, sizeof(AVCSEIPayLoadBase)); + + payLoadType = SEI_RESERVED; + payLoadSize = 0; + } +}; + +struct AVCSEIPayLoad : public HeapObject, public AVCSEIPayLoadBase +{ + std::vector user_data; // for UserDataRegistered or UserDataUnRegistered + + AVCSEIPayLoad() + : AVCSEIPayLoadBase() + { + } + + virtual void Reset() + { + AVCSEIPayLoadBase::Reset(); + user_data.clear(); + } + + mfxI32 GetID() const + { + return payLoadType; + } +}; + +#pragma pack() + +// This file defines some data structures and constants used by the decoder, +// that are also needed by other classes, such as post filters and +// error concealment. + +#define INTERP_FACTOR 4 +#define INTERP_SHIFT 2 + +#define CHROMA_INTERP_FACTOR 8 +#define CHROMA_INTERP_SHIFT 3 + +// at picture edge, clip motion vectors to only this far beyond the edge, +// in pixel units. +#define D_MV_CLIP_LIMIT 19 + +enum Direction_t{ + D_DIR_FWD = 0, + D_DIR_BWD = 1, + D_DIR_BIDIR = 2, + D_DIR_DIRECT = 3, + D_DIR_DIRECT_SPATIAL_FWD = 4, + D_DIR_DIRECT_SPATIAL_BWD = 5, + D_DIR_DIRECT_SPATIAL_BIDIR = 6 +}; + +inline bool IsForwardOnly(mfxI32 direction) +{ + return (direction == D_DIR_FWD) || (direction == D_DIR_DIRECT_SPATIAL_FWD); +} + +inline bool IsHaveForward(mfxI32 direction) +{ + return (direction == D_DIR_FWD) || (direction == D_DIR_BIDIR) || + (direction == D_DIR_DIRECT_SPATIAL_FWD) || (direction == D_DIR_DIRECT_SPATIAL_BIDIR) || + (direction == D_DIR_DIRECT); +} + +inline bool IsBackwardOnly(mfxI32 direction) +{ + return (direction == D_DIR_BWD) || (direction == D_DIR_DIRECT_SPATIAL_BWD); +} + +inline bool IsHaveBackward(mfxI32 direction) +{ + return (direction == D_DIR_BWD) || (direction == D_DIR_BIDIR) || + (direction == D_DIR_DIRECT_SPATIAL_BWD) || (direction == D_DIR_DIRECT_SPATIAL_BIDIR) || + (direction == D_DIR_DIRECT); +} + +inline bool IsBidirOnly(mfxI32 direction) +{ + return (direction == D_DIR_BIDIR) || (direction == D_DIR_DIRECT_SPATIAL_BIDIR) || + (direction == D_DIR_DIRECT); +} + +// Warning: If these bit defines change, also need to change same +// defines and related code in sresidual.s. +enum CBP +{ + D_CBP_LUMA_DC = 0x00001, + D_CBP_LUMA_AC = 0x1fffe, + + D_CBP_CHROMA_DC = 0x00001, + D_CBP_CHROMA_AC = 0x1fffe, + D_CBP_CHROMA_AC_420 = 0x0001e, + D_CBP_CHROMA_AC_422 = 0x001fe, + D_CBP_CHROMA_AC_444 = 0x1fffe, + + D_CBP_1ST_LUMA_AC_BITPOS = 1, + D_CBP_1ST_CHROMA_DC_BITPOS = 17, + D_CBP_1ST_CHROMA_AC_BITPOS = 19 +}; + +enum +{ + FIRST_DC_LUMA = 0, + FIRST_AC_LUMA = 1, + FIRST_DC_CHROMA = 17, + FIRST_AC_CHROMA = 19 +}; + +enum +{ + CHROMA_FORMAT_400 = 0, + CHROMA_FORMAT_420 = 1, + CHROMA_FORMAT_422 = 2, + CHROMA_FORMAT_444 = 3 +}; + +class AVC_exception +{ +public: + AVC_exception(mfxI32 status = -1) + : m_Status(status) + { + } + + virtual ~AVC_exception() + { + } + + mfxI32 GetStatus() const + { + return m_Status; + } + +private: + mfxI32 m_Status; +}; + + +#pragma pack(1) + +extern mfxI32 lock_failed; + +#pragma pack() + +template +inline T * AVC_new_array_throw(mfxI32 size) +{ + T * t = new T[size]; + if (!t) + throw AVC_exception(MFX_ERR_MEMORY_ALLOC); + return t; +} + +template +inline T * AVC_new_throw() +{ + T * t = new T(); + if (!t) + throw AVC_exception(MFX_ERR_MEMORY_ALLOC); + return t; +} + +template +inline T * AVC_new_throw_1(T1 t1) +{ + T * t = new T(t1); + if (!t) + throw AVC_exception(MFX_ERR_MEMORY_ALLOC); + return t; +} + +inline mfxU32 CalculateSuggestedSize(const AVCSeqParamSet * sps) +{ + mfxU32 base_size = sps->frame_width_in_mbs * sps->frame_height_in_mbs * 256; + mfxU32 size = 0; + + switch (sps->chroma_format_idc) + { + case 0: // YUV400 + size = base_size; + break; + case 1: // YUV420 + size = (base_size * 3) / 2; + break; + case 2: // YUV422 + size = base_size + base_size; + break; + case 3: // YUV444 + size = base_size + base_size + base_size; + break; + }; + + return size; +} + +#define SAMPLE_ASSERT(x) + +} // namespace ProtectedLibrary + + +#endif // __AVC_STRUCTURES_H__ diff --git a/vshampor/deshuffler/sample_common/include/base_allocator.h b/vshampor/deshuffler/sample_common/include/base_allocator.h new file mode 100644 index 0000000..0d15efc --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/base_allocator.h @@ -0,0 +1,236 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __BASE_ALLOCATOR_H__ +#define __BASE_ALLOCATOR_H__ + +#include +#include +#include +#include "mfxvideo.h" +#include + +class MSDKMutex; + +struct mfxAllocatorParams +{ + virtual ~mfxAllocatorParams(){}; +}; + +// this class implements methods declared in mfxFrameAllocator structure +// simply redirecting them to virtual methods which should be overridden in derived classes +class MFXFrameAllocator : public mfxFrameAllocator +{ +public: + MFXFrameAllocator(); + virtual ~MFXFrameAllocator(); + + // optional method, override if need to pass some parameters to allocator from application + virtual mfxStatus Init(mfxAllocatorParams *pParams) = 0; + virtual mfxStatus Close() = 0; + + virtual mfxStatus AllocFrames(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) = 0; + virtual mfxStatus LockFrame(mfxMemId mid, mfxFrameData *ptr) = 0; + virtual mfxStatus UnlockFrame(mfxMemId mid, mfxFrameData *ptr) = 0; + virtual mfxStatus GetFrameHDL(mfxMemId mid, mfxHDL *handle) = 0; + virtual mfxStatus FreeFrames(mfxFrameAllocResponse *response) = 0; + +private: + static mfxStatus MFX_CDECL Alloc_(mfxHDL pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response); + static mfxStatus MFX_CDECL Lock_(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); + static mfxStatus MFX_CDECL Unlock_(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); + static mfxStatus MFX_CDECL GetHDL_(mfxHDL pthis, mfxMemId mid, mfxHDL *handle); + static mfxStatus MFX_CDECL Free_(mfxHDL pthis, mfxFrameAllocResponse *response); +}; + +// This class implements basic logic of memory allocator +// Manages responses for different components according to allocation request type +// External frames of a particular component-related type are allocated in one call +// Further calls return previously allocated response. +// Ex. Preallocated frame chain with type=FROM_ENCODE | FROM_VPPIN will be returned when +// request type contains either FROM_ENCODE or FROM_VPPIN + +// This class does not allocate any actual memory +class MSDKMutex; + +class BaseFrameAllocator: public MFXFrameAllocator +{ +public: + BaseFrameAllocator(); + virtual ~BaseFrameAllocator(); + + virtual mfxStatus Init(mfxAllocatorParams *pParams) = 0; + virtual mfxStatus Close(); + virtual mfxStatus AllocFrames(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response); + virtual mfxStatus FreeFrames(mfxFrameAllocResponse *response); + +protected: + std::unique_ptr mtx; + typedef std::list::iterator Iter; + static const mfxU32 MEMTYPE_FROM_MASK = MFX_MEMTYPE_FROM_ENCODE | MFX_MEMTYPE_FROM_DECODE | \ + MFX_MEMTYPE_FROM_VPPIN | MFX_MEMTYPE_FROM_VPPOUT | \ + MFX_MEMTYPE_FROM_ENC | MFX_MEMTYPE_FROM_PAK; + + static const mfxU32 MEMTYPE_FROM_MASK_INT_EXT = MEMTYPE_FROM_MASK | MFX_MEMTYPE_INTERNAL_FRAME | MFX_MEMTYPE_EXTERNAL_FRAME; + + struct UniqueResponse + : mfxFrameAllocResponse + { + mfxU16 m_width; + mfxU16 m_height; + mfxU32 m_refCount; + mfxU16 m_type; + + UniqueResponse() + : m_width(0) + , m_height(0) + , m_refCount(0) + , m_type(0) + { + memset(static_cast(this), 0, sizeof(mfxFrameAllocResponse)); + } + + // compare responses by actual frame size, alignment (w and h) is up to application + UniqueResponse(const mfxFrameAllocResponse & response, mfxU16 width, mfxU16 height, mfxU16 type) + : mfxFrameAllocResponse(response) + , m_width(width) + , m_height(height) + , m_refCount(1) + , m_type(type) + { + } + + //compare by resolution (and memory type for FEI ENC / PAK) + bool operator () (const UniqueResponse &response)const + { + if (m_width <= response.m_width && m_height <= response.m_height) + { + // For FEI ENC and PAK we need to distinguish between INTERNAL and EXTERNAL frames + + if (m_type & response.m_type & (MFX_MEMTYPE_FROM_ENC | MFX_MEMTYPE_FROM_PAK)) + { + return !!((m_type & response.m_type) & 0x000f); + } + else + { + return !!(m_type & response.m_type & MFX_MEMTYPE_FROM_DECODE); + } + } + else + { + return false; + } + } + + static mfxU16 CropMemoryTypeToStore(mfxU16 type) + { + // Remain INTERNAL / EXTERNAL flag for FEI ENC / PAK + switch (type & 0xf000) + { + case MFX_MEMTYPE_FROM_ENC: + case MFX_MEMTYPE_FROM_PAK: + case (MFX_MEMTYPE_FROM_ENC | MFX_MEMTYPE_FROM_PAK): + return type & MEMTYPE_FROM_MASK_INT_EXT; + break; + default: + return type & MEMTYPE_FROM_MASK; + break; + } + } + }; + + std::list m_responses; + std::list m_ExtResponses; + + struct IsSame + : public std::binary_function + { + bool operator () (const mfxFrameAllocResponse & l, const mfxFrameAllocResponse &r)const + { + return r.mids != 0 && l.mids != 0 && + r.mids[0] == l.mids[0] && + r.NumFrameActual == l.NumFrameActual; + } + }; + + // checks if request is supported + virtual mfxStatus CheckRequestType(mfxFrameAllocRequest *request); + + // frees memory attached to response + virtual mfxStatus ReleaseResponse(mfxFrameAllocResponse *response) = 0; + // allocates memory + virtual mfxStatus AllocImpl(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) = 0; + + template + class safe_array + { + public: + safe_array(T *ptr = 0):m_ptr(ptr) + { // construct from object pointer + }; + ~safe_array() + { + reset(0); + } + T* get() + { // return wrapped pointer + return m_ptr; + } + T* release() + { // return wrapped pointer and give up ownership + T* ptr = m_ptr; + m_ptr = 0; + return ptr; + } + void reset(T* ptr) + { // destroy designated object and store new pointer + if (m_ptr) + { + delete[] m_ptr; + } + m_ptr = ptr; + } + protected: + T* m_ptr; // the wrapped object pointer + private: + safe_array(const safe_array& ); + safe_array& operator=(const safe_array& ); + }; +}; + +class MFXBufferAllocator : public mfxBufferAllocator +{ +public: + MFXBufferAllocator(); + virtual ~MFXBufferAllocator(); + + virtual mfxStatus AllocBuffer(mfxU32 nbytes, mfxU16 type, mfxMemId *mid) = 0; + virtual mfxStatus LockBuffer(mfxMemId mid, mfxU8 **ptr) = 0; + virtual mfxStatus UnlockBuffer(mfxMemId mid) = 0; + virtual mfxStatus FreeBuffer(mfxMemId mid) = 0; + +private: + static mfxStatus MFX_CDECL Alloc_(mfxHDL pthis, mfxU32 nbytes, mfxU16 type, mfxMemId *mid); + static mfxStatus MFX_CDECL Lock_(mfxHDL pthis, mfxMemId mid, mfxU8 **ptr); + static mfxStatus MFX_CDECL Unlock_(mfxHDL pthis, mfxMemId mid); + static mfxStatus MFX_CDECL Free_(mfxHDL pthis, mfxMemId mid); +}; + + +#endif // __BASE_ALLOCATOR_H__ diff --git a/vshampor/deshuffler/sample_common/include/brc_routines.h b/vshampor/deshuffler/sample_common/include/brc_routines.h new file mode 100644 index 0000000..42955c4 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/brc_routines.h @@ -0,0 +1,402 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ +#include "sample_defs.h" + +#if (MFX_VERSION >= 1024) +#include "mfxbrc.h" + +#ifndef __PIPELINE_ENCODE_BRC_H__ +#define __PIPELINE_ENCODE_BRC_H__ + +#define MFX_CHECK_NULL_PTR1(pointer) MSDK_CHECK_POINTER(pointer, MFX_ERR_NULL_PTR); + +#define MFX_CHECK_NULL_PTR2(pointer1,pointer2 )\ + MFX_CHECK_NULL_PTR1(pointer1);\ + MFX_CHECK_NULL_PTR1(pointer2); + +#define MFX_CHECK_NULL_PTR3(pointer1,pointer2, pointer3 )\ + MFX_CHECK_NULL_PTR2(pointer1,pointer2);\ + MFX_CHECK_NULL_PTR1(pointer3); + +#define MFX_CHECK(cond, error) MSDK_CHECK_NOT_EQUAL(cond, true, error) +#define MFX_CHECK_STS(sts) MFX_CHECK(sts == MFX_ERR_NONE, sts) + + +class cBRCParams +{ +public: + mfxU16 rateControlMethod; // CBR or VBR + + mfxU16 bHRDConformance; // is HRD compliance needed + mfxU16 bRec; // is Recoding possible + mfxU16 bPanic; // is Panic mode possible + + // HRD params + mfxU32 bufferSizeInBytes; + mfxU32 initialDelayInBytes; + + // Sliding window parameters + mfxU16 WinBRCMaxAvgKbps; + mfxU16 WinBRCSize; + + // RC params + mfxU32 targetbps; + mfxU32 maxbps; + mfxF64 frameRate; + mfxF64 inputBitsPerFrame; + mfxF64 maxInputBitsPerFrame; + mfxU32 maxFrameSizeInBits; + + // Frame size params + mfxU16 width; + mfxU16 height; + mfxU16 chromaFormat; + mfxU16 bitDepthLuma; + + // GOP params + mfxU16 gopPicSize; + mfxU16 gopRefDist; + bool bPyr; + + //BRC accurancy params + mfxF64 fAbPeriodLong; // number on frames to calculate abberation from target frame + mfxF64 fAbPeriodShort; // number on frames to calculate abberation from target frame + mfxF64 dqAbPeriod; // number on frames to calculate abberation from dequant + mfxF64 bAbPeriod; // number of frames to calculate abberation from target bitrate + + //QP parameters + mfxI32 quantOffset; + mfxI32 quantMaxI; + mfxI32 quantMinI; + mfxI32 quantMaxP; + mfxI32 quantMinP; + mfxI32 quantMaxB; + mfxI32 quantMinB; + +public: + cBRCParams(): + rateControlMethod(0), + bHRDConformance(0), + bRec(0), + bPanic(0), + bufferSizeInBytes(0), + initialDelayInBytes(0), + WinBRCMaxAvgKbps(0), + WinBRCSize(0), + targetbps(0), + maxbps(0), + frameRate(0), + inputBitsPerFrame(0), + maxInputBitsPerFrame(0), + maxFrameSizeInBits(0), + width(0), + height(0), + chromaFormat(0), + bitDepthLuma(0), + gopPicSize(0), + gopRefDist(0), + bPyr(0), + fAbPeriodLong(0), + fAbPeriodShort(0), + dqAbPeriod(0), + bAbPeriod(0), + quantOffset(0), + quantMaxI(0), + quantMinI(0), + quantMaxP(0), + quantMinP(0), + quantMaxB(0), + quantMinB(0) + {} + + mfxStatus Init(mfxVideoParam* par, bool bFieldMode = false); + mfxStatus GetBRCResetType(mfxVideoParam* par, bool bNewSequence, bool &bReset, bool &bSlidingWindowReset ); +}; +class cHRD +{ +public: + cHRD(): + m_bufFullness(0), + m_prevBufFullness(0), + m_frameNum(0), + m_minFrameSize(0), + m_maxFrameSize(0), + m_underflowQuant(0), + m_overflowQuant(0), + m_buffSizeInBits(0), + m_delayInBits(0), + m_inputBitsPerFrame(0), + m_bCBR (false) + {} + + void Init(mfxU32 buffSizeInBytes, mfxU32 delayInBytes, mfxF64 inputBitsPerFrame, bool cbr); + mfxU16 UpdateAndCheckHRD(mfxI32 frameBits, mfxI32 recode, mfxI32 minQuant, mfxI32 maxQuant); + mfxStatus UpdateMinMaxQPForRec(mfxU32 brcSts, mfxI32 qp); + mfxI32 GetTargetSize(mfxU32 brcSts); + mfxI32 GetMaxFrameSize() { return m_maxFrameSize;} + mfxI32 GetMinFrameSize() { return m_minFrameSize;} + mfxI32 GetMaxQuant() { return m_overflowQuant - 1;} + mfxI32 GetMinQuant() { return m_underflowQuant + 1;} + mfxF64 GetBufferDiviation(mfxU32 targetBitrate); + + + +private: + mfxF64 m_bufFullness; + mfxF64 m_prevBufFullness; + mfxI32 m_frameNum; + mfxI32 m_minFrameSize; + mfxI32 m_maxFrameSize; + mfxI32 m_underflowQuant; + mfxI32 m_overflowQuant; + +private: + mfxI32 m_buffSizeInBits; + mfxI32 m_delayInBits; + mfxF64 m_inputBitsPerFrame; + bool m_bCBR; + +}; + +#define IPP_MAX( a, b ) ( ((a) > (b)) ? (a) : (b) ) +#define IPP_MIN( a, b ) ( ((a) < (b)) ? (a) : (b) ) + +struct BRC_Ctx +{ + mfxI32 QuantI; //currect qp for intra frames + mfxI32 QuantP; //currect qp for P frames + mfxI32 QuantB; //currect qp for B frames + + mfxI32 Quant; // qp for last encoded frame + mfxI32 QuantMin; // qp Min for last encoded frame (is used for recoding) + mfxI32 QuantMax; // qp Max for last encoded frame (is used for recoding) + + bool bToRecode; // last frame is needed in recoding + bool bPanic; // last frame is needed in panic + mfxU32 encOrder; // encoding order of last encoded frame + mfxU32 poc; // poc of last encoded frame + mfxI32 SceneChange; // scene change parameter of last encoded frame + mfxU32 SChPoc; // poc of frame with scene change + mfxU32 LastIEncOrder; // encoded order if last intra frame + mfxU32 LastNonBFrameSize; // encoded frame size of last non B frame (is used for sceneChange) + + mfxF64 fAbLong; // avarage frame size (long period) + mfxF64 fAbShort; // avarage frame size (short period) + mfxF64 dQuantAb; // avarage dequant + mfxF64 totalDiviation; // divation from target bitrate (total) + + mfxF64 eRate; // eRate of last encoded frame, this parameter is used for scene change calculation + mfxF64 eRateSH; // eRate of last encoded scene change frame, this parameter is used for scene change calculation +}; +class AVGBitrate +{ +public: + AVGBitrate(mfxU32 windowSize, mfxU32 maxBitPerFrame, mfxU32 avgBitPerFrame) : + m_maxWinBits(maxBitPerFrame*windowSize), + m_maxWinBitsLim(0), + m_avgBitPerFrame(IPP_MIN(avgBitPerFrame, maxBitPerFrame)), + m_currPosInWindow(0), + m_lastFrameOrder(0) + + { + windowSize = windowSize > 0 ? windowSize : 1; // kw + m_slidingWindow.resize(windowSize); + for (mfxU32 i = 0; i < windowSize; i++) + { + m_slidingWindow[i] = maxBitPerFrame / 3; //initial value to prevent big first frames + } + m_maxWinBitsLim = GetMaxWinBitsLim(); + } + virtual ~AVGBitrate() + { + + } + void UpdateSlidingWindow(mfxU32 sizeInBits, mfxU32 FrameOrder, bool bPanic, bool bSH, mfxU32 recode) + { + mfxU32 windowSize = (mfxU32)m_slidingWindow.size(); + bool bNextFrame = FrameOrder != m_lastFrameOrder; + + if (bNextFrame) + { + m_lastFrameOrder = FrameOrder; + m_currPosInWindow = (m_currPosInWindow + 1) % windowSize; + } + m_slidingWindow[m_currPosInWindow] = sizeInBits; + + if (bNextFrame) + { + if (bPanic || bSH) + { + m_maxWinBitsLim = IPP_MAX(IPP_MIN( (GetLastFrameBits(windowSize) + m_maxWinBits)/2, m_maxWinBits), GetMaxWinBitsLim()); + } + else + { + if (recode) + m_maxWinBitsLim = IPP_MIN(IPP_MAX(GetLastFrameBits(windowSize) + GetStep()/2, m_maxWinBitsLim), m_maxWinBits); + else if ((m_maxWinBitsLim > GetMaxWinBitsLim() + GetStep()) && + (m_maxWinBitsLim - GetStep() > (GetLastFrameBits(windowSize - 1) + sizeInBits))) + m_maxWinBitsLim -= GetStep(); + } + + } + + } + mfxU32 GetMaxFrameSize(bool bPanic, bool bSH, mfxU32 recode) + { + mfxU32 winBits = GetLastFrameBits(GetWindowSize() - 1); + mfxU32 maxWinBitsLim = m_maxWinBitsLim; + if (bSH) + maxWinBitsLim = (m_maxWinBits + m_maxWinBitsLim)/2; + if (bPanic) + maxWinBitsLim = m_maxWinBits; + maxWinBitsLim = IPP_MIN(maxWinBitsLim + recode*GetStep()/2, m_maxWinBits); + + mfxU32 maxFrameSize = winBits >= m_maxWinBitsLim ? + m_maxWinBits - winBits: + maxWinBitsLim - winBits; + + + + return maxFrameSize; + } + mfxU32 GetWindowSize() + { + return (mfxU32)m_slidingWindow.size(); + } + +protected: + + mfxU32 m_maxWinBits; + mfxU32 m_maxWinBitsLim; + mfxU32 m_avgBitPerFrame; + + mfxU32 m_currPosInWindow; + mfxU32 m_lastFrameOrder; + std::vector m_slidingWindow; + + + + mfxU32 GetLastFrameBits(mfxU32 numFrames) + { + mfxU32 size = 0; + numFrames = numFrames < m_slidingWindow.size() ? numFrames : (mfxU32)m_slidingWindow.size(); + for (mfxU32 i = 0; i < numFrames; i++) + { + size += m_slidingWindow[(m_currPosInWindow + m_slidingWindow.size() - i) % m_slidingWindow.size()]; + //printf("GetLastFrames: %d) %d sum %d\n",i,m_slidingWindow[(m_currPosInWindow + m_slidingWindow.size() - i) % m_slidingWindow.size() ], size); + } + return size; + } + mfxU32 GetStep() + { + return (m_maxWinBits / GetWindowSize() - m_avgBitPerFrame) / 2; + } + + mfxU32 GetMaxWinBitsLim() + { + return m_maxWinBits - GetStep() * GetWindowSize(); + } + + +}; +#undef IPP_MAX +#undef IPP_MIN +class ExtBRC +{ +private: + cBRCParams m_par; + cHRD m_hrd; + bool m_bInit; + BRC_Ctx m_ctx; + std::unique_ptr m_avg; + +public: + ExtBRC(): + m_par(), + m_hrd(), + m_bInit(false) + { + memset(&m_ctx, 0, sizeof(m_ctx)); + + } + mfxStatus Init (mfxVideoParam* par); + mfxStatus Reset(mfxVideoParam* par); + mfxStatus Close () {m_bInit = false; return MFX_ERR_NONE;} + mfxStatus GetFrameCtrl (mfxBRCFrameParam* par, mfxBRCFrameCtrl* ctrl); + mfxStatus Update (mfxBRCFrameParam* par, mfxBRCFrameCtrl* ctrl, mfxBRCFrameStatus* status); +protected: + mfxI32 GetCurQP (mfxU32 type, mfxI32 layer); +}; + +namespace HEVCExtBRC +{ + inline mfxStatus Init (mfxHDL pthis, mfxVideoParam* par) + { + MFX_CHECK_NULL_PTR1(pthis); + return ((ExtBRC*)pthis)->Init(par) ; + } + inline mfxStatus Reset (mfxHDL pthis, mfxVideoParam* par) + { + MFX_CHECK_NULL_PTR1(pthis); + return ((ExtBRC*)pthis)->Reset(par) ; + } + inline mfxStatus Close (mfxHDL pthis) + { + MFX_CHECK_NULL_PTR1(pthis); + return ((ExtBRC*)pthis)->Close() ; + } + inline mfxStatus GetFrameCtrl (mfxHDL pthis, mfxBRCFrameParam* par, mfxBRCFrameCtrl* ctrl) + { + MFX_CHECK_NULL_PTR1(pthis); + return ((ExtBRC*)pthis)->GetFrameCtrl(par,ctrl) ; + } + inline mfxStatus Update (mfxHDL pthis, mfxBRCFrameParam* par, mfxBRCFrameCtrl* ctrl, mfxBRCFrameStatus* status) + { + MFX_CHECK_NULL_PTR1(pthis); + return ((ExtBRC*)pthis)->Update(par,ctrl, status) ; + } + inline mfxStatus Create(mfxExtBRC & m_BRC) + { + MFX_CHECK(m_BRC.pthis == NULL, MFX_ERR_UNDEFINED_BEHAVIOR); + m_BRC.pthis = new ExtBRC; + m_BRC.Init = Init; + m_BRC.Reset = Reset; + m_BRC.Close = Close; + m_BRC.GetFrameCtrl = GetFrameCtrl; + m_BRC.Update = Update; + return MFX_ERR_NONE; + } + inline mfxStatus Destroy(mfxExtBRC & m_BRC) + { + if(m_BRC.pthis != NULL) + { + delete (ExtBRC*)m_BRC.pthis; + m_BRC.pthis = 0; + m_BRC.Init = 0; + m_BRC.Reset = 0; + m_BRC.Close = 0; + m_BRC.GetFrameCtrl = 0; + m_BRC.Update = 0; + } + return MFX_ERR_NONE; + } +} +#endif + +#endif \ No newline at end of file diff --git a/vshampor/deshuffler/sample_common/include/current_date.h b/vshampor/deshuffler/sample_common/include/current_date.h new file mode 100644 index 0000000..4af21e5 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/current_date.h @@ -0,0 +1,26 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#define PRODUCT_NAME "Intel\xae Media SDK" +#define FILE_VERSION 1,0,0,0 +#define FILE_VERSION_STRING "1,0,0,0" +#define FILTER_NAME_PREFIX "" +#define FILTER_NAME_SUFFIX "" +#define PRODUCT_VERSION 1,0,0,0 +#define PRODUCT_VERSION_STRING "1,0,0,0" diff --git a/vshampor/deshuffler/sample_common/include/d3d11_allocator.h b/vshampor/deshuffler/sample_common/include/d3d11_allocator.h new file mode 100644 index 0000000..6975d81 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/d3d11_allocator.h @@ -0,0 +1,115 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __D3D11_ALLOCATOR_H__ +#define __D3D11_ALLOCATOR_H__ + +#include "base_allocator.h" +#include + +#ifdef __gnu_linux__ +#include // for uintptr_t on Linux +#endif + +//application can provide either generic mid from surface or this wrapper +//wrapper distinguishes from generic mid by highest 1 bit +//if it set then remained pointer points to extended structure of memid +//64 bits system layout +/*----+-----------------------------------------------------------+ +|b63=1|63 bits remained for pointer to extended structure of memid| +|b63=0|63 bits from original mfxMemId | ++-----+----------------------------------------------------------*/ +//32 bits system layout +/*--+---+--------------------------------------------+ +|b31=1|31 bits remained for pointer to extended memid| +|b31=0|31 bits remained for surface pointer | ++---+---+-------------------------------------------*/ +//#pragma warning (disable:4293) +class MFXReadWriteMid +{ + static const uintptr_t bits_offset = std::numeric_limits::digits - 1; + static const uintptr_t clear_mask = ~((uintptr_t)1 << bits_offset); +public: + enum + { + //if flag not set it means that read and write + not_set = 0, + reuse = 1, + read = 2, + write = 4, + }; + //here mfxmemid might be as MFXReadWriteMid or mfxMemId memid + MFXReadWriteMid(mfxMemId mid, mfxU8 flag = not_set) + { + //setup mid + m_mid_to_report = (mfxMemId)((uintptr_t)&m_mid | ((uintptr_t)1 << bits_offset)); + if (0 != ((uintptr_t)mid >> bits_offset)) + { + //it points to extended structure + mfxMedIdEx * pMemIdExt = reinterpret_cast((uintptr_t)mid & clear_mask); + m_mid.pId = pMemIdExt->pId; + if (reuse == flag) + { + m_mid.read_write = pMemIdExt->read_write; + } + else + { + m_mid.read_write = flag; + } + } + else + { + m_mid.pId = mid; + if (reuse == flag) + m_mid.read_write = not_set; + else + m_mid.read_write = flag; + } + + } + bool isRead() const + { + return 0 != (m_mid.read_write & read) || !m_mid.read_write; + } + bool isWrite() const + { + return 0 != (m_mid.read_write & write) || !m_mid.read_write; + } + /// returns original memid without read write flags + mfxMemId raw() const + { + return m_mid.pId; + } + operator mfxMemId() const + { + return m_mid_to_report; + } + +private: + struct mfxMedIdEx + { + mfxMemId pId; + mfxU8 read_write; + }; + + mfxMedIdEx m_mid; + mfxMemId m_mid_to_report; +}; + +#endif // __D3D11_ALLOCATOR_H__ diff --git a/vshampor/deshuffler/sample_common/include/d3d11_device.h b/vshampor/deshuffler/sample_common/include/d3d11_device.h new file mode 100644 index 0000000..81be68c --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/d3d11_device.h @@ -0,0 +1,21 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once + diff --git a/vshampor/deshuffler/sample_common/include/d3d_allocator.h b/vshampor/deshuffler/sample_common/include/d3d_allocator.h new file mode 100644 index 0000000..c4421a5 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/d3d_allocator.h @@ -0,0 +1,23 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __D3D_ALLOCATOR_H__ +#define __D3D_ALLOCATOR_H__ + +#endif // __D3D_ALLOCATOR_H__ diff --git a/vshampor/deshuffler/sample_common/include/d3d_device.h b/vshampor/deshuffler/sample_common/include/d3d_device.h new file mode 100644 index 0000000..81be68c --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/d3d_device.h @@ -0,0 +1,21 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once + diff --git a/vshampor/deshuffler/sample_common/include/decode_render.h b/vshampor/deshuffler/sample_common/include/decode_render.h new file mode 100644 index 0000000..fad66a2 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/decode_render.h @@ -0,0 +1,34 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + + +#ifndef __DECODE_D3D_RENDER_H__ +#define __DECODE_D3D_RENDER_H__ + + +#include "mfxstructures.h" +#include "mfxvideo.h" + +#include "hw_device.h" + +typedef void* WindowHandle; +typedef void* Handle; + + +#endif // __DECODE_D3D_RENDER_H__ \ No newline at end of file diff --git a/vshampor/deshuffler/sample_common/include/general_allocator.h b/vshampor/deshuffler/sample_common/include/general_allocator.h new file mode 100644 index 0000000..d2a5314 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/general_allocator.h @@ -0,0 +1,61 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __GENERAL_ALLOCATOR_H__ +#define __GENERAL_ALLOCATOR_H__ + +#include "sample_utils.h" +#include "base_allocator.h" + +#include +#include + +class SysMemFrameAllocator; + +// Wrapper on standard allocator for concurrent allocation of +// D3D and system surfaces +class GeneralAllocator : public BaseFrameAllocator +{ +public: + GeneralAllocator(); + virtual ~GeneralAllocator(); + + virtual mfxStatus Init(mfxAllocatorParams *pParams); + virtual mfxStatus Close(); + +protected: + virtual mfxStatus LockFrame(mfxMemId mid, mfxFrameData *ptr); + virtual mfxStatus UnlockFrame(mfxMemId mid, mfxFrameData *ptr); + virtual mfxStatus GetFrameHDL(mfxMemId mid, mfxHDL *handle); + + virtual mfxStatus ReleaseResponse(mfxFrameAllocResponse *response); + virtual mfxStatus AllocImpl(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response); + + void StoreFrameMids(bool isD3DFrames, mfxFrameAllocResponse *response); + bool isD3DMid(mfxHDL mid); + + std::map m_Mids; + std::unique_ptr m_D3DAllocator; + std::unique_ptr m_SYSAllocator; +private: + DISALLOW_COPY_AND_ASSIGN(GeneralAllocator); + +}; + +#endif //__GENERAL_ALLOCATOR_H__ diff --git a/vshampor/deshuffler/sample_common/include/hw_device.h b/vshampor/deshuffler/sample_common/include/hw_device.h new file mode 100644 index 0000000..a2094f3 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/hw_device.h @@ -0,0 +1,50 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once + +#include "mfxvideo++.h" + +/// Base class for hw device +class CHWDevice +{ +public: + virtual ~CHWDevice(){} + /** Initializes device for requested processing. + @param[in] hWindow Window handle to bundle device to. + @param[in] nViews Number of views to process. + @param[in] nAdapterNum Number of adapter to use + */ + virtual mfxStatus Init( + mfxHDL hWindow, + mfxU16 nViews, + mfxU32 nAdapterNum) = 0; + /// Reset device. + virtual mfxStatus Reset() = 0; + /// Get handle can be used for MFX session SetHandle calls + virtual mfxStatus GetHandle(mfxHandleType type, mfxHDL *pHdl) = 0; + /** Set handle. + Particular device implementation may require other objects to operate. + */ + virtual mfxStatus SetHandle(mfxHandleType type, mfxHDL hdl) = 0; + virtual mfxStatus RenderFrame(mfxFrameSurface1 * pSurface, mfxFrameAllocator * pmfxAlloc) = 0; + virtual void UpdateTitle(double fps) = 0; + virtual void SetMondelloInput(bool isMondelloInputEnabled) = 0; + virtual void Close() = 0; +}; diff --git a/vshampor/deshuffler/sample_common/include/intrusive_ptr.h b/vshampor/deshuffler/sample_common/include/intrusive_ptr.h new file mode 100644 index 0000000..b581827 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/intrusive_ptr.h @@ -0,0 +1,62 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once + +//intrusive ptr concept +//usage examples same as smart pointers except user has to define addref and release routine for that class + +//inline void intrusive_ptr_addref(UserClassA * pResource); +//inline void intrusive_ptr_release(UserClassA * pResource); + +template +class intrusive_ptr +{ + T * m_pResource; +public: + intrusive_ptr(T* pResource = NULL) + : m_pResource(pResource) { + intrusive_ptr_addref(m_pResource); + } + intrusive_ptr(const intrusive_ptr & rhs) + : m_pResource(rhs.m_pResource) { + intrusive_ptr_addref(m_pResource); + } + void reset(T* pResource) { + if (m_pResource){ + intrusive_ptr_release(m_pResource); + } + m_pResource = pResource; + intrusive_ptr_addref(m_pResource); + } + T* operator *() { + return m_pResource; + } + T* operator ->() { + return m_pResource; + } + T* get(){ + return m_pResource; + } + ~intrusive_ptr(){ + if (m_pResource) { + intrusive_ptr_release(m_pResource); + } + } +}; diff --git a/vshampor/deshuffler/sample_common/include/mfx_buffering.h b/vshampor/deshuffler/sample_common/include/mfx_buffering.h new file mode 100644 index 0000000..d2bd6ec --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/mfx_buffering.h @@ -0,0 +1,398 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __MFX_BUFFERING_H__ +#define __MFX_BUFFERING_H__ + +#include + +#include "mfxstructures.h" + +#include "vm/strings_defs.h" +#include "vm/thread_defs.h" +#include "vm/time_defs.h" +#include "vm/atomic_defs.h" + +struct msdkFrameSurface +{ + mfxFrameSurface1 frame; // NOTE: this _should_ be the first item (see CBuffering::FindUsedSurface()) + msdk_tick submit; // tick when frame was submitted for processing + mfxU16 render_lock; // signifies that frame is locked for rendering + msdkFrameSurface* prev; + msdkFrameSurface* next; +}; + +struct msdkOutputSurface +{ + msdkFrameSurface* surface; + mfxSyncPoint syncp; + msdkOutputSurface* next; +}; + +/** \brief Debug purpose macro to terminate execution if buggy situation happenned. + * + * Use this macro to check impossible, buggy condition which should not occur under + * normal circumstances. Macro should be used where check in release mode is not + * desirable and atually needed. + */ + #define MSDK_SELF_CHECK(C) + +class CBuffering; + +// LIFO list of frame surfaces +class msdkFreeSurfacesPool +{ + friend class CBuffering; +public: + msdkFreeSurfacesPool(MSDKMutex* mutex): + m_pSurfaces(NULL), + m_pMutex(mutex) {} + + ~msdkFreeSurfacesPool() { + m_pSurfaces = NULL; + } + /** \brief The function adds free surface to the free surfaces array. + * + * @note That's caller responsibility to pass valid surface. + * @note We always add and get free surface from the array head. In case not all surfaces + * will be actually used we have good chance to avoid actual allocation of the surface memory. + */ + inline void AddSurface(msdkFrameSurface* surface) { + AutomaticMutex lock(*m_pMutex); + AddSurfaceUnsafe(surface); + } + /** \brief The function gets the next free surface from the free surfaces array. + * + * @note Surface is detached from the free surfaces array. + */ + inline msdkFrameSurface* GetSurface() { + AutomaticMutex lock(*m_pMutex); + return GetSurfaceUnsafe(); + } + +private: + inline void AddSurfaceUnsafe(msdkFrameSurface* surface) { + msdkFrameSurface* head; + + MSDK_SELF_CHECK(surface); + MSDK_SELF_CHECK(!surface->prev); + MSDK_SELF_CHECK(!surface->next); + + head = m_pSurfaces; + m_pSurfaces = surface; + m_pSurfaces->next = head; + } + inline msdkFrameSurface* GetSurfaceUnsafe() { + + msdkFrameSurface* surface = NULL; + + if (m_pSurfaces) { + surface = m_pSurfaces; + m_pSurfaces = m_pSurfaces->next; + surface->prev = surface->next = NULL; + MSDK_SELF_CHECK(!surface->prev); + MSDK_SELF_CHECK(!surface->next); + } + return surface; + } + +protected: + msdkFrameSurface* m_pSurfaces; + MSDKMutex* m_pMutex; + +private: + msdkFreeSurfacesPool(const msdkFreeSurfacesPool&); + void operator=(const msdkFreeSurfacesPool&); +}; + +// random access, predicted as FIFO +class msdkUsedSurfacesPool +{ + friend class CBuffering; +public: + msdkUsedSurfacesPool(MSDKMutex* mutex): + m_pSurfacesHead(NULL), + m_pSurfacesTail(NULL), + m_pMutex(mutex) {} + + ~msdkUsedSurfacesPool() { + m_pSurfacesHead = NULL; + m_pSurfacesTail = NULL; + } + + /** \brief The function adds surface to the used surfaces array (m_pUsedSurfaces). + * + * @note That's caller responsibility to pass valid surface. + * @note We can't actually know which surface will be returned by the decoder or unlocked. However, + * we can make prediction that it will be the oldest surface. Thus, here the function adds new + * surface (youngest) to the tail of the least. Check operations for the list will run starting from + * head. + */ + inline void AddSurface(msdkFrameSurface* surface) { + AutomaticMutex lock(*m_pMutex); + AddSurfaceUnsafe(surface); + } + + /** \brief The function detaches surface from the used surfaces array. + * + * @note That's caller responsibility to pass valid surface. + */ + + inline void DetachSurface(msdkFrameSurface* surface) { + AutomaticMutex lock(*m_pMutex); + DetachSurfaceUnsafe(surface); + } + +private: + inline void DetachSurfaceUnsafe(msdkFrameSurface* surface) + { + MSDK_SELF_CHECK(surface); + + msdkFrameSurface *prev = surface->prev; + msdkFrameSurface *next = surface->next; + + if (prev) { + prev->next = next; + } + else { + MSDK_SELF_CHECK(surface == m_pSurfacesHead); + m_pSurfacesHead = next; + } + if (next) { + next->prev = prev; + } else { + MSDK_SELF_CHECK(surface == m_pSurfacesTail); + m_pSurfacesTail = prev; + } + + surface->prev = surface->next = NULL; + MSDK_SELF_CHECK(!surface->prev); + MSDK_SELF_CHECK(!surface->next); + } + inline void AddSurfaceUnsafe(msdkFrameSurface* surface) + { + MSDK_SELF_CHECK(surface); + MSDK_SELF_CHECK(!surface->prev); + MSDK_SELF_CHECK(!surface->next); + + surface->prev = m_pSurfacesTail; + surface->next = NULL; + if (m_pSurfacesTail) { + m_pSurfacesTail->next = surface; + m_pSurfacesTail = m_pSurfacesTail->next; + } else { + m_pSurfacesHead = m_pSurfacesTail = surface; + } + } + +protected: + msdkFrameSurface* m_pSurfacesHead; // oldest surface + msdkFrameSurface* m_pSurfacesTail; // youngest surface + MSDKMutex* m_pMutex; + +private: + msdkUsedSurfacesPool(const msdkUsedSurfacesPool&); + void operator=(const msdkUsedSurfacesPool&); +}; + +// FIFO list of surfaces +class msdkOutputSurfacesPool +{ + friend class CBuffering; +public: + msdkOutputSurfacesPool(MSDKMutex* mutex): + m_pSurfacesHead(NULL), + m_pSurfacesTail(NULL), + m_SurfacesCount(0), + m_pMutex(mutex) {} + + ~msdkOutputSurfacesPool() { + m_pSurfacesHead = NULL; + m_pSurfacesTail = NULL; + } + + inline void AddSurface(msdkOutputSurface* surface) { + AutomaticMutex lock(*m_pMutex); + AddSurfaceUnsafe(surface); + } + inline msdkOutputSurface* GetSurface() { + AutomaticMutex lock(*m_pMutex); + return GetSurfaceUnsafe(); + } + + inline mfxU32 GetSurfaceCount() { + return m_SurfacesCount; + } +private: + inline void AddSurfaceUnsafe(msdkOutputSurface* surface) + { + MSDK_SELF_CHECK(surface); + MSDK_SELF_CHECK(!surface->next); + surface->next = NULL; + + if (m_pSurfacesTail) { + m_pSurfacesTail->next = surface; + m_pSurfacesTail = m_pSurfacesTail->next; + } else { + m_pSurfacesHead = m_pSurfacesTail = surface; + } + ++m_SurfacesCount; + } + inline msdkOutputSurface* GetSurfaceUnsafe() + { + msdkOutputSurface* surface = NULL; + + if (m_pSurfacesHead) { + surface = m_pSurfacesHead; + m_pSurfacesHead = m_pSurfacesHead->next; + if (!m_pSurfacesHead) { + // there was only one surface in the array... + m_pSurfacesTail = NULL; + } + --m_SurfacesCount; + surface->next = NULL; + MSDK_SELF_CHECK(!surface->next); + } + return surface; + } + +protected: + msdkOutputSurface* m_pSurfacesHead; // oldest surface + msdkOutputSurface* m_pSurfacesTail; // youngest surface + mfxU32 m_SurfacesCount; + MSDKMutex* m_pMutex; + +private: + msdkOutputSurfacesPool(const msdkOutputSurfacesPool&); + void operator=(const msdkOutputSurfacesPool&); +}; + +/** \brief Helper class defining optimal buffering operations for the Media SDK decoder. + */ +class CBuffering +{ +public: + CBuffering(); + virtual ~CBuffering(); + +protected: // functions + mfxStatus AllocBuffers(mfxU32 SurfaceNumber); + mfxStatus AllocVppBuffers(mfxU32 VppSurfaceNumber); + void AllocOutputBuffer(); + void FreeBuffers(); + void ResetBuffers(); + void ResetVppBuffers(); + + /** \brief The function syncs arrays of free and used surfaces. + * + * If Media SDK used surface for internal needs and unlocked it, the function moves such a surface + * back to the free surfaces array. + */ + void SyncFrameSurfaces(); + void SyncVppFrameSurfaces(); + + /** \brief Returns surface which corresponds to the given one in Media SDK format (mfxFrameSurface1). + * + * @note This function will not detach the surface from the array, perform this explicitly. + */ + inline msdkFrameSurface* FindUsedSurface(mfxFrameSurface1* frame) + { + return (msdkFrameSurface*)(frame); + } + + inline void AddFreeOutputSurfaceUnsafe(msdkOutputSurface* surface) + { + msdkOutputSurface* head = m_pFreeOutputSurfaces; + + MSDK_SELF_CHECK(surface); + MSDK_SELF_CHECK(!surface->next); + m_pFreeOutputSurfaces = surface; + m_pFreeOutputSurfaces->next = head; + } + inline void AddFreeOutputSurface(msdkOutputSurface* surface) { + AutomaticMutex lock(m_Mutex); + AddFreeOutputSurfaceUnsafe(surface); + } + + inline msdkOutputSurface* GetFreeOutputSurfaceUnsafe() + { + msdkOutputSurface* surface = NULL; + + if (!m_pFreeOutputSurfaces) { + m_Mutex.Unlock(); + AllocOutputBuffer(); + m_Mutex.Lock(); + } + if (m_pFreeOutputSurfaces) { + surface = m_pFreeOutputSurfaces; + m_pFreeOutputSurfaces = m_pFreeOutputSurfaces->next; + surface->next = NULL; + MSDK_SELF_CHECK(!surface->next); + } + return surface; + } + inline msdkOutputSurface* GetFreeOutputSurface() { + AutomaticMutex lock(m_Mutex); + return GetFreeOutputSurfaceUnsafe(); + } + + /** \brief Function returns surface data to the corresponding buffers. + */ + inline void ReturnSurfaceToBuffers(msdkOutputSurface* output_surface) + { + MSDK_SELF_CHECK(output_surface); + MSDK_SELF_CHECK(output_surface->surface); + MSDK_SELF_CHECK(output_surface->syncp); + + msdk_atomic_dec16(&(output_surface->surface->render_lock)); + + output_surface->surface = NULL; + output_surface->syncp = NULL; + + AddFreeOutputSurface(output_surface); + } + +protected: // variables + mfxU32 m_SurfacesNumber; + mfxU32 m_OutputSurfacesNumber; + msdkFrameSurface* m_pSurfaces; + msdkFrameSurface* m_pVppSurfaces; + MSDKMutex m_Mutex; + + // LIFO list of frame surfaces + msdkFreeSurfacesPool m_FreeSurfacesPool; + msdkFreeSurfacesPool m_FreeVppSurfacesPool; + + // random access, predicted as FIFO + msdkUsedSurfacesPool m_UsedSurfacesPool; + msdkUsedSurfacesPool m_UsedVppSurfacesPool; + + // LIFO list of output surfaces + msdkOutputSurface* m_pFreeOutputSurfaces; + + // FIFO list of surfaces + msdkOutputSurfacesPool m_OutputSurfacesPool; + msdkOutputSurfacesPool m_DeliveredSurfacesPool; + +private: + CBuffering(const CBuffering&); + void operator=(const CBuffering&); +}; + +#endif // __MFX_BUFFERING_H__ diff --git a/vshampor/deshuffler/sample_common/include/mfx_itt_trace.h b/vshampor/deshuffler/sample_common/include/mfx_itt_trace.h new file mode 100644 index 0000000..8b3840d --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/mfx_itt_trace.h @@ -0,0 +1,59 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __MFX_ITT_TRACE_H__ +#define __MFX_ITT_TRACE_H__ + +#ifdef ITT_SUPPORT +#include +#endif + + +#ifdef ITT_SUPPORT + +static inline __itt_domain* mfx_itt_get_domain() { + static __itt_domain *domain = NULL; + + if (!domain) domain = __itt_domain_create("MFX_SAMPLES"); + return domain; +} + +class MFX_ITT_Tracer +{ +public: + MFX_ITT_Tracer(const char* trace_name) + { + m_domain = mfx_itt_get_domain(); + if (m_domain) + __itt_task_begin(m_domain, __itt_null, __itt_null, __itt_string_handle_create(trace_name)); + } + ~MFX_ITT_Tracer() + { + if (m_domain) __itt_task_end(m_domain); + } +private: + __itt_domain* m_domain; +}; +#define MFX_ITT_TASK(x) MFX_ITT_Tracer __mfx_itt_tracer(x); + +#else +#define MFX_ITT_TASK(x) +#endif + +#endif //__MFX_ITT_TRACE_H__ diff --git a/vshampor/deshuffler/sample_common/include/mfx_plugin_base.h b/vshampor/deshuffler/sample_common/include/mfx_plugin_base.h new file mode 100644 index 0000000..19a9217 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/mfx_plugin_base.h @@ -0,0 +1,40 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once + + +#include + +typedef MFXDecoderPlugin* (*mfxCreateDecoderPlugin)(); +typedef MFXEncoderPlugin* (*mfxCreateEncoderPlugin)(); +typedef MFXGenericPlugin* (*mfxCreateGenericPlugin)(); + +class PluginFactory +{ + static MFXDecoderPlugin* CreateDecoderPlugin() { + return PluginFactory::CreateDecoderPlugin(); + } + static MFXEncoderPlugin* CreateEncoderPlugin() { + return PluginFactory::CreateEncoderPlugin(); + } + static MFXGenericPlugin* CreateGenericPlugin() { + return PluginFactory::CreateGenericPlugin(); + } +}; \ No newline at end of file diff --git a/vshampor/deshuffler/sample_common/include/mfx_plugin_module.h b/vshampor/deshuffler/sample_common/include/mfx_plugin_module.h new file mode 100644 index 0000000..4092e51 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/mfx_plugin_module.h @@ -0,0 +1,40 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once + +#include "mfxplugin++.h" + +struct PluginModuleTemplate { + typedef MFXDecoderPlugin* (*fncCreateDecoderPlugin)(); + typedef MFXEncoderPlugin* (*fncCreateEncoderPlugin)(); + typedef MFXAudioDecoderPlugin* (*fncCreateAudioDecoderPlugin)(); + typedef MFXAudioEncoderPlugin* (*fncCreateAudioEncoderPlugin)(); + typedef MFXGenericPlugin* (*fncCreateGenericPlugin)(); + typedef mfxStatus (MFX_CDECL *CreatePluginPtr_t)(mfxPluginUID uid, mfxPlugin* plugin); + + fncCreateDecoderPlugin CreateDecoderPlugin; + fncCreateEncoderPlugin CreateEncoderPlugin; + fncCreateGenericPlugin CreateGenericPlugin; + CreatePluginPtr_t CreatePlugin; + fncCreateAudioDecoderPlugin CreateAudioDecoderPlugin; + fncCreateAudioEncoderPlugin CreateAudioEncoderPlugin; +}; + +extern PluginModuleTemplate g_PluginModule; diff --git a/vshampor/deshuffler/sample_common/include/mfx_samples_config.h b/vshampor/deshuffler/sample_common/include/mfx_samples_config.h new file mode 100644 index 0000000..0fe88d8 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/mfx_samples_config.h @@ -0,0 +1,24 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __MFX_SAMPLES_CONFIG__ +#define __MFX_SAMPLES_CONFIG__ + + +#endif //__MFX_SAMPLES_CONFIG__ diff --git a/vshampor/deshuffler/sample_common/include/parameters_dumper.h b/vshampor/deshuffler/sample_common/include/parameters_dumper.h new file mode 100644 index 0000000..3f65e8e --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/parameters_dumper.h @@ -0,0 +1,73 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __PARAMETERS_DUMPER_H__ +#define __PARAMETERS_DUMPER_H__ +#include "sample_defs.h" + +class CParametersDumper +{ +protected: + static void SerializeFrameInfoStruct(msdk_ostream& sstr,msdk_string prefix,mfxFrameInfo& info); + static void SerializeMfxInfoMFXStruct(msdk_ostream& sstr,msdk_string prefix,mfxInfoMFX& info); + static void SerializeExtensionBuffer(msdk_ostream& sstr,msdk_string prefix,mfxExtBuffer* pExtBuffer); + static void SerializeVPPCompInputStream(msdk_ostream& sstr, msdk_string prefix, mfxVPPCompInputStream& info); + + template + static mfxStatus GetUnitParams(T* pMfxUnit, const mfxVideoParam* pPresetParams, mfxVideoParam* pOutParams) + { + memset(pOutParams,0, sizeof(mfxVideoParam)); + mfxExtBuffer** paramsArray = new mfxExtBuffer*[pPresetParams->NumExtParam]; + for (int paramNum = 0; paramNum < pPresetParams->NumExtParam; paramNum++) + { + mfxExtBuffer* buf = pPresetParams->ExtParam[paramNum]; + mfxExtBuffer* newBuf = (mfxExtBuffer*)new mfxU8[buf->BufferSz]; + memset(newBuf, 0, buf->BufferSz); + newBuf->BufferId = buf->BufferId; + newBuf->BufferSz = buf->BufferSz; + paramsArray[paramNum]=newBuf; + } + pOutParams->NumExtParam = pPresetParams->NumExtParam; + pOutParams->ExtParam = paramsArray; + + mfxStatus sts = pMfxUnit->GetVideoParam(pOutParams); + MSDK_CHECK_STATUS_SAFE(sts, "Cannot read configuration from encoder: GetVideoParam failed", ClearExtBuffs(pOutParams)); + + return MFX_ERR_NONE; + } + + static void ClearExtBuffs(mfxVideoParam* params) + { + // Cleaning params array + for (int paramNum = 0; paramNum < params->NumExtParam; paramNum++) + { + delete[] params->ExtParam[paramNum]; + } + delete[] params->ExtParam; + params->ExtParam = NULL; + params->NumExtParam = 0; + } + +public: + static void SerializeVideoParamStruct(msdk_ostream& sstr,msdk_string sectionName,mfxVideoParam& info,bool shouldUseVPPSection=false); + static mfxStatus DumpLibraryConfiguration(msdk_string fileName, MFXVideoDECODE* pMfxDec, MFXVideoVPP* pMfxVPP, MFXVideoENCODE* pMfxEnc, + const mfxVideoParam* pDecoderPresetParams, const mfxVideoParam* pVPPPresetParams, const mfxVideoParam* pEncoderPresetParams); + static void ShowConfigurationDiff(msdk_ostream& sstr1, msdk_ostream& sstr2); +}; +#endif diff --git a/vshampor/deshuffler/sample_common/include/plugin_loader.h b/vshampor/deshuffler/sample_common/include/plugin_loader.h new file mode 100644 index 0000000..f7c17ec --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/plugin_loader.h @@ -0,0 +1,267 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once + +#ifndef __PLUGIN_LOADER_H__ +#define __PLUGIN_LOADER_H__ + +#include "vm/so_defs.h" +#include "sample_utils.h" +#include "plugin_utils.h" +//#include "mfx_plugin_module.h" +#include +#include // for std::setfill, std::setw +#include // for std::unique_ptr + +class MsdkSoModule +{ +protected: + msdk_so_handle m_module; +public: + MsdkSoModule() + : m_module(NULL) + { + } + MsdkSoModule(const msdk_string & pluginName) + : m_module(NULL) + { + m_module = msdk_so_load(pluginName.c_str()); + if (NULL == m_module) + { + MSDK_TRACE_ERROR(msdk_tstring(MSDK_CHAR("Failed to load shared module: ")) + pluginName); + } + } + template + T GetAddr(const std::string & fncName) + { + T pCreateFunc = reinterpret_cast(msdk_so_get_addr(m_module, fncName.c_str())); + if (NULL == pCreateFunc) { + MSDK_TRACE_ERROR(msdk_tstring("Failed to get function addres: ") + fncName.c_str()); + } + return pCreateFunc; + } + + virtual ~MsdkSoModule() + { + if (m_module) + { + msdk_so_free(m_module); + m_module = NULL; + } + } +}; + +/* +* Rationale: class to load+register any mediasdk plugin decoder/encoder/generic by given name +*/ +class PluginLoader : public MFXPlugin +{ +protected: + mfxPluginType ePluginType; + + mfxSession m_session; + mfxPluginUID m_uid; + +private: + const msdk_char* msdkGetPluginName(const mfxPluginUID& guid) + { + if (AreGuidsEqual(guid, MFX_PLUGINID_HEVCD_SW)) + return MSDK_STRING("Intel (R) Media SDK plugin for HEVC DECODE"); + else if(AreGuidsEqual(guid, MFX_PLUGINID_HEVCD_HW)) + return MSDK_STRING("Intel (R) Media SDK HW plugin for HEVC DECODE"); + else if(AreGuidsEqual(guid, MFX_PLUGINID_HEVCE_SW)) + return MSDK_STRING("Intel (R) Media SDK plugin for HEVC ENCODE"); + else if(AreGuidsEqual(guid, MFX_PLUGINID_HEVCE_HW)) + return MSDK_STRING("Intel (R) Media SDK HW plugin for HEVC ENCODE"); + else if(AreGuidsEqual(guid, MFX_PLUGINID_VP8E_HW)) + return MSDK_STRING("Intel (R) Media SDK HW plugin for VP8 ENCODE"); + else if(AreGuidsEqual(guid, MFX_PLUGINID_VP8D_HW)) + return MSDK_STRING("Intel (R) Media SDK HW plugin for VP8 DECODE"); + else if(AreGuidsEqual(guid, MFX_PLUGINID_VP9E_HW)) + return MSDK_STRING("Intel (R) Media SDK HW plugin for VP9 ENCODE"); + else if(AreGuidsEqual(guid, MFX_PLUGINID_VP9D_HW)) + return MSDK_STRING("Intel (R) Media SDK HW plugin for VP9 DECODE"); + else if(AreGuidsEqual(guid, MFX_PLUGINID_H264LA_HW)) + return MSDK_STRING("Intel (R) Media SDK plugin for LA ENC"); + else if(AreGuidsEqual(guid, MFX_PLUGINID_ITELECINE_HW)) + return MSDK_STRING("Intel (R) Media SDK PTIR plugin (HW)"); + else if (AreGuidsEqual(guid, MFX_PLUGINID_HEVCE_GACC)) + return MSDK_STRING("Intel (R) Media SDK GPU-Accelerated plugin for HEVC ENCODE"); + else +#if MFX_VERSION >= 1027 + if (AreGuidsEqual(guid, MFX_PLUGINID_HEVC_FEI_ENCODE)) + return MSDK_STRING("Intel (R) Media SDK HW plugin for HEVC FEI ENCODE"); + else +#endif + return MSDK_STRING("Unknown plugin"); + } + +public: + PluginLoader(mfxPluginType type, mfxSession session, const mfxPluginUID & uid, mfxU32 version, const mfxChar *pluginName, mfxU32 len) + : ePluginType(type) + , m_session() + , m_uid() + { + len; pluginName; + mfxStatus sts = MFX_ERR_NONE; + msdk_stringstream strStream; + + MSDK_MEMCPY(&m_uid, &uid, sizeof(mfxPluginUID)); + for (size_t i = 0; i != sizeof(mfxPluginUID); i++) + { + strStream << MSDK_STRING("0x") << std::setfill(MSDK_CHAR('0')) << std::setw(2) << std::hex << (int)m_uid.Data[i]; + if (i != (sizeof(mfxPluginUID)-1)) strStream << MSDK_STRING(", "); + } + + if ((ePluginType == MFX_PLUGINTYPE_AUDIO_DECODE) || + (ePluginType == MFX_PLUGINTYPE_AUDIO_ENCODE)) + { + // Audio plugins are not loaded by path + sts = MFX_ERR_UNSUPPORTED; + } + else + { + sts = MFXVideoUSER_LoadByPath(session, &m_uid, version, pluginName, len); + } + + if (MFX_ERR_NONE != sts) + { + MSDK_TRACE_ERROR(MSDK_STRING("Failed to load plugin from GUID, sts=") << sts << MSDK_STRING(": { ") << strStream.str().c_str() << MSDK_STRING(" } (") << msdkGetPluginName(m_uid) << MSDK_STRING(")")); + } + else + { + MSDK_TRACE_INFO(MSDK_STRING("Plugin was loaded from GUID")); + m_session = session; + } + } + + PluginLoader(mfxPluginType type, mfxSession session, const mfxPluginUID & uid, mfxU32 version) + : ePluginType(type) + , m_session() + , m_uid() + { + mfxStatus sts = MFX_ERR_NONE; + msdk_stringstream strStream; + + MSDK_MEMCPY(&m_uid, &uid, sizeof(mfxPluginUID)); + for (size_t i = 0; i != sizeof(mfxPluginUID); i++) + { + strStream << MSDK_STRING("0x") << std::setfill(MSDK_CHAR('0')) << std::setw(2) << std::hex << (int)m_uid.Data[i]; + if (i != (sizeof(mfxPluginUID)-1)) strStream << MSDK_STRING(", "); + } + + if ((ePluginType == MFX_PLUGINTYPE_AUDIO_DECODE) || + (ePluginType == MFX_PLUGINTYPE_AUDIO_ENCODE)) + { + sts = MFXAudioUSER_Load(session, &m_uid, version); + } + else + { + sts = MFXVideoUSER_Load(session, &m_uid, version); + } + + if (MFX_ERR_NONE != sts) + { + MSDK_TRACE_ERROR(MSDK_STRING("Failed to load plugin from GUID, sts=") << sts << MSDK_STRING(": { ") << strStream.str().c_str() << MSDK_STRING(" } (") << msdkGetPluginName(m_uid) << MSDK_STRING(")")); + } + else + { + MSDK_TRACE_INFO(MSDK_STRING("Plugin was loaded from GUID")<< MSDK_STRING(": { ") << strStream.str().c_str() << MSDK_STRING(" } (") << msdkGetPluginName(m_uid) << MSDK_STRING(")")); + m_session = session; + } + } + + virtual ~PluginLoader() + { + mfxStatus sts = MFX_ERR_NONE; + if (m_session) + { + if ((ePluginType == MFX_PLUGINTYPE_AUDIO_DECODE) || + (ePluginType == MFX_PLUGINTYPE_AUDIO_ENCODE)) + { + sts = MFXAudioUSER_UnLoad(m_session, &m_uid); + } + else + { + sts = MFXVideoUSER_UnLoad(m_session, &m_uid); + } + + if (sts != MFX_ERR_NONE) + { + MSDK_TRACE_ERROR(MSDK_STRING("Failed to unload plugin from GUID, sts=") << sts); + } + else + { + MSDK_TRACE_INFO(MSDK_STRING("MFXBaseUSER_UnLoad(session=0x") << m_session << MSDK_STRING("), sts=") << sts); + } + } + } + + bool IsOk() { + return m_session != 0; + } + virtual mfxStatus PluginInit( mfxCoreInterface *core ) { + core; + return MFX_ERR_NULL_PTR; + } + virtual mfxStatus PluginClose() { + return MFX_ERR_NULL_PTR; + } + virtual mfxStatus GetPluginParam( mfxPluginParam *par ) { + par; + return MFX_ERR_NULL_PTR; + } + virtual mfxStatus Execute( mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a ) { + task; uid_p; uid_a; + return MFX_ERR_NULL_PTR; + } + virtual mfxStatus FreeResources( mfxThreadTask task, mfxStatus sts ) { + task; sts; + return MFX_ERR_NULL_PTR; + } + virtual void Release() { + } + virtual mfxStatus Close() { + return MFX_ERR_NULL_PTR; + } + virtual mfxStatus SetAuxParams( void* auxParam, int auxParamSize ) { + auxParam; auxParamSize; + return MFX_ERR_NULL_PTR; + } +}; + +inline MFXPlugin * LoadPluginByType(mfxPluginType type, mfxSession session, const mfxPluginUID & uid, mfxU32 version, const mfxChar *pluginName, mfxU32 len) { + std::unique_ptr plg(new PluginLoader (type, session, uid, version, pluginName, len)); + return plg->IsOk() ? plg.release() : NULL; +} + +inline MFXPlugin * LoadPluginByGUID(mfxPluginType type, mfxSession session, const mfxPluginUID & uid, mfxU32 version) { + std::unique_ptr plg(new PluginLoader (type, session, uid, version)); + return plg->IsOk() ? plg.release() : NULL; +} + +inline MFXPlugin * LoadPlugin(mfxPluginType type, mfxSession session, const mfxPluginUID & uid, mfxU32 version, const mfxChar *pluginName, mfxU32 len) { + return LoadPluginByType(type, session, uid, version, pluginName, len); +} + +inline MFXPlugin * LoadPlugin(mfxPluginType type, mfxSession session, const mfxPluginUID & uid, mfxU32 version) { + return LoadPluginByGUID(type, session, uid, version); +} +#endif // PLUGIN_LOADER \ No newline at end of file diff --git a/vshampor/deshuffler/sample_common/include/plugin_utils.h b/vshampor/deshuffler/sample_common/include/plugin_utils.h new file mode 100644 index 0000000..b01f36c --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/plugin_utils.h @@ -0,0 +1,67 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __PLUGIN_UTILS_H__ +#define __PLUGIN_UTILS_H__ + +#include "sample_defs.h" +#include "sample_types.h" + + #define MSDK_CPU_ROTATE_PLUGIN MSDK_STRING("libsample_rotate_plugin.so") + #define MSDK_OCL_ROTATE_PLUGIN MSDK_STRING("libsample_plugin_opencl.so") + +typedef mfxI32 msdkComponentType; +enum +{ + MSDK_VDECODE = 0x0001, + MSDK_VENCODE = 0x0002, + MSDK_VPP = 0x0004, + MSDK_VENC = 0x0008, +#if (MFX_VERSION >= 1027) + MSDK_FEI = 0x1000, +#endif +}; + +typedef enum { + MFX_PLUGINLOAD_TYPE_GUID = 1, + MFX_PLUGINLOAD_TYPE_FILE = 2 +} MfxPluginLoadType; + +struct sPluginParams +{ + mfxPluginUID pluginGuid; + mfxChar strPluginPath[MSDK_MAX_FILENAME_LEN]; + MfxPluginLoadType type; + sPluginParams() + { + MSDK_ZERO_MEMORY(*this); + } +}; + +static const mfxPluginUID MSDK_PLUGINGUID_NULL = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + +bool AreGuidsEqual(const mfxPluginUID& guid1, const mfxPluginUID& guid2); + +const mfxPluginUID & msdkGetPluginUID(mfxIMPL impl, msdkComponentType type, mfxU32 uCodecid); + +sPluginParams ParsePluginGuid(msdk_char* ); +sPluginParams ParsePluginPath(msdk_char* ); +mfxStatus ConvertStringToGuid(const msdk_string & strGuid, mfxPluginUID & mfxGuid); + +#endif //__PLUGIN_UTILS_H__ diff --git a/vshampor/deshuffler/sample_common/include/sample_defs.h b/vshampor/deshuffler/sample_common/include/sample_defs.h new file mode 100644 index 0000000..7756532 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/sample_defs.h @@ -0,0 +1,190 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __SAMPLE_DEFS_H__ +#define __SAMPLE_DEFS_H__ + +#include +#include + +#include "mfxdefs.h" +#include "vm/strings_defs.h" +#include "vm/file_defs.h" +#include "vm/time_defs.h" + +#define _MSDK_API (MFX_VERSION_MAJOR*256+MFX_VERSION_MINOR) +#define MSDK_API(M,MM) (M*256+MM) + +// Run-time HSBC +// the condition below must be changed to MFX_VERSION >= 1027 after API is promoted to 1.27 +#if (MFX_VERSION >= MFX_VERSION_NEXT) +#define ENABLE_VPP_RUNTIME_HSBC +#endif + +#if (MFX_VERSION >= 1026) +#define ENABLE_MCTF +#if (MFX_VERSION >= MFX_VERSION_NEXT) +//---MCTF, extended interface +#define ENABLE_MCTF_EXT +enum {MCTF_BITRATE_MULTIPLIER = 100000}; +#endif +#endif + + +#if defined(WIN32) || defined(WIN64) + +enum { + MFX_HANDLE_DEVICEWINDOW = 0x101 /* A handle to the render window */ +}; //mfxHandleType + +#ifndef D3D_SURFACES_SUPPORT +#define D3D_SURFACES_SUPPORT 1 +#endif + +#if defined(_WIN32) && !defined(MFX_D3D11_SUPPORT) +#include +#if (NTDDI_VERSION >= NTDDI_VERSION_FROM_WIN32_WINNT2(0x0602)) // >= _WIN32_WINNT_WIN8 + #define MFX_D3D11_SUPPORT 1 // Enable D3D11 support if SDK allows +#else + #define MFX_D3D11_SUPPORT 0 +#endif +#endif // #if defined(WIN32) && !defined(MFX_D3D11_SUPPORT) +#endif // #if defined(WIN32) || defined(WIN64) + +enum +{ +#define __DECLARE(type) MFX_MONITOR_ ## type + __DECLARE(Unknown) = 0, + __DECLARE(AUTO) = __DECLARE(Unknown), + __DECLARE(VGA), + __DECLARE(DVII), + __DECLARE(DVID), + __DECLARE(DVIA), + __DECLARE(Composite), + __DECLARE(SVIDEO), + __DECLARE(LVDS), + __DECLARE(Component), + __DECLARE(9PinDIN), + __DECLARE(HDMIA), + __DECLARE(HDMIB), + __DECLARE(eDP), + __DECLARE(TV), + __DECLARE(DisplayPort), +#if defined(DRM_MODE_CONNECTOR_VIRTUAL) // from libdrm 2.4.59 + __DECLARE(VIRTUAL), +#endif +#if defined(DRM_MODE_CONNECTOR_DSI) // from libdrm 2.4.59 + __DECLARE(DSI), +#endif + __DECLARE(MAXNUMBER) +#undef __DECLARE +}; + +#if defined(LIBVA_SUPPORT) + +enum LibVABackend +{ + MFX_LIBVA_AUTO, + MFX_LIBVA_DRM, + MFX_LIBVA_DRM_RENDERNODE = MFX_LIBVA_DRM, + MFX_LIBVA_DRM_MODESET, + MFX_LIBVA_X11, + MFX_LIBVA_WAYLAND +}; + +#endif + +//affects win32 winnt version macro +#include "vm/time_defs.h" +#include "sample_utils.h" + + +#define MSDK_DEC_WAIT_INTERVAL 300000 +#define MSDK_ENC_WAIT_INTERVAL 300000 +#define MSDK_VPP_WAIT_INTERVAL 300000 +#define MSDK_SURFACE_WAIT_INTERVAL 20000 +#define MSDK_DEVICE_FREE_WAIT_INTERVAL 30000 +#define MSDK_WAIT_INTERVAL MSDK_DEC_WAIT_INTERVAL+3*MSDK_VPP_WAIT_INTERVAL+MSDK_ENC_WAIT_INTERVAL // an estimate for the longest pipeline we have in samples + +#define MSDK_INVALID_SURF_IDX 0xFFFF + +#define MSDK_MAX_FILENAME_LEN 1024 +#define MSDK_MAX_USER_DATA_UNREG_SEI_LEN 80 + +#define MSDK_PRINT_RET_MSG(ERR,MSG) {msdk_stringstream tmpStr1;tmpStr1< MFX_ERR_NONE) {MSDK_PRINT_WRN_MSG(X, MSG); }} +#define MSDK_CHECK_PARSE_RESULT(P, X, ERR) {if ((X) > (P)) {return ERR;}} + +#define MSDK_CHECK_STATUS_SAFE(X, FUNC, ADD) {if ((X) < MFX_ERR_NONE) {ADD; MSDK_PRINT_RET_MSG(X, FUNC); return X;}} +#define MSDK_IGNORE_MFX_STS(P, X) {if ((X) == (P)) {P = MFX_ERR_NONE;}} +#define MSDK_CHECK_POINTER(P, ...) {if (!(P)) {msdk_stringstream tmpStr4;tmpStr4<Release(); X = NULL; }} +#define MSDK_SAFE_FREE(X) {if (X) { free(X); X = NULL; }} + +#ifndef MSDK_SAFE_DELETE +#define MSDK_SAFE_DELETE(P) {if (P) {delete P; P = NULL;}} +#endif // MSDK_SAFE_DELETE + +#define MSDK_ZERO_MEMORY(VAR) {memset(&VAR, 0, sizeof(VAR));} +#define MSDK_MAX(A, B) (((A) > (B)) ? (A) : (B)) +#define MSDK_MIN(A, B) (((A) < (B)) ? (A) : (B)) +#define MSDK_ALIGN16(value) (((value + 15) >> 4) << 4) // round up to a multiple of 16 +#define MSDK_ALIGN32(value) (((value + 31) >> 5) << 5) // round up to a multiple of 32 +#define MSDK_ALIGN(value, alignment) (alignment) * ( (value) / (alignment) + (((value) % (alignment)) ? 1 : 0)) +#define MSDK_ARRAY_LEN(value) (sizeof(value) / sizeof(value[0])) + +#ifndef UNREFERENCED_PARAMETER +#define UNREFERENCED_PARAMETER(par) (par) +#endif + +#define MFX_IMPL_VIA_MASK(x) (0x0f00 & (x)) + +// Deprecated +#define MSDK_PRINT_RET_MSG_(ERR) {msdk_printf(MSDK_STRING("\nReturn on error: error code %d,\t%s\t%d\n\n"), (int)ERR, MSDK_STRING(__FILE__), __LINE__);} +#define MSDK_CHECK_RESULT(P, X, ERR) {if ((X) > (P)) {MSDK_PRINT_RET_MSG_(ERR); return ERR;}} +#define MSDK_CHECK_RESULT_SAFE(P, X, ERR, ADD) {if ((X) > (P)) {ADD; MSDK_PRINT_RET_MSG_(ERR); return ERR;}} + +#endif //__SAMPLE_DEFS_H__ + diff --git a/vshampor/deshuffler/sample_common/include/sample_types.h b/vshampor/deshuffler/sample_common/include/sample_types.h new file mode 100644 index 0000000..5b17f60 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/sample_types.h @@ -0,0 +1,49 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __SAMPLE_TYPES_H__ +#define __SAMPLE_TYPES_H__ + +#ifdef UNICODE + #define msdk_cout std::wcout + #define msdk_err std::wcerr +#else + #define msdk_cout std::cout + #define msdk_err std::cerr +#endif + +typedef std::basic_string msdk_string; +typedef std::basic_stringstream msdk_stringstream; +typedef std::basic_ostream > msdk_ostream; +typedef std::basic_istream > msdk_istream; +typedef std::basic_fstream > msdk_fstream; + +#if defined(_UNICODE) +#define MSDK_MAKE_BYTE_STRING(src,dest) \ + {\ + std::wstring wstr(src);\ + std::string str(wstr.begin(), wstr.end());\ + strcpy_s(dest, str.c_str());\ + } +#else +#define MSDK_MAKE_BYTE_STRING(src,dest) msdk_strcopy(dest, src); +#endif + + +#endif //__SAMPLE_TYPES_H__ diff --git a/vshampor/deshuffler/sample_common/include/sample_utils.h b/vshampor/deshuffler/sample_common/include/sample_utils.h new file mode 100644 index 0000000..77147b4 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/sample_utils.h @@ -0,0 +1,855 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __SAMPLE_UTILS_H__ +#define __SAMPLE_UTILS_H__ + +#include +#include +#include +#include + +#include "mfxstructures.h" +#include "mfxvideo.h" +#include "mfxvideo++.h" +#include "mfxjpeg.h" +#include "mfxplugin.h" + +#include "vm/strings_defs.h" +#include "vm/file_defs.h" +#include "vm/time_defs.h" +#include "vm/atomic_defs.h" +#include "vm/thread_defs.h" +#include + +#include "sample_types.h" + +#include "abstract_splitter.h" +#include "avc_bitstream.h" +#include "avc_spl.h" +#include "avc_headers.h" +#include "avc_nal_spl.h" + +#ifdef ENABLE_MCTF +#include +#endif + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +//! Base class for types that should not be assigned. +class no_assign { + // Deny assignment + void operator=( const no_assign& ); +public: +#if __GNUC__ + //! Explicitly define default construction, because otherwise gcc issues gratuitous warning. + no_assign() {} +#endif /* __GNUC__ */ +}; + +//! Base class for types that should not be copied or assigned. +class no_copy: no_assign { + //! Deny copy construction + no_copy( const no_copy& ); +public: + //! Allow default construction + no_copy() {} +}; + +struct DeletePtr { + template T* operator () (T* p) const { + delete p; + return 0; + } +}; + +enum { + CODEC_VP8 = MFX_MAKEFOURCC('V','P','8',' '), + CODEC_MVC = MFX_MAKEFOURCC('M','V','C',' '), +}; + +#define MFX_CODEC_DUMP MFX_MAKEFOURCC('D','U','M','P') +#define MFX_CODEC_RGB4 MFX_FOURCC_RGB4 + +enum +{ + MFX_FOURCC_IMC3 = MFX_MAKEFOURCC('I','M','C','3'), + MFX_FOURCC_YUV400 = MFX_MAKEFOURCC('4','0','0','P'), + MFX_FOURCC_YUV411 = MFX_MAKEFOURCC('4','1','1','P'), + MFX_FOURCC_YUV422H = MFX_MAKEFOURCC('4','2','2','H'), + MFX_FOURCC_YUV422V = MFX_MAKEFOURCC('4','2','2','V'), + MFX_FOURCC_YUV444 = MFX_MAKEFOURCC('4','4','4','P'), +#if (MFX_VERSION <= 1027) + MFX_FOURCC_RGBP = MFX_MAKEFOURCC('R','G','B','P'), +#else +#endif + MFX_FOURCC_I420 = MFX_MAKEFOURCC('I','4','2','0') +}; + +bool IsDecodeCodecSupported(mfxU32 codecFormat); +bool IsEncodeCodecSupported(mfxU32 codecFormat); +bool IsPluginCodecSupported(mfxU32 codecFormat); + +class CSmplYUVReader +{ +public : + typedef std::list::iterator ls_iterator; + CSmplYUVReader(); + virtual ~CSmplYUVReader(); + + virtual void Close(); + virtual mfxStatus Init(std::list inputs, mfxU32 ColorFormat, bool shouldShiftP010=false); + virtual mfxStatus LoadNextFrame(mfxFrameSurface1* pSurface); + virtual void Reset(); + mfxU32 m_ColorFormat; // color format of input YUV data, YUV420 or NV12 + +protected: + + std::vector m_files; + + bool shouldShiftP010High; + bool m_bInited; +}; + +class CSmplBitstreamWriter +{ +public : + + CSmplBitstreamWriter(); + virtual ~CSmplBitstreamWriter(); + + virtual mfxStatus Init(const msdk_char *strFileName); + virtual mfxStatus WriteNextFrame(mfxBitstream *pMfxBitstream, bool isPrint = true); + virtual mfxStatus Reset(); + virtual void Close(); + mfxU32 m_nProcessedFramesNum; + +protected: + FILE* m_fSource; + bool m_bInited; + msdk_string m_sFile; +}; + +class CSmplYUVWriter +{ +public : + + CSmplYUVWriter(); + virtual ~CSmplYUVWriter(); + + virtual void Close(); + virtual mfxStatus Init(const msdk_char *strFileName, const mfxU32 numViews); + virtual mfxStatus Reset(); + virtual mfxStatus WriteNextFrame(mfxFrameSurface1 *pSurface); + virtual mfxStatus WriteNextFrameI420(mfxFrameSurface1 *pSurface); + + void SetMultiView() { m_bIsMultiView = true; } + +protected: + FILE *m_fDest, **m_fDestMVC; + bool m_bInited, m_bIsMultiView; + mfxU32 m_numCreatedFiles; + msdk_string m_sFile; + mfxU32 m_nViews; +}; + +class CSmplBitstreamReader +{ +public : + + CSmplBitstreamReader(); + virtual ~CSmplBitstreamReader(); + + //resets position to file begin + virtual void Reset(); + virtual void Close(); + virtual mfxStatus Init(const msdk_char *strFileName); + virtual mfxStatus ReadNextFrame(mfxBitstream *pBS); + +protected: + FILE* m_fSource; + bool m_bInited; +}; + +class CH264FrameReader : public CSmplBitstreamReader +{ +public: + CH264FrameReader(); + virtual ~CH264FrameReader(); + + /** Free resources.*/ + virtual void Close(); + virtual mfxStatus Init(const msdk_char *strFileName); + virtual mfxStatus ReadNextFrame(mfxBitstream *pBS); + +private: + mfxBitstream *m_processedBS; + // input bit stream + std::unique_ptr m_originalBS; + + mfxStatus PrepareNextFrame(mfxBitstream *in, mfxBitstream **out); + + // is stream ended + bool m_isEndOfStream; + + std::unique_ptr m_pNALSplitter; + FrameSplitterInfo *m_frame; + mfxU8 *m_plainBuffer; + mfxU32 m_plainBufferSize; + mfxBitstream m_outBS; +}; + +//provides output bistream with at least 1 frame, reports about error +class CJPEGFrameReader : public CSmplBitstreamReader +{ + enum JPEGMarker + { + SOI=0xD8FF, + EOI=0xD9FF + }; +public: + virtual mfxStatus ReadNextFrame(mfxBitstream *pBS); +protected: + mfxU32 FindMarker(mfxBitstream *pBS,mfxU32 startOffset,JPEGMarker marker); +}; + +//appends output bistream with exactly 1 frame, reports about error +class CIVFFrameReader : public CSmplBitstreamReader +{ +public: + CIVFFrameReader(); + virtual mfxStatus Init(const msdk_char *strFileName); + virtual mfxStatus ReadNextFrame(mfxBitstream *pBS); + +protected: + + /*bytes 0-3 signature: 'DKIF' + bytes 4-5 version (should be 0) + bytes 6-7 length of header in bytes + bytes 8-11 codec FourCC (e.g., 'VP80') + bytes 12-13 width in pixels + bytes 14-15 height in pixels + bytes 16-19 frame rate + bytes 20-23 time scale + bytes 24-27 number of frames in file + bytes 28-31 unused*/ + + struct DKIFHrd + { + mfxU32 dkif; + mfxU16 version; + mfxU16 header_len; + mfxU32 codec_FourCC; + mfxU16 width; + mfxU16 height; + mfxU32 frame_rate; + mfxU32 time_scale; + mfxU32 num_frames; + mfxU32 unused; + }m_hdr; +}; + +// writes bitstream to duplicate-file & supports joining +// (for ViewOutput encoder mode) +class CSmplBitstreamDuplicateWriter : public CSmplBitstreamWriter +{ +public: + CSmplBitstreamDuplicateWriter(); + + virtual mfxStatus InitDuplicate(const msdk_char *strFileName); + virtual mfxStatus JoinDuplicate(CSmplBitstreamDuplicateWriter *pJoinee); + virtual mfxStatus WriteNextFrame(mfxBitstream *pMfxBitstream, bool isPrint = true); + virtual void Close(); +protected: + FILE* m_fSourceDuplicate; + bool m_bJoined; +}; + +//timeinterval calculation helper + +template +class CTimeInterval : private no_copy +{ + static double g_Freq; + double &m_start; + double m_own;//reference to this if external counter not required + //since QPC functions are quite slow it makes sense to optionally enable them + bool m_bEnable; + msdk_tick m_StartTick; + +public: + CTimeInterval(double &dRef , bool bEnable = true) + : m_start(dRef) + , m_bEnable(bEnable) + , m_StartTick(0) + { + if (!m_bEnable) + return; + Initialize(); + } + CTimeInterval(bool bEnable = true) + : m_start(m_own) + , m_own() + , m_bEnable(bEnable) + , m_StartTick(0) + { + if (!m_bEnable) + return; + Initialize(); + } + + //updates external value with current time + double Commit() + { + if (!m_bEnable) + return 0.0; + + if (0.0 != g_Freq) + { + m_start = MSDK_GET_TIME(msdk_time_get_tick(), m_StartTick, g_Freq); + } + return m_start; + } + //last comitted value + double Last() + { + return m_start; + } + ~CTimeInterval() + { + Commit(); + } +private: + void Initialize() + { + if (0.0 == g_Freq) + { + g_Freq = (double)msdk_time_get_frequency(); + } + m_StartTick = msdk_time_get_tick(); + } +}; + +template double CTimeInterval::g_Freq = 0.0f; + +/** Helper class to measure execution time of some code. Use this class + * if you need manual measurements. + * + * Usage example: + * { + * CTimer timer; + * msdk_tick summary_tick; + * + * timer.Start() + * function_to_measure(); + * summary_tick = timer.GetDelta(); + * printf("Elapsed time 1: %f\n", timer.GetTime()); + * ... + * if (condition) timer.Start(); + function_to_measure(); + * if (condition) { + * summary_tick += timer.GetDelta(); + * printf("Elapsed time 2: %f\n", timer.GetTime(); + * } + * printf("Overall time: %f\n", CTimer::ConvertToSeconds(summary_tick); + * } + */ +class CTimer +{ +public: + CTimer(): + start(0) + { + } + static msdk_tick GetFrequency() + { + if (!frequency) frequency = msdk_time_get_frequency(); + return frequency; + } + static mfxF64 ConvertToSeconds(msdk_tick elapsed) + { + return MSDK_GET_TIME(elapsed, 0, GetFrequency()); + } + + inline void Start() + { + start = msdk_time_get_tick(); + } + inline msdk_tick GetDelta() + { + return msdk_time_get_tick() - start; + } + inline mfxF64 GetTime() + { + return MSDK_GET_TIME(msdk_time_get_tick(), start, GetFrequency()); + } + +protected: + static msdk_tick frequency; + msdk_tick start; +private: + CTimer(const CTimer&); + void operator=(const CTimer&); +}; + +/** Helper class to measure overall execution time of some code. Use this + * class if you want to measure execution time of the repeatedly executed + * code. + * + * Usage example 1: + * + * msdk_tick summary_tick = 0; + * + * void function() { + * + * { + * CAutoTimer timer(&summary_tick); + * ... + * } + * ... + * int main() { + * for (;condition;) { + * function(); + * } + * printf("Elapsed time: %f\n", CTimer::ConvertToSeconds(summary_tick); + * return 0; + * } + * + * Usage example 2: + * { + * msdk_tick summary_tick = 0; + * + * { + * CAutoTimer timer(&summary_tick); + * + * for (;condition;) { + * ... + * { + * function_to_measure(); + * timer.Sync(); + * printf("Progress: %f\n", CTimer::ConvertToSeconds(summary_tick); + * } + * ... + * } + * } + * printf("Elapsed time: %f\n", CTimer::ConvertToSeconds(summary_tick); + * } + * + */ +class CAutoTimer +{ +public: + CAutoTimer(msdk_tick& _elapsed): + elapsed(_elapsed), + start(0) + { + elapsed = _elapsed; + start = msdk_time_get_tick(); + } + ~CAutoTimer() + { + elapsed += msdk_time_get_tick() - start; + } + msdk_tick Sync() + { + msdk_tick cur = msdk_time_get_tick(); + elapsed += cur - start; + start = cur; + return elapsed; + } +protected: + msdk_tick& elapsed; + msdk_tick start; +private: + CAutoTimer(const CAutoTimer&); + void operator=(const CAutoTimer&); +}; + +mfxStatus ConvertFrameRate(mfxF64 dFrameRate, mfxU32* pnFrameRateExtN, mfxU32* pnFrameRateExtD); +mfxF64 CalculateFrameRate(mfxU32 nFrameRateExtN, mfxU32 nFrameRateExtD); +mfxU16 GetFreeSurfaceIndex(mfxFrameSurface1* pSurfacesPool, mfxU16 nPoolSize); +mfxU16 GetFreeSurface(mfxFrameSurface1* pSurfacesPool, mfxU16 nPoolSize); +mfxStatus InitMfxBitstream(mfxBitstream* pBitstream, mfxU32 nSize); + +//performs copy to end if possible, also move data to buffer begin if necessary +//shifts offset pointer in source bitstream in success case +mfxStatus MoveMfxBitstream(mfxBitstream *pTarget, mfxBitstream *pSrc, mfxU32 nBytesToCopy); +mfxStatus ExtendMfxBitstream(mfxBitstream* pBitstream, mfxU32 nSize); +void WipeMfxBitstream(mfxBitstream* pBitstream); + +mfxU16 CalculateDefaultBitrate(mfxU32 nCodecId, mfxU32 nTargetUsage, mfxU32 nWidth, mfxU32 nHeight, mfxF64 dFrameRate); + +//serialization fnc set +std::basic_string CodecIdToStr(mfxU32 nFourCC); +mfxU16 StrToTargetUsage(msdk_string strInput); +const msdk_char* TargetUsageToStr(mfxU16 tu); +const msdk_char* ColorFormatToStr(mfxU32 format); +const msdk_char* MfxStatusToStr(mfxStatus sts); + + +// sets bitstream->PicStruct parsing first APP0 marker in bitstream +mfxStatus MJPEG_AVI_ParsePicStruct(mfxBitstream *bitstream); + +// For MVC encoding/decoding purposes +std::basic_string FormMVCFileName(const msdk_char *strFileName, const mfxU32 numView); + +//piecewise linear function for bitrate approximation +class PartiallyLinearFNC +{ + mfxF64 *m_pX; + mfxF64 *m_pY; + mfxU32 m_nPoints; + mfxU32 m_nAllocated; + +public: + PartiallyLinearFNC(); + ~PartiallyLinearFNC(); + + void AddPair(mfxF64 x, mfxF64 y); + mfxF64 at(mfxF64); +private: + DISALLOW_COPY_AND_ASSIGN(PartiallyLinearFNC); +}; + +// function for conversion of display aspect ratio to pixel aspect ratio +mfxStatus DARtoPAR(mfxU32 darw, mfxU32 darh, mfxU32 w, mfxU32 h, mfxU16 *pparw, mfxU16 *pparh); + +// function for getting a pointer to a specific external buffer from the array +mfxExtBuffer* GetExtBuffer(mfxExtBuffer** ebuffers, mfxU32 nbuffers, mfxU32 BufferId); + +//declare used extended buffers +template +struct mfx_ext_buffer_id{ + enum {id = 0}; +}; +template<>struct mfx_ext_buffer_id{ + enum {id = MFX_EXTBUFF_CODING_OPTION}; +}; +template<>struct mfx_ext_buffer_id{ + enum {id = MFX_EXTBUFF_CODING_OPTION2}; +}; +template<>struct mfx_ext_buffer_id{ + enum {id = MFX_EXTBUFF_CODING_OPTION3}; +}; +template<>struct mfx_ext_buffer_id{ + enum {id = MFX_EXTBUFF_AVC_TEMPORAL_LAYERS}; +}; +template<>struct mfx_ext_buffer_id{ + enum {id = MFX_EXTBUFF_AVC_REFLIST_CTRL}; +}; +template<>struct mfx_ext_buffer_id{ + enum {id = MFX_EXTBUFF_THREADS_PARAM}; +}; + + +//helper function to initialize mfx ext buffer structure +template +void init_ext_buffer(T & ext_buffer) +{ + memset(&ext_buffer, 0, sizeof(ext_buffer)); + reinterpret_cast(&ext_buffer)->BufferId = mfx_ext_buffer_id::id; + reinterpret_cast(&ext_buffer)->BufferSz = sizeof(ext_buffer); +} + +// returns false if buf length is insufficient, otherwise +// skips step bytes in buf with specified length and returns true +template +bool skip(const Buf_t *&buf, Length_t &length, Length_t step) +{ + if (length < step) + return false; + + buf += step; + length -= step; + + return true; +} + +//do not link MediaSDK dispatched if class not used +struct MSDKAdapter { + // returns the number of adapter associated with MSDK session, 0 for SW session + static mfxU32 GetNumber(mfxSession session, mfxIMPL implVia = 0) { + mfxU32 adapterNum = 0; // default + mfxIMPL impl = MFX_IMPL_SOFTWARE; // default in case no HW IMPL is found + + // we don't care for error codes in further code; if something goes wrong we fall back to the default adapter + if (session) + { + MFXQueryIMPL(session, &impl); + } + else + { + // an auxiliary session, internal for this function + mfxSession auxSession; + memset(&auxSession, 0, sizeof(auxSession)); + + mfxVersion ver = { {1, 1 }}; // minimum API version which supports multiple devices + MFXInit(MFX_IMPL_HARDWARE_ANY | implVia, &ver, &auxSession); + MFXQueryIMPL(auxSession, &impl); + MFXClose(auxSession); + } + + // extract the base implementation type + mfxIMPL baseImpl = MFX_IMPL_BASETYPE(impl); + + const struct + { + // actual implementation + mfxIMPL impl; + // adapter's number + mfxU32 adapterID; + + } implTypes[] = { + {MFX_IMPL_HARDWARE, 0}, + {MFX_IMPL_SOFTWARE, 0}, + {MFX_IMPL_HARDWARE2, 1}, + {MFX_IMPL_HARDWARE3, 2}, + {MFX_IMPL_HARDWARE4, 3} + }; + + + // get corresponding adapter number + for (mfxU8 i = 0; i < sizeof(implTypes)/sizeof(*implTypes); i++) + { + if (implTypes[i].impl == baseImpl) + { + adapterNum = implTypes[i].adapterID; + break; + } + } + + return adapterNum; + } +}; + +struct APIChangeFeatures { + bool JpegDecode; + bool JpegEncode; + bool MVCDecode; + bool MVCEncode; + bool IntraRefresh; + bool LowLatency; + bool ViewOutput; + bool LookAheadBRC; + bool AudioDecode; + bool SupportCodecPluginAPI; +}; + +mfxVersion getMinimalRequiredVersion(const APIChangeFeatures &features); + +enum msdkAPIFeature { + MSDK_FEATURE_NONE, + MSDK_FEATURE_MVC, + MSDK_FEATURE_JPEG_DECODE, + MSDK_FEATURE_LOW_LATENCY, + MSDK_FEATURE_MVC_VIEWOUTPUT, + MSDK_FEATURE_JPEG_ENCODE, + MSDK_FEATURE_LOOK_AHEAD, + MSDK_FEATURE_PLUGIN_API +}; + +/* Returns true if feature is supported in the given API version */ +bool CheckVersion(mfxVersion* version, msdkAPIFeature feature); + +void ConfigureAspectRatioConversion(mfxInfoVPP* pVppInfo); + +void SEICalcSizeType(std::vector& data, mfxU16 type, mfxU32 size); + +mfxU8 Char2Hex(msdk_char ch); + +enum MsdkTraceLevel { + MSDK_TRACE_LEVEL_SILENT = -1, + MSDK_TRACE_LEVEL_CRITICAL = 0, + MSDK_TRACE_LEVEL_ERROR = 1, + MSDK_TRACE_LEVEL_WARNING = 2, + MSDK_TRACE_LEVEL_INFO = 3, + MSDK_TRACE_LEVEL_DEBUG = 4, +}; + +msdk_string NoFullPath(const msdk_string &); +int msdk_trace_get_level(); +void msdk_trace_set_level(int); +bool msdk_trace_is_printable(int); + +msdk_ostream & operator <<(msdk_ostream & os, MsdkTraceLevel tt); + +template + mfxStatus msdk_opt_read(const msdk_char* string, T& value); + +template + mfxStatus msdk_opt_read(const msdk_char* string, msdk_char (&value)[S]) + { + value[0]=0; + if (strlen(string) < S) { + strncpy(value, string, S-1); + value[S - 1] = 0; + return MFX_ERR_NONE; + } + return MFX_ERR_UNKNOWN; + } + +template + inline mfxStatus msdk_opt_read(const msdk_string& string, T& value) + { + return msdk_opt_read(string.c_str(), value); + } + +mfxStatus StrFormatToCodecFormatFourCC(msdk_char* strInput, mfxU32 &codecFormat); +msdk_string StatusToString(mfxStatus sts); +mfxI32 getMonitorType(msdk_char* str); + +void WaitForDeviceToBecomeFree(MFXVideoSession& session, mfxSyncPoint& syncPoint,mfxStatus& currentStatus); + +mfxU16 FourCCToChroma(mfxU32 fourCC); + +// class is used as custom exception +class mfxError +{ +public: + mfxError(mfxStatus status = MFX_ERR_UNKNOWN, std::string msg = "") + : m_Status(status), + m_msg(msg) + { + } + + virtual ~mfxError() + { + } + + mfxStatus GetStatus() const + { + return m_Status; + } + + std::string GetMessage() const + { + return m_msg; + } + +private: + mfxStatus m_Status; + std::string m_msg; +}; + +#ifdef ENABLE_MCTF +// this function implements a simple management of MCTF control-buffers that can be attached to pmfxSurface +// internally it tracks all created buffers, if s buffer with MFX_EXTBUFF_MCTF_CONTROL header already exists +// and attached to the pmfxSurface, it is cleared (set to zero) and returned to a caller; +// if nothing with MFX_EXTBUFF_MCTF_CONTROL is attached yet, a new buffer is created and stored in internal +// list, then cleaned and a pointer is returned to a caller; +// finally, if DeallocateAll is true, internal pool is traverserd and for each entry delete is called; +// Alternative is to implement own allocator which for internal list which will clean everything; +// not implemented yet for simpicity reasons. +template +//mfxExtMctfControl* GetMctfParamBuffer(mfxFrameSurface1* pmfxSurface, bool DeallocateAll = false) +ParamT * GetMctfParamBuffer(mfxFrameSurface1* pmfxSurface, bool DeallocateAll = false) +{ + // map + static std::map mfxFrameParamPool; + static MSDKMutex ExclusiveAccess; + typedef typename std::map::iterator map_iter; + + + AutomaticMutex guard(ExclusiveAccess); + if (!pmfxSurface && !DeallocateAll) + return NULL; + if (DeallocateAll) + { + for (map_iter it = mfxFrameParamPool.begin(); it != mfxFrameParamPool.end(); ++it) + { + if (it->first) + delete (it->first); + }; + mfxFrameParamPool.clear(); + return NULL; + }; + if (!pmfxSurface->Data.ExtParam) + { + msdk_printf(MSDK_STRING("GetMctfParamBuffer: pmfxSurface->Data.ExtParam is null!\n")); + return NULL; + } + + mfxExtBuffer* pBuf = GetExtBuffer(pmfxSurface->Data.ExtParam, pmfxSurface->Data.NumExtParam, ParamName); + // try to find; if exist, set not-busy; otherwise, insert as a new + if (pBuf) + { + map_iter it = mfxFrameParamPool.find(reinterpret_cast(pBuf)); + if (it != mfxFrameParamPool.end()) + it->second = false; + else + mfxFrameParamPool.insert(std::make_pair(reinterpret_cast(pBuf), false)); + } + else + { + ParamT* pBuf1(NULL); + try + { + pBuf1 = new ParamT; + mfxFrameParamPool.insert(std::make_pair(pBuf1, false)); + } + catch (const std::exception& ) + { + msdk_printf(MSDK_STRING("GetMctfParamBuffer: error (exception) when creating a new ext. buffer & inserting.\n")); + if (pBuf1) + delete pBuf1; + return NULL; + } + } + + // now try to find non-busy buffer: + ParamT* pControl = NULL; + for (map_iter it = mfxFrameParamPool.begin(); it != mfxFrameParamPool.end(); ++it) + { + if (!it->second) + { + it->second = true; + pControl = it->first; + break; + } + } + + if (!pControl) + { + msdk_printf(MSDK_STRING("GetMctfParamBuffer: cannot find an empty buffer & cannot create!\n")); + return NULL; + } + else + { + memset(pControl, 0, sizeof(ParamT)); + pControl->Header.BufferId = ParamName; + pControl->Header.BufferSz = sizeof(ParamT); + return pControl; + }; +}; +inline static void WipeOutExtParams(mfxFrameSurface1* pmfxSurface, bool isInput, mfxU32 size) +{ + if (isInput && pmfxSurface->Data.ExtParam) + { + for (mfxU32 i = 0; i < size; ++i) + pmfxSurface->Data.ExtParam[i] = NULL; + pmfxSurface->Data.NumExtParam = 0; + } +}; +#endif + +#endif //__SAMPLE_UTILS_H__ diff --git a/vshampor/deshuffler/sample_common/include/shared_ptr.h b/vshampor/deshuffler/sample_common/include/shared_ptr.h new file mode 100644 index 0000000..ee52457 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/shared_ptr.h @@ -0,0 +1,20 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once diff --git a/vshampor/deshuffler/sample_common/include/surface_auto_lock.h b/vshampor/deshuffler/sample_common/include/surface_auto_lock.h new file mode 100644 index 0000000..3f49e99 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/surface_auto_lock.h @@ -0,0 +1,69 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once + +#include "mfxstructures.h" +#include "sample_utils.h" + +/* + Rationale: locks allocator if necessary to get RAW pointers, unlock it at the end +*/ +class SurfaceAutoLock : private no_copy +{ +public: + SurfaceAutoLock(mfxFrameAllocator & alloc, mfxFrameSurface1 &srf) + : m_alloc(alloc) , m_srf(srf), m_lockRes(MFX_ERR_NONE), m_bLocked() { + LockFrame(); + } + operator mfxStatus () { + return m_lockRes; + } + ~SurfaceAutoLock() { + UnlockFrame(); + } + +protected: + + mfxFrameAllocator & m_alloc; + mfxFrameSurface1 & m_srf; + mfxStatus m_lockRes; + bool m_bLocked; + + void LockFrame() + { + //no allocator used, no need to do lock + if (m_srf.Data.Y != 0) + return ; + //lock required + m_lockRes = m_alloc.Lock(m_alloc.pthis, m_srf.Data.MemId, &m_srf.Data); + if (m_lockRes == MFX_ERR_NONE) { + m_bLocked = true; + } + } + + void UnlockFrame() + { + if (m_lockRes != MFX_ERR_NONE || !m_bLocked) { + return; + } + //unlock required + m_alloc.Unlock(m_alloc.pthis, m_srf.Data.MemId, &m_srf.Data); + } +}; \ No newline at end of file diff --git a/vshampor/deshuffler/sample_common/include/sysmem_allocator.h b/vshampor/deshuffler/sample_common/include/sysmem_allocator.h new file mode 100644 index 0000000..9f6cff9 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/sysmem_allocator.h @@ -0,0 +1,78 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __SYSMEM_ALLOCATOR_H__ +#define __SYSMEM_ALLOCATOR_H__ + +#include +#include "base_allocator.h" + +struct sBuffer +{ + mfxU32 id; + mfxU32 nbytes; + mfxU16 type; +}; + +struct sFrame +{ + mfxU32 id; + mfxFrameInfo info; +}; + +struct SysMemAllocatorParams : mfxAllocatorParams +{ + SysMemAllocatorParams() + : mfxAllocatorParams(), pBufferAllocator(NULL) { } + MFXBufferAllocator *pBufferAllocator; +}; + +class SysMemFrameAllocator: public BaseFrameAllocator +{ +public: + SysMemFrameAllocator(); + virtual ~SysMemFrameAllocator(); + + virtual mfxStatus Init(mfxAllocatorParams *pParams); + virtual mfxStatus Close(); + virtual mfxStatus LockFrame(mfxMemId mid, mfxFrameData *ptr); + virtual mfxStatus UnlockFrame(mfxMemId mid, mfxFrameData *ptr); + virtual mfxStatus GetFrameHDL(mfxMemId mid, mfxHDL *handle); + +protected: + virtual mfxStatus CheckRequestType(mfxFrameAllocRequest *request); + virtual mfxStatus ReleaseResponse(mfxFrameAllocResponse *response); + virtual mfxStatus AllocImpl(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response); + + MFXBufferAllocator *m_pBufferAllocator; + bool m_bOwnBufferAllocator; +}; + +class SysMemBufferAllocator : public MFXBufferAllocator +{ +public: + SysMemBufferAllocator(); + virtual ~SysMemBufferAllocator(); + virtual mfxStatus AllocBuffer(mfxU32 nbytes, mfxU16 type, mfxMemId *mid); + virtual mfxStatus LockBuffer(mfxMemId mid, mfxU8 **ptr); + virtual mfxStatus UnlockBuffer(mfxMemId mid); + virtual mfxStatus FreeBuffer(mfxMemId mid); +}; + +#endif // __SYSMEM_ALLOCATOR_H__ \ No newline at end of file diff --git a/vshampor/deshuffler/sample_common/include/time_statistics.h b/vshampor/deshuffler/sample_common/include/time_statistics.h new file mode 100644 index 0000000..9a25b16 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/time_statistics.h @@ -0,0 +1,264 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once + +#include "mfxstructures.h" +#include "vm/time_defs.h" +#include "vm/strings_defs.h" +#include "math.h" +#include +#include + +#pragma warning(disable:4100) + +class CTimeStatisticsReal +{ +public: + CTimeStatisticsReal() + { + ResetStatistics(); + start=0; + m_bNeedDumping = false; + } + + static msdk_tick GetFrequency() + { + if (!frequency) + { + frequency = msdk_time_get_frequency(); + } + return frequency; + } + + static mfxF64 ConvertToSeconds(msdk_tick elapsed) + { + return MSDK_GET_TIME(elapsed, 0, GetFrequency()); + } + + inline void StartTimeMeasurement() + { + start = msdk_time_get_tick(); + } + + inline void StopTimeMeasurement() + { + mfxF64 delta=GetDeltaTime(); + totalTime+=delta; + totalTimeSquares+=delta*delta; + // dump in ms: + if(m_bNeedDumping) + m_time_deltas.push_back(delta * 1000); + + if(deltamaxTime) + { + maxTime=delta; + } + numMeasurements++; + } + + inline void StopTimeMeasurementWithCheck() + { + if(start) + { + StopTimeMeasurement(); + } + } + + inline mfxF64 GetDeltaTime() + { + return MSDK_GET_TIME(msdk_time_get_tick(), start, GetFrequency()); + } + + inline mfxF64 GetDeltaTimeInMiliSeconds() + { + return GetDeltaTime() * 1000; + } + + inline void TurnOnDumping(){m_bNeedDumping = true; } + + inline void TurnOffDumping(){m_bNeedDumping = false; } + + inline void PrintStatistics(const msdk_char* prefix) + { + msdk_printf(MSDK_STRING("%s Total:%.3lfms(%lld smpls),Avg %.3lfms,StdDev:%.3lfms,Min:%.3lfms,Max:%.3lfms\n"), + prefix,totalTime,numMeasurements, + GetAvgTime(false),GetTimeStdDev(false), + GetMinTime(false),GetMaxTime(false)); + } + + inline mfxU64 GetNumMeasurements() + { + return numMeasurements; + } + + inline mfxF64 GetAvgTime(bool inSeconds=true) + { + if (inSeconds) + { + return (numMeasurements ? totalTime / numMeasurements : 0); + } + else + { + return (numMeasurements ? totalTime / numMeasurements : 0) * 1000; + } + } + + inline mfxF64 GetTimeStdDev(bool inSeconds=true) + { + mfxF64 avg = GetAvgTime(); + mfxF64 ftmp = (numMeasurements ? sqrt(totalTimeSquares/numMeasurements-avg*avg) : 0.0); + return inSeconds ? ftmp : ftmp * 1000; + } + + inline mfxF64 GetMinTime(bool inSeconds=true) + { + return inSeconds ? minTime : minTime * 1000; + } + + inline mfxF64 GetMaxTime(bool inSeconds=true) + { + return inSeconds ? maxTime : maxTime * 1000; + } + + inline mfxF64 GetTotalTime(bool inSeconds=true) + { + return inSeconds ? totalTime : totalTime * 1000; + } + + inline void ResetStatistics() + { + totalTime=0; + totalTimeSquares=0; + minTime=1E100; + maxTime=-1; + numMeasurements=0; + m_time_deltas.clear(); + TurnOffDumping(); + } + +protected: + static msdk_tick frequency; + + msdk_tick start; + mfxF64 totalTime; + mfxF64 totalTimeSquares; + mfxF64 minTime; + mfxF64 maxTime; + mfxU64 numMeasurements; + std::vector m_time_deltas; + bool m_bNeedDumping; + +}; + +class CTimeStatisticsDummy +{ +public: + static msdk_tick GetFrequency() + { + if (!frequency) + { + frequency = msdk_time_get_frequency(); + } + return frequency; + } + + static mfxF64 ConvertToSeconds(msdk_tick elapsed) + { + return 0; + } + + inline void StartTimeMeasurement() + { + } + + inline void StopTimeMeasurement() + { + } + + inline void StopTimeMeasurementWithCheck() + { + } + + inline mfxF64 GetDeltaTime() + { + return 0; + } + + inline mfxF64 GetDeltaTimeInMiliSeconds() + { + return 0; + } + + inline void TurnOnDumping() {} + + inline void TurnOffDumping() {} + + inline void PrintStatistics(const msdk_char* prefix) + { + } + + inline mfxU64 GetNumMeasurements() + { + return 0; + } + + inline mfxF64 GetAvgTime(bool) + { + return 0; + } + + inline mfxF64 GetTimeStdDev(bool) + { + return 0; + } + + inline mfxF64 GetMinTime(bool) + { + return 0; + } + + inline mfxF64 GetMaxTime(bool) + { + return 0; + } + + inline mfxF64 GetTotalTime(bool) + { + return 0; + } + + inline void ResetStatistics() + { + } + +protected: + static msdk_tick frequency; +}; + +#ifdef TIME_STATS + typedef CTimeStatisticsReal CTimeStatistics; +#else + typedef CTimeStatisticsDummy CTimeStatistics; +#endif diff --git a/vshampor/deshuffler/sample_common/include/v4l2_util.h b/vshampor/deshuffler/sample_common/include/v4l2_util.h new file mode 100644 index 0000000..cf9a8b0 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/v4l2_util.h @@ -0,0 +1,120 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __V4L2_UTIL_H__ +#define __V4L2_UTIL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "sample_defs.h" + +/* MIPI DRIVER Configurations*/ +#define _ISP_MODE_NONE 0x0000 +#define _ISP_MODE_CONTINUOUS 0x1000 +#define _ISP_MODE_STILL 0x2000 +#define _ISP_MODE_VIDEO 0x4000 +#define _ISP_MODE_PREVIEW 0x8000 + +#define CLEAR(x) memset(&(x), 0, sizeof(x)) +#define ERRSTR strerror(errno) + +#define BYE_ON(cond, ...) \ + do { \ + if (cond) { \ + int errsv = errno; \ + fprintf(stderr, "ERROR(%s:%d) : ", \ + __FILE__, __LINE__); \ + errno = errsv; \ + fprintf(stderr, __VA_ARGS__); \ + abort(); \ + } \ + } while(0) + +enum AtomISPMode +{ + NONE = 0, PREVIEW, STILL, VIDEO, CONTINUOUS +}; + +enum V4L2PixelFormat +{ + NO_FORMAT = 0, UYVY, YUY2 +}; + +typedef struct _Buffer +{ + int fd, index; +} Buffer; + +extern Buffer *buffers; +void *PollingThread(void *data); + +class v4l2Device +{ +public: + v4l2Device(const char *devname = "/dev/video0", + uint32_t width = 1920, + uint32_t height = 1080, + uint32_t num_buffer = 4, + enum AtomISPMode MipiMode = NONE, + enum V4L2PixelFormat m_v4l2Format = NO_FORMAT); + + ~v4l2Device(); + + void Init(const char *devname, + uint32_t width, + uint32_t height, + uint32_t num_buffer, + enum V4L2PixelFormat v4l2Format, + enum AtomISPMode MipiMode, + int m_MipiPort); + + void V4L2Init(); + void V4L2Alloc(); + int blockIOCTL(int handle, int request, void *args); + int GetAtomISPModes(enum AtomISPMode mode); + void V4L2QueueBuffer(Buffer *buffer); + Buffer *V4L2DeQueueBuffer(Buffer *buffer); + void V4L2StartCapture(); + void V4L2StopCapture(); + int GetV4L2TerminationSignal(); + void PutOnQ(int x); + int GetOffQ(); + int ConvertToMFXFourCC(enum V4L2PixelFormat v4l2Format); + int ConvertToV4L2FourCC(); + int GetV4L2DisplayID() { return m_fd; } + bool ISV4L2Enabled() { return (m_fd > 0)? true : false; } + +protected: + const char *m_devname; + uint32_t m_height; + uint32_t m_width; + uint32_t m_num_buffers; + struct v4l2_pix_format m_format; + int m_MipiPort; + enum AtomISPMode m_MipiMode; + enum V4L2PixelFormat m_v4l2Format; + int m_fd; +}; + +#endif // ifdef __V4L2_UTIL_H__ diff --git a/vshampor/deshuffler/sample_common/include/vaapi_allocator.h b/vshampor/deshuffler/sample_common/include/vaapi_allocator.h new file mode 100644 index 0000000..48a75f2 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vaapi_allocator.h @@ -0,0 +1,110 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __VAAPI_ALLOCATOR_H__ +#define __VAAPI_ALLOCATOR_H__ + +#if defined(LIBVA_SUPPORT) + +#include +#include +#include + +#include "base_allocator.h" +#include "vaapi_utils.h" + +// VAAPI Allocator internal Mem ID +struct vaapiMemId +{ + VASurfaceID* m_surface; + VAImage m_image; + // variables for VAAPI Allocator internal color conversion + unsigned int m_fourcc; + mfxU8* m_sys_buffer; + mfxU8* m_va_buffer; + // buffer info to support surface export + VABufferInfo m_buffer_info; + // pointer to private export data + void* m_custom; +}; + +namespace MfxLoader +{ + class VA_Proxy; +} + +struct vaapiAllocatorParams : mfxAllocatorParams +{ + enum { + DONOT_EXPORT = 0, + FLINK = 0x01, + PRIME = 0x02, + NATIVE_EXPORT_MASK = FLINK | PRIME, + CUSTOM = 0x100, + CUSTOM_FLINK = CUSTOM | FLINK, + CUSTOM_PRIME = CUSTOM | PRIME + }; + class Exporter + { + public: + virtual ~Exporter(){} + virtual void* acquire(mfxMemId mid) = 0; + virtual void release(mfxMemId mid, void * hdl) = 0; + }; + + vaapiAllocatorParams() + : m_dpy(NULL) + , m_export_mode(DONOT_EXPORT) + , m_exporter(NULL) + {} + + VADisplay m_dpy; + mfxU32 m_export_mode; + Exporter* m_exporter; +}; + +class vaapiFrameAllocator: public BaseFrameAllocator +{ +public: + vaapiFrameAllocator(); + virtual ~vaapiFrameAllocator(); + + virtual mfxStatus Init(mfxAllocatorParams *pParams); + virtual mfxStatus Close(); + +protected: + DISALLOW_COPY_AND_ASSIGN(vaapiFrameAllocator); + + virtual mfxStatus LockFrame(mfxMemId mid, mfxFrameData *ptr); + virtual mfxStatus UnlockFrame(mfxMemId mid, mfxFrameData *ptr); + virtual mfxStatus GetFrameHDL(mfxMemId mid, mfxHDL *handle); + + virtual mfxStatus CheckRequestType(mfxFrameAllocRequest *request); + virtual mfxStatus ReleaseResponse(mfxFrameAllocResponse *response); + virtual mfxStatus AllocImpl(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response); + + VADisplay m_dpy; + MfxLoader::VA_Proxy * m_libva; + mfxU32 m_export_mode; + vaapiAllocatorParams::Exporter* m_exporter; +}; + +#endif //#if defined(LIBVA_SUPPORT) + +#endif // __VAAPI_ALLOCATOR_H__ diff --git a/vshampor/deshuffler/sample_common/include/vaapi_device.h b/vshampor/deshuffler/sample_common/include/vaapi_device.h new file mode 100644 index 0000000..7768fed --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vaapi_device.h @@ -0,0 +1,233 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#if defined(LIBVA_DRM_SUPPORT) || defined(LIBVA_X11_SUPPORT) || defined(LIBVA_ANDROID_SUPPORT) || defined(LIBVA_WAYLAND_SUPPORT) + +#include "hw_device.h" +#include "vaapi_utils_drm.h" +#include "vaapi_utils_x11.h" +#if defined(LIBVA_ANDROID_SUPPORT) +#include "vaapi_utils_android.h" +#endif + +CHWDevice* CreateVAAPIDevice(int type = MFX_LIBVA_DRM); + +#if defined(LIBVA_DRM_SUPPORT) +/** VAAPI DRM implementation. */ +class CVAAPIDeviceDRM : public CHWDevice +{ +public: + CVAAPIDeviceDRM(int type); + virtual ~CVAAPIDeviceDRM(void); + + virtual mfxStatus Init(mfxHDL hWindow, mfxU16 nViews, mfxU32 nAdapterNum); + virtual mfxStatus Reset(void) { return MFX_ERR_NONE; } + virtual void Close(void) { } + + virtual mfxStatus SetHandle(mfxHandleType type, mfxHDL hdl) { return MFX_ERR_UNSUPPORTED; } + virtual mfxStatus GetHandle(mfxHandleType type, mfxHDL *pHdl) + { + if ((MFX_HANDLE_VA_DISPLAY == type) && (NULL != pHdl)) + { + *pHdl = m_DRMLibVA.GetVADisplay(); + + return MFX_ERR_NONE; + } + return MFX_ERR_UNSUPPORTED; + } + + virtual mfxStatus RenderFrame(mfxFrameSurface1 * pSurface, mfxFrameAllocator * pmfxAlloc); + virtual void UpdateTitle(double fps) { } + virtual void SetMondelloInput(bool isMondelloInputEnabled) { } + + inline drmRenderer* getRenderer() { return m_rndr; } +protected: + DRMLibVA m_DRMLibVA; + drmRenderer * m_rndr; +private: + // no copies allowed + CVAAPIDeviceDRM(const CVAAPIDeviceDRM &); + void operator=(const CVAAPIDeviceDRM &); +}; + +#endif + +#if defined(LIBVA_X11_SUPPORT) + +/** VAAPI X11 implementation. */ +class CVAAPIDeviceX11 : public CHWDevice +{ +public: + CVAAPIDeviceX11() + { + m_window = NULL; + m_nRenderWinX=0; + m_nRenderWinY=0; + m_nRenderWinW=0; + m_nRenderWinH=0; + m_bRenderWin=false; +#if defined(X11_DRI3_SUPPORT) + m_dri_fd = 0; + m_dpy = NULL; + m_bufmgr = NULL; + m_xcbconn = NULL; +#endif + } + virtual ~CVAAPIDeviceX11(void); + + virtual mfxStatus Init( + mfxHDL hWindow, + mfxU16 nViews, + mfxU32 nAdapterNum); + virtual mfxStatus Reset(void); + virtual void Close(void); + + virtual mfxStatus SetHandle(mfxHandleType type, mfxHDL hdl); + virtual mfxStatus GetHandle(mfxHandleType type, mfxHDL *pHdl); + + virtual mfxStatus RenderFrame(mfxFrameSurface1 * pSurface, mfxFrameAllocator * pmfxAlloc); + virtual void UpdateTitle(double fps) { } + virtual void SetMondelloInput(bool isMondelloInputEnabled) { } + +protected: + mfxHDL m_window; + X11LibVA m_X11LibVA; +private: + + bool m_bRenderWin; + mfxU32 m_nRenderWinX; + mfxU32 m_nRenderWinY; + mfxU32 m_nRenderWinW; + mfxU32 m_nRenderWinH; +#if defined(X11_DRI3_SUPPORT) + int m_dri_fd; + Display* m_dpy; + drm_intel_bufmgr* m_bufmgr; + xcb_connection_t *m_xcbconn; +#endif + // no copies allowed + CVAAPIDeviceX11(const CVAAPIDeviceX11 &); + void operator=(const CVAAPIDeviceX11 &); +}; + +#endif + +#if defined(LIBVA_WAYLAND_SUPPORT) + +class Wayland; + +#define HANDLE_WAYLAND_DRIVER (MFX_HANDLE_VA_DISPLAY << 4) + +class CVAAPIDeviceWayland : public CHWDevice +{ +public: + CVAAPIDeviceWayland(){ + m_nRenderWinX = 0; + m_nRenderWinY = 0; + m_nRenderWinW = 0; + m_nRenderWinH = 0; + m_isMondelloInputEnabled = false; + m_Wayland = NULL; + } + virtual ~CVAAPIDeviceWayland(void); + + virtual mfxStatus Init(mfxHDL hWindow, mfxU16 nViews, mfxU32 nAdapterNum); + virtual mfxStatus Reset(void) { return MFX_ERR_NONE; } + virtual void Close(void); + + virtual mfxStatus SetHandle(mfxHandleType type, mfxHDL hdl) { return MFX_ERR_UNSUPPORTED; } + virtual mfxStatus GetHandle(mfxHandleType type, mfxHDL *pHdl) + { + if((MFX_HANDLE_VA_DISPLAY == type) && (NULL != pHdl)) { + *pHdl = m_DRMLibVA.GetVADisplay(); + return MFX_ERR_NONE; + } else if((HANDLE_WAYLAND_DRIVER == type) && (NULL != m_Wayland)) { + *pHdl = m_Wayland; + return MFX_ERR_NONE; + } + return MFX_ERR_UNSUPPORTED; + } + virtual mfxStatus RenderFrame(mfxFrameSurface1 * pSurface, mfxFrameAllocator * pmfxAlloc); + virtual void UpdateTitle(double fps) { } + + virtual void SetMondelloInput(bool isMondelloInputEnabled) + { + m_isMondelloInputEnabled = isMondelloInputEnabled; + } + +protected: + DRMLibVA m_DRMLibVA; + MfxLoader::VA_WaylandClientProxy m_WaylandClient; + Wayland *m_Wayland; +private: + mfxU32 m_nRenderWinX; + mfxU32 m_nRenderWinY; + mfxU32 m_nRenderWinW; + mfxU32 m_nRenderWinH; + + bool m_isMondelloInputEnabled; + + // no copies allowed + CVAAPIDeviceWayland(const CVAAPIDeviceWayland &); + void operator=(const CVAAPIDeviceWayland &); +}; + +#endif + +#if defined(LIBVA_ANDROID_SUPPORT) + +/** VAAPI Android implementation. */ +class CVAAPIDeviceAndroid : public CHWDevice +{ +public: + CVAAPIDeviceAndroid(AndroidLibVA *pAndroidLibVA): + m_pAndroidLibVA(pAndroidLibVA) + { + if (!m_pAndroidLibVA) + { + throw std::bad_alloc(); + } + }; + virtual ~CVAAPIDeviceAndroid(void) { Close();} + + virtual mfxStatus Init(mfxHDL hWindow, mfxU16 nViews, mfxU32 nAdapterNum) { return MFX_ERR_NONE;} + virtual mfxStatus Reset(void) { return MFX_ERR_NONE; } + virtual void Close(void) { } + + virtual mfxStatus SetHandle(mfxHandleType type, mfxHDL hdl) { return MFX_ERR_UNSUPPORTED; } + virtual mfxStatus GetHandle(mfxHandleType type, mfxHDL *pHdl) + { + if ((MFX_HANDLE_VA_DISPLAY == type) && (NULL != pHdl)) + { + if(m_pAndroidLibVA)*pHdl = m_pAndroidLibVA->GetVADisplay(); + return MFX_ERR_NONE; + } + + return MFX_ERR_UNSUPPORTED; + } + + virtual mfxStatus RenderFrame(mfxFrameSurface1 * pSurface, mfxFrameAllocator * pmfxAlloc) { return MFX_ERR_NONE; } + virtual void UpdateTitle(double fps) { } + virtual void SetMondelloInput(bool isMondelloInputEnabled) { } + +protected: + AndroidLibVA* m_pAndroidLibVA; +}; +#endif +#endif //#if defined(LIBVA_DRM_SUPPORT) || defined(LIBVA_X11_SUPPORT) || defined(LIBVA_ANDROID_SUPPORT) diff --git a/vshampor/deshuffler/sample_common/include/vaapi_utils.h b/vshampor/deshuffler/sample_common/include/vaapi_utils.h new file mode 100644 index 0000000..31e7c80 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vaapi_utils.h @@ -0,0 +1,474 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __VAAPI_UTILS_H__ +#define __VAAPI_UTILS_H__ + +#ifdef LIBVA_SUPPORT + +#include +#include + +#if defined(LIBVA_DRM_SUPPORT) +#include +#include +#include +#include +#endif +#if defined(LIBVA_X11_SUPPORT) +#include +#if defined(X11_DRI3_SUPPORT) +#include +#include +#endif // X11_DRI3_SUPPORT +#endif +#include "sample_defs.h" +#include "sample_utils.h" +#include "vm/thread_defs.h" + +namespace MfxLoader +{ + + class SimpleLoader + { + public: + SimpleLoader(const char * name); + + void * GetFunction(const char * name); + + ~SimpleLoader(); + + private: + SimpleLoader(SimpleLoader&); + void operator=(SimpleLoader&); + + void * so_handle; + }; + +#ifdef LIBVA_SUPPORT + class VA_Proxy + { + private: + SimpleLoader lib; // should appear first in member list + + public: + typedef VAStatus (*vaInitialize_type)(VADisplay, int *, int *); + typedef VAStatus (*vaTerminate_type)(VADisplay); + typedef VAStatus (*vaCreateSurfaces_type)(VADisplay, unsigned int, + unsigned int, unsigned int, VASurfaceID *, unsigned int, + VASurfaceAttrib *, unsigned int); + typedef VAStatus (*vaDestroySurfaces_type)(VADisplay, VASurfaceID *, int); + typedef VAStatus (*vaCreateBuffer_type)(VADisplay, VAContextID, + VABufferType, unsigned int, unsigned int, void *, VABufferID *); + typedef VAStatus (*vaDestroyBuffer_type)(VADisplay, VABufferID); + typedef VAStatus (*vaMapBuffer_type)(VADisplay, VABufferID, void **pbuf); + typedef VAStatus (*vaUnmapBuffer_type)(VADisplay, VABufferID); + typedef VAStatus (*vaSyncSurface_type)(VADisplay, VASurfaceID); + typedef VAStatus (*vaDeriveImage_type)(VADisplay, VASurfaceID, VAImage *); + typedef VAStatus (*vaDestroyImage_type)(VADisplay, VAImageID); + typedef VAStatus (*vaGetLibFunc_type)(VADisplay, const char *func); + typedef VAStatus (*vaAcquireBufferHandle_type)(VADisplay, VABufferID, VABufferInfo *); + typedef VAStatus (*vaReleaseBufferHandle_type)(VADisplay, VABufferID); + typedef VAStatus (*vaMaxNumEntrypoints_type)(VADisplay dpy); + typedef VAStatus (*vaQueryConfigEntrypoints_type)(VADisplay dpy, + VAProfile profile, + VAEntrypoint *entrypoint_list, + int *num_entrypoints); + typedef VAStatus (*vaGetConfigAttributes_type)(VADisplay dpy, + VAProfile profile, + VAEntrypoint entrypoint, + VAConfigAttrib *attrib_list, + int num_attribs); + typedef VAStatus (*vaCreateConfig_type)(VADisplay dpy, + VAProfile profile, + VAEntrypoint entrypoint, + VAConfigAttrib *attrib_list, + int num_attribs, + VAConfigID *config_id); + + typedef VAStatus (*vaCreateContext_type)(VADisplay dpy, + VAConfigID config_id, + int picture_width, + int picture_height, + int flag, + VASurfaceID *render_targets, + int num_render_targets, + VAContextID *context); + typedef VAStatus (*vaDestroyConfig_type) (VADisplay dpy, + VAConfigID config_id); + typedef VAStatus (*vaDestroyContext_type) (VADisplay dpy, + VAContextID context); + + VA_Proxy(); + ~VA_Proxy(); + + const vaInitialize_type vaInitialize; + const vaTerminate_type vaTerminate; + const vaCreateSurfaces_type vaCreateSurfaces; + const vaDestroySurfaces_type vaDestroySurfaces; + const vaCreateBuffer_type vaCreateBuffer; + const vaDestroyBuffer_type vaDestroyBuffer; + const vaMapBuffer_type vaMapBuffer; + const vaUnmapBuffer_type vaUnmapBuffer; + const vaSyncSurface_type vaSyncSurface; + const vaDeriveImage_type vaDeriveImage; + const vaDestroyImage_type vaDestroyImage; + const vaGetLibFunc_type vaGetLibFunc; + const vaAcquireBufferHandle_type vaAcquireBufferHandle; + const vaReleaseBufferHandle_type vaReleaseBufferHandle; + const vaMaxNumEntrypoints_type vaMaxNumEntrypoints; + const vaQueryConfigEntrypoints_type vaQueryConfigEntrypoints; + const vaGetConfigAttributes_type vaGetConfigAttributes; + const vaCreateConfig_type vaCreateConfig; + const vaCreateContext_type vaCreateContext; + const vaDestroyConfig_type vaDestroyConfig; + const vaDestroyContext_type vaDestroyContext; + }; +#endif + +#if defined(LIBVA_DRM_SUPPORT) + class DRM_Proxy + { + private: + SimpleLoader lib; // should appear first in member list + + public: + typedef int (*drmIoctl_type)(int fd, unsigned long request, void *arg); + typedef int (*drmModeAddFB_type)( + int fd, uint32_t width, uint32_t height, uint8_t depth, + uint8_t bpp, uint32_t pitch, uint32_t bo_handle, + uint32_t *buf_id); + typedef void (*drmModeFreeConnector_type)( drmModeConnectorPtr ptr ); + typedef void (*drmModeFreeCrtc_type)( drmModeCrtcPtr ptr ); + typedef void (*drmModeFreeEncoder_type)( drmModeEncoderPtr ptr ); + typedef void (*drmModeFreePlane_type)( drmModePlanePtr ptr ); + typedef void (*drmModeFreePlaneResources_type)(drmModePlaneResPtr ptr); + typedef void (*drmModeFreeResources_type)( drmModeResPtr ptr ); + typedef drmModeConnectorPtr (*drmModeGetConnector_type)( + int fd, uint32_t connectorId); + typedef drmModeCrtcPtr (*drmModeGetCrtc_type)(int fd, uint32_t crtcId); + typedef drmModeEncoderPtr (*drmModeGetEncoder_type)(int fd, uint32_t encoder_id); + typedef drmModePlanePtr (*drmModeGetPlane_type)(int fd, uint32_t plane_id); + typedef drmModePlaneResPtr (*drmModeGetPlaneResources_type)(int fd); + typedef drmModeResPtr (*drmModeGetResources_type)(int fd); + typedef int (*drmModeRmFB_type)(int fd, uint32_t bufferId); + typedef int (*drmModeSetCrtc_type)( + int fd, uint32_t crtcId, uint32_t bufferId, + uint32_t x, uint32_t y, uint32_t *connectors, int count, + drmModeModeInfoPtr mode); + typedef int (*drmSetMaster_type)(int fd); + typedef int (*drmDropMaster_type)(int fd); + typedef int (*drmModeSetPlane_type)( + int fd, uint32_t plane_id, uint32_t crtc_id, + uint32_t fb_id, uint32_t flags, + int32_t crtc_x, int32_t crtc_y, + uint32_t crtc_w, uint32_t crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); + + DRM_Proxy(); + ~DRM_Proxy(); + +#define __DECLARE(name) const name ## _type name + __DECLARE(drmIoctl); + __DECLARE(drmModeAddFB); + __DECLARE(drmModeFreeConnector); + __DECLARE(drmModeFreeCrtc); + __DECLARE(drmModeFreeEncoder); + __DECLARE(drmModeFreePlane); + __DECLARE(drmModeFreePlaneResources); + __DECLARE(drmModeFreeResources); + __DECLARE(drmModeGetConnector); + __DECLARE(drmModeGetCrtc); + __DECLARE(drmModeGetEncoder); + __DECLARE(drmModeGetPlane); + __DECLARE(drmModeGetPlaneResources); + __DECLARE(drmModeGetResources); + __DECLARE(drmModeRmFB); + __DECLARE(drmModeSetCrtc); + __DECLARE(drmSetMaster); + __DECLARE(drmDropMaster); + __DECLARE(drmModeSetPlane); +#undef __DECLARE + }; + + class DrmIntel_Proxy + { + private: + SimpleLoader lib; // should appear first in member list + + public: + typedef drm_intel_bo* (*drm_intel_bo_gem_create_from_prime_type)( + drm_intel_bufmgr *bufmgr, int prime_fd, int size); + typedef void (*drm_intel_bo_unreference_type)(drm_intel_bo *bo); + typedef drm_intel_bufmgr* (*drm_intel_bufmgr_gem_init_type)(int fd, int batch_size); + typedef int (*drm_intel_bo_gem_export_to_prime_type) (drm_intel_bo *, int *); + typedef void (*drm_intel_bufmgr_destroy_type)(drm_intel_bufmgr*); + + DrmIntel_Proxy(); + ~DrmIntel_Proxy(); + +#define __DECLARE(name) const name ## _type name + __DECLARE(drm_intel_bo_gem_create_from_prime); + __DECLARE(drm_intel_bo_unreference); + __DECLARE(drm_intel_bufmgr_gem_init); + __DECLARE(drm_intel_bufmgr_destroy); +#if defined(X11_DRI3_SUPPORT) + __DECLARE(drm_intel_bo_gem_export_to_prime); +#endif + +#undef __DECLARE + }; + + class VA_DRMProxy + { + private: + SimpleLoader lib; // should appear first in member list + + public: + typedef VADisplay (*vaGetDisplayDRM_type)(int); + + + VA_DRMProxy(); + ~VA_DRMProxy(); + + const vaGetDisplayDRM_type vaGetDisplayDRM; + }; +#endif + +#if defined (LIBVA_WAYLAND_SUPPORT) + + class Wayland; + + class VA_WaylandClientProxy + { + private: + SimpleLoader lib; // should appear first in member list + + public: + typedef Wayland* (*WaylandCreate_type)(void); + + VA_WaylandClientProxy(); + ~VA_WaylandClientProxy(); + + const WaylandCreate_type WaylandCreate; + }; + +#endif // LIBVA_WAYLAND_SUPPORT + +#if defined(LIBVA_X11_SUPPORT) + class VA_X11Proxy + { + private: + SimpleLoader lib; // should appear first in member list + + public: + typedef VADisplay (*vaGetDisplay_type)(Display*); + typedef VAStatus (*vaPutSurface_type)( + VADisplay, VASurfaceID, + Drawable, + short, short, + unsigned short, unsigned short, + short, short, + unsigned short, unsigned short, + VARectangle *, + unsigned int, unsigned int); + + VA_X11Proxy(); + ~VA_X11Proxy(); + + const vaGetDisplay_type vaGetDisplay; + const vaPutSurface_type vaPutSurface; + }; + + class XLib_Proxy + { + private: + SimpleLoader lib; // should appear first in member list + + public: + typedef Display* (*XOpenDisplay_type) (const char*); + typedef int (*XCloseDisplay_type)(Display*); + typedef Window (*XCreateSimpleWindow_type)(Display *, + Window, int, int, + unsigned int, unsigned int, + unsigned int, unsigned long, + unsigned long); + typedef int (*XMapWindow_type)(Display*, Window); + typedef int (*XSync_type)(Display*, Bool); + typedef int (*XDestroyWindow_type)(Display*, Window); + typedef int (*XResizeWindow_type)(Display *, Window, unsigned int, unsigned int); +#if defined(X11_DRI3_SUPPORT) + typedef Status (*XGetGeometry_type)(register Display *, Drawable, Window *, + int *, int *, unsigned int *, unsigned int *, + unsigned int *, unsigned int *); +#endif // X11_DRI3_SUPPORT + XLib_Proxy(); + ~XLib_Proxy(); + + const XOpenDisplay_type XOpenDisplay; + const XCloseDisplay_type XCloseDisplay; + const XCreateSimpleWindow_type XCreateSimpleWindow; + const XMapWindow_type XMapWindow; + const XSync_type XSync; + const XDestroyWindow_type XDestroyWindow; + const XResizeWindow_type XResizeWindow; +#if defined(X11_DRI3_SUPPORT) + const XGetGeometry_type XGetGeometry; +#endif // X11_DRI3_SUPPORT + }; + +#if defined(X11_DRI3_SUPPORT) + + class XCB_Dri3_Proxy + { + private: + SimpleLoader lib; // should appear first in member list + + public: + typedef xcb_void_cookie_t (*xcb_dri3_pixmap_from_buffer_type) (xcb_connection_t *, + xcb_pixmap_t, + xcb_drawable_t, + uint32_t, + uint16_t, + uint16_t, + uint16_t, + uint8_t, + uint8_t, + int32_t); + XCB_Dri3_Proxy(); + ~XCB_Dri3_Proxy(); + + const xcb_dri3_pixmap_from_buffer_type xcb_dri3_pixmap_from_buffer; + }; + + class Xcb_Proxy + { + private: + SimpleLoader lib; // should appear first in member list + + public: + typedef uint32_t (*xcb_generate_id_type) (xcb_connection_t *); + typedef xcb_void_cookie_t (*xcb_free_pixmap_type) (xcb_connection_t *, xcb_pixmap_t); + typedef int (*xcb_flush_type)(xcb_connection_t *); + + Xcb_Proxy(); + ~Xcb_Proxy(); + + const xcb_generate_id_type xcb_generate_id; + const xcb_free_pixmap_type xcb_free_pixmap; + const xcb_flush_type xcb_flush; + }; + + class X11_Xcb_Proxy + { + private: + SimpleLoader lib; // should appear first in member list + + public: + typedef xcb_connection_t * (*XGetXCBConnection_type) (Display *dpy); + + X11_Xcb_Proxy(); + ~X11_Xcb_Proxy(); + + const XGetXCBConnection_type XGetXCBConnection; + }; + + class Xcbpresent_Proxy + { + private: + SimpleLoader lib; + + public: + typedef xcb_void_cookie_t (*xcb_present_pixmap_type) (xcb_connection_t *, + xcb_window_t, + xcb_pixmap_t, + uint32_t, + xcb_xfixes_region_t, + xcb_xfixes_region_t, + int16_t, + int16_t, + xcb_randr_crtc_t, + xcb_sync_fence_t, + xcb_sync_fence_t, + uint32_t, + uint64_t, + uint64_t, + uint64_t, + uint32_t, + const xcb_present_notify_t *); + Xcbpresent_Proxy(); + ~Xcbpresent_Proxy(); + + const xcb_present_pixmap_type xcb_present_pixmap; + }; + +#endif // X11_DRI3_SUPPORT +#endif + +} // namespace MfxLoader + + +class CLibVA +{ +public: + virtual ~CLibVA(void) {}; + + VAStatus AcquireVASurface( + void** pctx, + VADisplay dpy1, + VASurfaceID srf1, + VADisplay dpy2, + VASurfaceID* srf2); + void ReleaseVASurface( + void* actx, + VADisplay dpy1, + VASurfaceID /*srf1*/, + VADisplay dpy2, + VASurfaceID srf2); + + inline int getBackendType() { return m_type; } + VADisplay GetVADisplay(bool render = false) + { return (render && m_va_dpy_render)? m_va_dpy_render: m_va_dpy; } + const MfxLoader::VA_Proxy m_libva; + +protected: + CLibVA(int type) + : m_type(type) + , m_va_dpy(NULL) + , m_va_dpy_render(NULL) + {} + int m_type; + VADisplay m_va_dpy; + VADisplay m_va_dpy_render; + +private: + DISALLOW_COPY_AND_ASSIGN(CLibVA); +}; + +CLibVA* CreateLibVA(int type = MFX_LIBVA_DRM); + +VAStatus AcquireVASurface(void** ctx, VADisplay dpy1, VASurfaceID srf1, VADisplay dpy2, VASurfaceID* srf2); +void ReleaseVASurface(void* actx, VADisplay dpy1, VASurfaceID srf1, VADisplay dpy2, VASurfaceID srf2); + +mfxStatus va_to_mfx_status(VAStatus va_res); + +#endif // #ifdef LIBVA_SUPPORT + +#endif // #ifndef __VAAPI_UTILS_H__ diff --git a/vshampor/deshuffler/sample_common/include/vaapi_utils_android.h b/vshampor/deshuffler/sample_common/include/vaapi_utils_android.h new file mode 100644 index 0000000..ecc3283 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vaapi_utils_android.h @@ -0,0 +1,43 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __VAAPI_UTILS_ANDROID_H__ +#define __VAAPI_UTILS_ANDROID_H__ + +#if defined(LIBVA_ANDROID_SUPPORT) + +#include +#include "vaapi_utils.h" + +class AndroidLibVA : public CLibVA +{ +public: + AndroidLibVA(void); + virtual ~AndroidLibVA(void); + +protected: + void *m_display; + +private: + DISALLOW_COPY_AND_ASSIGN(AndroidLibVA); +}; + +#endif // #if defined(LIBVA_ANDROID_SUPPORT) + +#endif // #ifndef __VAAPI_UTILS_ANDROID_H__ diff --git a/vshampor/deshuffler/sample_common/include/vaapi_utils_drm.h b/vshampor/deshuffler/sample_common/include/vaapi_utils_drm.h new file mode 100644 index 0000000..5e6fc8e --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vaapi_utils_drm.h @@ -0,0 +1,94 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __VAAPI_UTILS_DRM_H__ +#define __VAAPI_UTILS_DRM_H__ + +#if defined(LIBVA_DRM_SUPPORT) + +#include +#include +#include "vaapi_utils.h" +#include "vaapi_allocator.h" + +class drmRenderer; + +class DRMLibVA : public CLibVA +{ +public: + DRMLibVA(int type = MFX_LIBVA_DRM); + virtual ~DRMLibVA(void); + + inline int getFD() { return m_fd; } + +protected: + int m_fd; + MfxLoader::VA_DRMProxy m_vadrmlib; + +private: + DISALLOW_COPY_AND_ASSIGN(DRMLibVA); +}; + +class drmRenderer : public vaapiAllocatorParams::Exporter +{ +public: + drmRenderer(int fd, mfxI32 monitorType); + virtual ~drmRenderer(); + + virtual mfxStatus render(mfxFrameSurface1 * pSurface); + + // vaapiAllocatorParams::Exporter methods + virtual void* acquire(mfxMemId mid); + virtual void release(mfxMemId mid, void * mem); + + static uint32_t getConnectorType(mfxI32 monitor_type); + static const msdk_char* getConnectorName(uint32_t connector_type); + +private: + bool getConnector(drmModeRes *resource, uint32_t connector_type); + bool setupConnection(drmModeRes *resource, drmModeConnector* connector); + bool getPlane(); + + bool setMaster(); + void dropMaster(); + bool restore(); + + const MfxLoader::DRM_Proxy m_drmlib; + const MfxLoader::DrmIntel_Proxy m_drmintellib; + + int m_fd; + uint32_t m_connector_type; + uint32_t m_connectorID; + uint32_t m_encoderID; + uint32_t m_crtcID; + uint32_t m_crtcIndex; + uint32_t m_planeID; + drmModeModeInfo m_mode; + drmModeCrtcPtr m_crtc; + drm_intel_bufmgr* m_bufmgr; + bool m_overlay_wrn; + mfxFrameSurface1 * m_pCurrentRenderTargetSurface; + +private: + DISALLOW_COPY_AND_ASSIGN(drmRenderer); +}; + +#endif // #if defined(LIBVA_DRM_SUPPORT) + +#endif // #ifndef __VAAPI_UTILS_DRM_H__ diff --git a/vshampor/deshuffler/sample_common/include/vaapi_utils_x11.h b/vshampor/deshuffler/sample_common/include/vaapi_utils_x11.h new file mode 100644 index 0000000..93245cf --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vaapi_utils_x11.h @@ -0,0 +1,67 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __VAAPI_UTILS_X11_H__ +#define __VAAPI_UTILS_X11_H__ + +#if defined(LIBVA_X11_SUPPORT) + +#include +#include "vaapi_utils.h" + +class X11LibVA : public CLibVA +{ +public: + X11LibVA(void); + virtual ~X11LibVA(void); + + void *GetXDisplay(void) { return m_display;} + + + MfxLoader::XLib_Proxy & GetX11() { return m_x11lib; } + MfxLoader::VA_X11Proxy & GetVAX11() { return m_vax11lib; } +#if defined(X11_DRI3_SUPPORT) + MfxLoader::Xcb_Proxy & GetXcbX11() { return m_xcblib; } + MfxLoader::X11_Xcb_Proxy & GetX11XcbX11() { return m_x11xcblib; } + MfxLoader::XCB_Dri3_Proxy & GetXCBDri3X11() { return m_xcbdri3lib; } + MfxLoader::Xcbpresent_Proxy & GetXcbpresentX11() { return m_xcbpresentlib; } + MfxLoader::DrmIntel_Proxy & GetDrmIntelX11() { return m_drmintellib; } +#endif // X11_DRI3_SUPPORT + +protected: + Display* m_display; + VAContextID m_contextID; + MfxLoader::XLib_Proxy m_x11lib; + MfxLoader::VA_X11Proxy m_vax11lib; +#if defined(X11_DRI3_SUPPORT) + MfxLoader::VA_DRMProxy m_vadrmlib; + MfxLoader::Xcb_Proxy m_xcblib; + MfxLoader::X11_Xcb_Proxy m_x11xcblib; + MfxLoader::XCB_Dri3_Proxy m_xcbdri3lib; + MfxLoader::Xcbpresent_Proxy m_xcbpresentlib; + MfxLoader::DrmIntel_Proxy m_drmintellib; +#endif // X11_DRI3_SUPPORT + +private: + DISALLOW_COPY_AND_ASSIGN(X11LibVA); +}; + +#endif // #if defined(LIBVA_X11_SUPPORT) + +#endif // #ifndef __VAAPI_UTILS_X11_H__ diff --git a/vshampor/deshuffler/sample_common/include/version.h b/vshampor/deshuffler/sample_common/include/version.h new file mode 100644 index 0000000..483fb44 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/version.h @@ -0,0 +1,45 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#pragma once + +#include "sample_defs.h" + +#ifndef MSDK_MAJOR +#define MSDK_MAJOR 8 +#endif + +#ifndef MSDK_MINOR +#define MSDK_MINOR 3 +#endif + +#ifndef MSDK_RELEASE +#define MSDK_TARGETAPIMINOR 26 +#endif + +#ifndef MSDK_BUILD +#define MSDK_BUILD 0 +#endif + +static msdk_string GetMSDKSampleVersion() +{ + msdk_stringstream ss; + ss << MSDK_MAJOR << "." << MSDK_MINOR << "." << MSDK_TARGETAPIMINOR << "." << MSDK_BUILD; + return ss.str(); +} diff --git a/vshampor/deshuffler/sample_common/include/vm/atomic_defs.h b/vshampor/deshuffler/sample_common/include/vm/atomic_defs.h new file mode 100644 index 0000000..37aed3a --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vm/atomic_defs.h @@ -0,0 +1,37 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __ATOMIC_DEFS_H__ +#define __ATOMIC_DEFS_H__ + +#include "mfxdefs.h" + +/* Thread-safe 16-bit variable incrementing */ +mfxU16 msdk_atomic_inc16(volatile mfxU16 *pVariable); + +/* Thread-safe 16-bit variable decrementing */ +mfxU16 msdk_atomic_dec16(volatile mfxU16 *pVariable); + +/* Thread-safe 32-bit variable incrementing */ +mfxU32 msdk_atomic_inc32(volatile mfxU32 *pVariable); + +/* Thread-safe 32-bit variable decrementing */ +mfxU32 msdk_atomic_dec32(volatile mfxU32 *pVariable); + +#endif // #ifndef __ATOMIC_DEFS_H__ diff --git a/vshampor/deshuffler/sample_common/include/vm/file_defs.h b/vshampor/deshuffler/sample_common/include/vm/file_defs.h new file mode 100644 index 0000000..aa3b801 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vm/file_defs.h @@ -0,0 +1,33 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __FILE_DEFS_H__ +#define __FILE_DEFS_H__ + +#include "mfxdefs.h" + +#include + +#include + +#define MSDK_FOPEN(file, name, mode) !(file = fopen(name, mode)) + +#define msdk_fgets fgets + +#endif // #ifndef __FILE_DEFS_H__ diff --git a/vshampor/deshuffler/sample_common/include/vm/so_defs.h b/vshampor/deshuffler/sample_common/include/vm/so_defs.h new file mode 100644 index 0000000..0ab6228 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vm/so_defs.h @@ -0,0 +1,34 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __SO_DEFS_H__ +#define __SO_DEFS_H__ + +#include "mfxdefs.h" +#include "strings_defs.h" + +/* Declare shared object handle */ +typedef void * msdk_so_handle; +typedef void (*msdk_func_pointer)(void); + +msdk_so_handle msdk_so_load(const msdk_char *file_name); +msdk_func_pointer msdk_so_get_addr(msdk_so_handle handle, const char *func_name); +void msdk_so_free(msdk_so_handle handle); + +#endif // #ifndef __SO_DEFS_H__ diff --git a/vshampor/deshuffler/sample_common/include/vm/strings_defs.h b/vshampor/deshuffler/sample_common/include/vm/strings_defs.h new file mode 100644 index 0000000..65f0bc8 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vm/strings_defs.h @@ -0,0 +1,71 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __STRING_DEFS_H__ +#define __STRING_DEFS_H__ + +#include +#include +#include + +#ifdef __cplusplus +#include +#endif + + +#define MSDK_STRING(x) x +#define MSDK_CHAR(x) x + +#ifdef __cplusplus +typedef std::string msdk_tstring; +#endif +typedef char msdk_char; + +#define msdk_printf printf +#define msdk_sprintf sprintf +#define msdk_vprintf vprintf +#define msdk_fprintf fprintf +#define msdk_strlen strlen +#define msdk_strcmp strcmp +#define msdk_stricmp strcasecmp +#define msdk_strncmp strncmp +#define msdk_strstr strstr +#define msdk_atoi atoi +#define msdk_atoll atoll +#define msdk_strtol strtol +#define msdk_strtod strtod +#define msdk_itoa_decimal(value, str) \ + snprintf(str, sizeof(str)/sizeof(str[0])-1, "%d", value) +#define msdk_strnlen(str,maxlen) strlen(str) +#define msdk_sscanf sscanf + +#define msdk_strcopy strcpy + +#define msdk_strncopy_s(dst, num_dst, src, count) strncpy(dst, src, count) + +#define MSDK_MEMCPY_BITSTREAM(bitstream, offset, src, count) memcpy((bitstream).Data + (offset), (src), (count)) + +#define MSDK_MEMCPY_BUF(bufptr, offset, maxsize, src, count) memcpy((bufptr)+ (offset), (src), (count)) + +#define MSDK_MEMCPY_VAR(dstVarName, src, count) memcpy(&(dstVarName), (src), (count)) + +#define MSDK_MEMCPY(dst, src, count) memcpy(dst, (src), (count)) + + +#endif //__STRING_DEFS_H__ diff --git a/vshampor/deshuffler/sample_common/include/vm/thread_defs.h b/vshampor/deshuffler/sample_common/include/vm/thread_defs.h new file mode 100644 index 0000000..5d2e307 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vm/thread_defs.h @@ -0,0 +1,168 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __THREAD_DEFS_H__ +#define __THREAD_DEFS_H__ + +#include "mfxdefs.h" +#include "vm/strings_defs.h" + +typedef unsigned int (MFX_STDCALL * msdk_thread_callback)(void*); + + +#include +#include +#include +#include + +struct msdkMutexHandle +{ + pthread_mutex_t m_mutex; +}; + +struct msdkSemaphoreHandle +{ + msdkSemaphoreHandle(mfxU32 count): + m_count(count) + {} + + mfxU32 m_count; + pthread_cond_t m_semaphore; + pthread_mutex_t m_mutex; +}; + +struct msdkEventHandle +{ + msdkEventHandle(bool manual, bool state): + m_manual(manual), + m_state(state) + {} + + bool m_manual; + bool m_state; + pthread_cond_t m_event; + pthread_mutex_t m_mutex; +}; + +class MSDKEvent; + +struct msdkThreadHandle +{ + msdkThreadHandle( + msdk_thread_callback func, + void* arg): + m_func(func), + m_arg(arg), + m_event(0), + m_thread(0) + {} + + msdk_thread_callback m_func; + void* m_arg; + MSDKEvent* m_event; + pthread_t m_thread; +}; + + +class MSDKMutex: public msdkMutexHandle +{ +public: + MSDKMutex(void); + ~MSDKMutex(void); + + mfxStatus Lock(void); + mfxStatus Unlock(void); + int Try(void); + +private: + MSDKMutex(const MSDKMutex&); + void operator=(const MSDKMutex&); +}; + +class AutomaticMutex +{ +public: + AutomaticMutex(MSDKMutex& mutex); + ~AutomaticMutex(void); + +private: + mfxStatus Lock(void); + mfxStatus Unlock(void); + + MSDKMutex& m_rMutex; + bool m_bLocked; + +private: + AutomaticMutex(const AutomaticMutex&); + void operator=(const AutomaticMutex&); +}; + +class MSDKSemaphore: public msdkSemaphoreHandle +{ +public: + MSDKSemaphore(mfxStatus &sts, mfxU32 count = 0); + ~MSDKSemaphore(void); + + mfxStatus Post(void); + mfxStatus Wait(void); + +private: + MSDKSemaphore(const MSDKSemaphore&); + void operator=(const MSDKSemaphore&); +}; + +class MSDKEvent: public msdkEventHandle +{ +public: + MSDKEvent(mfxStatus &sts, bool manual, bool state); + ~MSDKEvent(void); + + mfxStatus Signal(void); + mfxStatus Reset(void); + mfxStatus Wait(void); + mfxStatus TimedWait(mfxU32 msec); + +private: + MSDKEvent(const MSDKEvent&); + void operator=(const MSDKEvent&); +}; + +class MSDKThread: public msdkThreadHandle +{ +public: + MSDKThread(mfxStatus &sts, msdk_thread_callback func, void* arg); + ~MSDKThread(void); + + mfxStatus Wait(void); + mfxStatus TimedWait(mfxU32 msec); + mfxStatus GetExitCode(); + + friend void* msdk_thread_start(void* arg); + +private: + MSDKThread(const MSDKThread&); + void operator=(const MSDKThread&); +}; + +mfxU32 msdk_get_current_pid(); +mfxStatus msdk_setrlimit_vmem(mfxU64 size); +mfxStatus msdk_thread_get_schedtype(const msdk_char*, mfxI32 &type); +void msdk_thread_printf_scheduling_help(); + +#endif //__THREAD_DEFS_H__ diff --git a/vshampor/deshuffler/sample_common/include/vm/time_defs.h b/vshampor/deshuffler/sample_common/include/vm/time_defs.h new file mode 100644 index 0000000..6e46ce3 --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vm/time_defs.h @@ -0,0 +1,50 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __TIME_DEFS_H__ +#define __TIME_DEFS_H__ + +#include "mfxdefs.h" +#include "mfx_itt_trace.h" + + +#include + +#define MSDK_SLEEP(msec) \ + do { \ + MFX_ITT_TASK("MSDK_SLEEP"); \ + usleep(1000*msec); \ + } while(0) + +#define MSDK_USLEEP(usec) \ + do { \ + MFX_ITT_TASK("MSDK_USLEEP"); \ + usleep(usec); \ + } while(0) + + +#define MSDK_GET_TIME(T,S,F) ((mfxF64)((T)-(S))/(mfxF64)(F)) + +typedef mfxI64 msdk_tick; + +msdk_tick msdk_time_get_tick(void); +msdk_tick msdk_time_get_frequency(void); +mfxU64 rdtsc(void); + +#endif // #ifndef __TIME_DEFS_H__ diff --git a/vshampor/deshuffler/sample_common/include/vpp_ex.h b/vshampor/deshuffler/sample_common/include/vpp_ex.h new file mode 100644 index 0000000..2d30c9b --- /dev/null +++ b/vshampor/deshuffler/sample_common/include/vpp_ex.h @@ -0,0 +1,59 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifndef __VPP_EX_H__ +#define __VPP_EX_H__ + +#include "sample_utils.h" +#include "mfxvideo++.h" +#include + +/* #define USE_VPP_EX */ + +class MFXVideoVPPEx : public MFXVideoVPP +{ + +public: + MFXVideoVPPEx(mfxSession session); + +#if defined USE_VPP_EX + + mfxStatus QueryIOSurf(mfxVideoParam *par, mfxFrameAllocRequest request[2]); + mfxStatus Query(mfxVideoParam *in, mfxVideoParam *out); + mfxStatus Init(mfxVideoParam *par); + mfxStatus RunFrameVPPAsync(mfxFrameSurface1 *in, mfxFrameSurface1 *out, mfxExtVppAuxData *aux, mfxSyncPoint *syncp); + mfxStatus GetVideoParam(mfxVideoParam *par); + mfxStatus Close(void); + +protected: + + std::vector m_LockedSurfacesList; + mfxVideoParam m_VideoParams; + + mfxU64 m_nCurrentPTS; + + mfxU64 m_nIncreaseTime; + mfxU64 m_nArraySize; + mfxU64 m_nInputTimeStamp; + +#endif + +}; + +#endif //__VPP_EX_H__ diff --git a/vshampor/deshuffler/sample_common/lib/libsample_common.a b/vshampor/deshuffler/sample_common/lib/libsample_common.a new file mode 100644 index 0000000000000000000000000000000000000000..e2fc2e93e019a9f1277717b3d18c097f188a9835 GIT binary patch literal 879306 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 h4*nKQe1T524#xhz=O5m_@LPBi>^wc`NW5 0 && (nbits) <= 32); \ + SAMPLE_ASSERT(offset >= 0 && offset <= 31); \ + /* decrease number of available bits */ \ + offset -= (nbits); \ + /* normalize bitstream pointer */ \ + if (0 > offset) \ + { \ + offset += 32; \ + current_data++; \ + } \ + /* check error(s) again */ \ + SAMPLE_ASSERT(offset >= 0 && offset <= 31); \ + } + +#define avcGetBits8( current_data, offset, data) \ + _avcGetBits(current_data, offset, 8, data); + + +#define avcUngetNBits(current_data, offset, nbits) \ +{ \ + SAMPLE_ASSERT(offset >= 0 && offset <= 31); \ + \ + offset += (nbits); \ + if (offset > 31) \ + { \ + offset -= 32; \ + current_data--; \ + } \ + \ + SAMPLE_ASSERT(offset >= 0 && offset <= 31); \ +} + +#define avcUngetBits32(current_data, offset) \ + SAMPLE_ASSERT(offset >= 0 && offset <= 31); \ + current_data--; + +#define avcAlignBSPointerRight(current_data, offset) \ +{ \ + if ((offset & 0x07) != 0x07) \ + { \ + offset = (offset | 0x07) - 8; \ + if (offset == -1) \ + { \ + offset = 31; \ + current_data++; \ + } \ + } \ +} + +#define avcNextBits(current_data, bp, nbits, data) \ +{ \ + mfxU32 x; \ + \ + SAMPLE_ASSERT((nbits) > 0 && (nbits) <= 32); \ + SAMPLE_ASSERT(nbits >= 0 && nbits <= 31); \ + \ + mfxI32 offset = bp - (nbits); \ + \ + if (offset >= 0) \ + { \ + x = current_data[0] >> (offset + 1); \ + } \ + else \ + { \ + offset += 32; \ + \ + x = current_data[1] >> (offset); \ + x >>= 1; \ + x += current_data[0] << (31 - offset); \ + } \ + \ + SAMPLE_ASSERT(offset >= 0 && offset <= 31); \ + \ + (data) = x & bits_data[nbits]; \ +} + +inline void FillFlatScalingList4x4(AVCScalingList4x4 *scl) +{ + for (mfxI32 i=0;i<16;i++) + scl->ScalingListCoeffs[i] = 16; +} + +inline void FillFlatScalingList8x8(AVCScalingList8x8 *scl) +{ + for (mfxI32 i=0;i<64;i++) + scl->ScalingListCoeffs[i] = 16; +} + +inline void FillScalingList4x4(AVCScalingList4x4 *scl_dst,mfxU8 *coefs_src) +{ + for (mfxI32 i=0;i<16;i++) + scl_dst->ScalingListCoeffs[i] = coefs_src[i]; +} + +inline void FillScalingList8x8(AVCScalingList8x8 *scl_dst,mfxU8 *coefs_src) +{ + for (mfxI32 i=0;i<64;i++) + scl_dst->ScalingListCoeffs[i] = coefs_src[i]; +} + +AVCBaseBitstream::AVCBaseBitstream() +{ + Reset(0, 0); +} + +AVCBaseBitstream::AVCBaseBitstream(mfxU8 * const pb, const mfxU32 maxsize) +{ + Reset(pb, maxsize); +} + +AVCBaseBitstream::~AVCBaseBitstream() +{ +} + +void AVCBaseBitstream::Reset(mfxU8 * const pb, const mfxU32 maxsize) +{ + m_pbs = (mfxU32*)pb; + m_pbsBase = (mfxU32*)pb; + m_bitOffset = 31; + m_maxBsSize = maxsize; + +} // void Reset(mfxU8 * const pb, const mfxU32 maxsize) + +void AVCBaseBitstream::Reset(mfxU8 * const pb, mfxI32 offset, const mfxU32 maxsize) +{ + m_pbs = (mfxU32*)pb; + m_pbsBase = (mfxU32*)pb; + m_bitOffset = offset; + m_maxBsSize = maxsize; + +} // void Reset(mfxU8 * const pb, mfxI32 offset, const mfxU32 maxsize) + +mfxStatus AVCBaseBitstream::GetNALUnitType( NAL_Unit_Type &uNALUnitType,mfxU8 &uNALStorageIDC) +{ + mfxU32 code; + avcGetBits8(m_pbs, m_bitOffset, code); + + uNALStorageIDC = (mfxU8)((code & NAL_STORAGE_IDC_BITS)>>5); + uNALUnitType = (NAL_Unit_Type)(code & NAL_UNITTYPE_BITS); + return MFX_ERR_NONE; +} // GetNALUnitType + +mfxI32 AVCBaseBitstream::GetVLCElement(bool bIsSigned) +{ + mfxI32 sval = 0; + + mfxStatus ippRes = DecodeExpGolombOne(&m_pbs, &m_bitOffset, &sval, bIsSigned); + + if (ippRes < MFX_ERR_NONE) + throw AVC_exception(MFX_ERR_UNDEFINED_BEHAVIOR); + + return sval; +} + +void AVCBaseBitstream::AlignPointerRight(void) +{ + avcAlignBSPointerRight(m_pbs, m_bitOffset); + +} // void AVCBitstream::AlignPointerRight(void) + +bool AVCBaseBitstream::More_RBSP_Data() +{ + mfxI32 code, tmp; + mfxU32* ptr_state = m_pbs; + mfxI32 bit_state = m_bitOffset; + + SAMPLE_ASSERT(m_bitOffset >= 0 && m_bitOffset <= 31); + + mfxI32 remaining_bytes = (mfxI32)BytesLeft(); + + if (remaining_bytes <= 0) + return false; + + // get top bit, it can be "rbsp stop" bit + avcGetNBits(m_pbs, m_bitOffset, 1, code); + + // get remain bits, which is less then byte + tmp = (m_bitOffset + 1) % 8; + + if(tmp) + { + avcGetNBits(m_pbs, m_bitOffset, tmp, code); + if ((code << (8 - tmp)) & 0x7f) // most sig bit could be rbsp stop bit + { + m_pbs = ptr_state; + m_bitOffset = bit_state; + // there are more data + return true; + } + } + + remaining_bytes = (mfxI32)BytesLeft(); + + // run through remain bytes + while (0 < remaining_bytes) + { + avcGetBits8(m_pbs, m_bitOffset, code); + + if (code) + { + m_pbs = ptr_state; + m_bitOffset = bit_state; + // there are more data + return true; + } + + remaining_bytes -= 1; + } + + return false; +} + +AVCHeadersBitstream::AVCHeadersBitstream() + : AVCBaseBitstream() +{ +} + +AVCHeadersBitstream::AVCHeadersBitstream(mfxU8 * const pb, const mfxU32 maxsize) + : AVCBaseBitstream(pb, maxsize) +{ +} + +// --------------------------------------------------------------------------- +// AVCBitstream::GetSequenceParamSet() +// Read sequence parameter set data from bitstream. +// --------------------------------------------------------------------------- +mfxStatus AVCHeadersBitstream::GetSequenceParamSet(AVCSeqParamSet *sps) +{ + // Not all members of the seq param set structure are contained in all + // seq param sets. So start by init all to zero. + mfxStatus ps = MFX_ERR_NONE; + sps->Reset(); + + // profile + sps->profile_idc = (mfxU8)GetBits(8); + + switch (sps->profile_idc) + { + case AVC_PROFILE_BASELINE: + case AVC_PROFILE_MAIN: + case AVC_PROFILE_SCALABLE_BASELINE: + case AVC_PROFILE_SCALABLE_HIGH: + case AVC_PROFILE_EXTENDED: + case AVC_PROFILE_HIGH: + case AVC_PROFILE_HIGH10: + case AVC_PROFILE_MULTIVIEW_HIGH: + case AVC_PROFILE_HIGH422: + case AVC_PROFILE_STEREO_HIGH: + case AVC_PROFILE_HIGH444: + case AVC_PROFILE_ADVANCED444_INTRA: + case AVC_PROFILE_ADVANCED444: + break; + default: + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + sps->constrained_set0_flag = (mfxU8)Get1Bit(); + + sps->constrained_set1_flag = (mfxU8)Get1Bit(); + + sps->constrained_set2_flag = (mfxU8)Get1Bit(); + + sps->constrained_set3_flag = (mfxU8)Get1Bit(); + + // skip 4 zero bits + GetBits(4); + + sps->level_idc = (mfxU8)GetBits(8); + + switch(sps->level_idc) + { + case AVC_LEVEL_1: + case AVC_LEVEL_11: + case AVC_LEVEL_12: + case AVC_LEVEL_13: + + case AVC_LEVEL_2: + case AVC_LEVEL_21: + case AVC_LEVEL_22: + + case AVC_LEVEL_3: + case AVC_LEVEL_31: + case AVC_LEVEL_32: + + case AVC_LEVEL_4: + case AVC_LEVEL_41: + case AVC_LEVEL_42: + + case AVC_LEVEL_5: + case AVC_LEVEL_51: + break; + default: + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + // id + mfxI32 sps_id = GetVLCElement(false); + if (sps_id > MAX_NUM_SEQ_PARAM_SETS - 1) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + sps->seq_parameter_set_id = (mfxU8)sps_id; + + // see 7.3.2.1.1 "Sequence parameter set data syntax" + // chapter of H264 standard for full list of profiles with chrominance + if ((AVC_PROFILE_SCALABLE_BASELINE == sps->profile_idc) || + (AVC_PROFILE_SCALABLE_HIGH == sps->profile_idc) || + (AVC_PROFILE_HIGH == sps->profile_idc) || + (AVC_PROFILE_HIGH10 == sps->profile_idc) || + (AVC_PROFILE_MULTIVIEW_HIGH == sps->profile_idc) || + (AVC_PROFILE_HIGH422 == sps->profile_idc) || + (AVC_PROFILE_STEREO_HIGH == sps->profile_idc) || + (244 == sps->profile_idc) || + (44 == sps->profile_idc)) + { + mfxU32 chroma_format_idc = GetVLCElement(false); + + if (chroma_format_idc > 3) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + sps->chroma_format_idc = (mfxU8)chroma_format_idc; + if (sps->chroma_format_idc==3) + { + sps->residual_colour_transform_flag = (mfxU8) Get1Bit(); + } + + mfxU32 bit_depth_luma = GetVLCElement(false) + 8; + mfxU32 bit_depth_chroma = GetVLCElement(false) + 8; + + if (bit_depth_luma > 16 || bit_depth_chroma > 16) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + sps->bit_depth_luma = (mfxU8)bit_depth_luma; + sps->bit_depth_chroma = (mfxU8)bit_depth_chroma; + + if (!chroma_format_idc) + sps->bit_depth_chroma = sps->bit_depth_luma; + + SAMPLE_ASSERT(!sps->residual_colour_transform_flag); + if (sps->residual_colour_transform_flag == 1) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + sps->qpprime_y_zero_transform_bypass_flag = (mfxU8)Get1Bit(); + sps->seq_scaling_matrix_present_flag = (mfxU8)Get1Bit(); + if(sps->seq_scaling_matrix_present_flag) + { + // 0 + if(Get1Bit()) + { + GetScalingList4x4(&sps->ScalingLists4x4[0],(mfxU8*)default_intra_scaling_list4x4,&sps->type_of_scaling_list_used[0]); + } + else + { + FillScalingList4x4(&sps->ScalingLists4x4[0],(mfxU8*) default_intra_scaling_list4x4); + sps->type_of_scaling_list_used[0] = SCLDEFAULT; + } + // 1 + if(Get1Bit()) + { + GetScalingList4x4(&sps->ScalingLists4x4[1],(mfxU8*) default_intra_scaling_list4x4,&sps->type_of_scaling_list_used[1]); + } + else + { + FillScalingList4x4(&sps->ScalingLists4x4[1],(mfxU8*) sps->ScalingLists4x4[0].ScalingListCoeffs); + sps->type_of_scaling_list_used[1] = SCLDEFAULT; + } + // 2 + if(Get1Bit()) + { + GetScalingList4x4(&sps->ScalingLists4x4[2],(mfxU8*) default_intra_scaling_list4x4,&sps->type_of_scaling_list_used[2]); + } + else + { + FillScalingList4x4(&sps->ScalingLists4x4[2],(mfxU8*) sps->ScalingLists4x4[1].ScalingListCoeffs); + sps->type_of_scaling_list_used[2] = SCLDEFAULT; + } + // 3 + if(Get1Bit()) + { + GetScalingList4x4(&sps->ScalingLists4x4[3],(mfxU8*)default_inter_scaling_list4x4,&sps->type_of_scaling_list_used[3]); + } + else + { + FillScalingList4x4(&sps->ScalingLists4x4[3],(mfxU8*) default_inter_scaling_list4x4); + sps->type_of_scaling_list_used[3] = SCLDEFAULT; + } + // 4 + if(Get1Bit()) + { + GetScalingList4x4(&sps->ScalingLists4x4[4],(mfxU8*) default_inter_scaling_list4x4,&sps->type_of_scaling_list_used[4]); + } + else + { + FillScalingList4x4(&sps->ScalingLists4x4[4],(mfxU8*) sps->ScalingLists4x4[3].ScalingListCoeffs); + sps->type_of_scaling_list_used[4] = SCLDEFAULT; + } + // 5 + if(Get1Bit()) + { + GetScalingList4x4(&sps->ScalingLists4x4[5],(mfxU8*) default_inter_scaling_list4x4,&sps->type_of_scaling_list_used[5]); + } + else + { + FillScalingList4x4(&sps->ScalingLists4x4[5],(mfxU8*) sps->ScalingLists4x4[4].ScalingListCoeffs); + sps->type_of_scaling_list_used[5] = SCLDEFAULT; + } + + // 0 + if(Get1Bit()) + { + GetScalingList8x8(&sps->ScalingLists8x8[0],(mfxU8*)default_intra_scaling_list8x8,&sps->type_of_scaling_list_used[6]); + } + else + { + FillScalingList8x8(&sps->ScalingLists8x8[0],(mfxU8*) default_intra_scaling_list8x8); + sps->type_of_scaling_list_used[6] = SCLDEFAULT; + } + // 1 + if(Get1Bit()) + { + GetScalingList8x8(&sps->ScalingLists8x8[1],(mfxU8*) default_inter_scaling_list8x8,&sps->type_of_scaling_list_used[7]); + } + else + { + FillScalingList8x8(&sps->ScalingLists8x8[1],(mfxU8*) default_inter_scaling_list8x8); + sps->type_of_scaling_list_used[7] = SCLDEFAULT; + } + + } + else + { + mfxI32 i; + + for (i = 0; i < 6; i += 1) + { + FillFlatScalingList4x4(&sps->ScalingLists4x4[i]); + } + for (i = 0; i < 2; i += 1) + { + FillFlatScalingList8x8(&sps->ScalingLists8x8[i]); + } + } + } + else + { + sps->chroma_format_idc = 1; + sps->bit_depth_luma = 8; + sps->bit_depth_chroma = 8; + + SetDefaultScalingLists(sps); + } + + // log2 max frame num (bitstream contains value - 4) + mfxU32 log2_max_frame_num = GetVLCElement(false) + 4; + sps->log2_max_frame_num = (mfxU8)log2_max_frame_num; + + if (log2_max_frame_num > 16 || log2_max_frame_num < 4) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + // pic order cnt type (0..2) + mfxU32 pic_order_cnt_type = GetVLCElement(false); + sps->pic_order_cnt_type = (mfxU8)pic_order_cnt_type; + if (pic_order_cnt_type > 2) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + if (sps->pic_order_cnt_type == 0) + { + // log2 max pic order count lsb (bitstream contains value - 4) + mfxU32 log2_max_pic_order_cnt_lsb = GetVLCElement(false) + 4; + sps->log2_max_pic_order_cnt_lsb = (mfxU8)log2_max_pic_order_cnt_lsb; + + if (log2_max_pic_order_cnt_lsb > 16 || log2_max_pic_order_cnt_lsb < 4) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + sps->MaxPicOrderCntLsb = (1 << sps->log2_max_pic_order_cnt_lsb); + } + else if (sps->pic_order_cnt_type == 1) + { + sps->delta_pic_order_always_zero_flag = (mfxU8)Get1Bit(); + sps->offset_for_non_ref_pic = GetVLCElement(true); + sps->offset_for_top_to_bottom_field = GetVLCElement(true); + sps->num_ref_frames_in_pic_order_cnt_cycle = GetVLCElement(false); + + if (sps->num_ref_frames_in_pic_order_cnt_cycle > 255) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + // get offsets + for (mfxU32 i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++) + { + sps->poffset_for_ref_frame[i] = GetVLCElement(true); + } + } // pic order count type 1 + + // num ref frames + sps->num_ref_frames = GetVLCElement(false); + if (sps->num_ref_frames > 16) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + sps->gaps_in_frame_num_value_allowed_flag = (mfxU8)Get1Bit(); + + // picture width in MBs (bitstream contains value - 1) + sps->frame_width_in_mbs = GetVLCElement(false) + 1; + + // picture height in MBs (bitstream contains value - 1) + sps->frame_height_in_mbs = GetVLCElement(false) + 1; + + sps->frame_mbs_only_flag = (mfxU8)Get1Bit(); + sps->frame_height_in_mbs = (2-sps->frame_mbs_only_flag)*sps->frame_height_in_mbs; + if (sps->frame_mbs_only_flag == 0) + { + sps->mb_adaptive_frame_field_flag = (mfxU8)Get1Bit(); + } + sps->direct_8x8_inference_flag = (mfxU8)Get1Bit(); + if (sps->frame_mbs_only_flag==0) + { + sps->direct_8x8_inference_flag = 1; + } + sps->frame_cropping_flag = (mfxU8)Get1Bit(); + + if (sps->frame_cropping_flag) + { + sps->frame_cropping_rect_left_offset = GetVLCElement(false); + sps->frame_cropping_rect_right_offset = GetVLCElement(false); + sps->frame_cropping_rect_top_offset = GetVLCElement(false); + sps->frame_cropping_rect_bottom_offset = GetVLCElement(false); + } // don't need else because we zeroid structure + + sps->vui_parameters_present_flag = (mfxU8)Get1Bit(); + if (sps->vui_parameters_present_flag) + { + if (ps == MFX_ERR_NONE) + ps = GetVUIParam(sps); + } + + return ps; +} // GetSequenceParamSet + +mfxStatus AVCHeadersBitstream::GetVUIParam(AVCSeqParamSet *sps) +{ + mfxStatus ps=MFX_ERR_NONE; + sps->aspect_ratio_info_present_flag = (mfxU8) Get1Bit(); + + sps->sar_width = 1; // default values + sps->sar_height = 1; + + if (sps->aspect_ratio_info_present_flag) + { + sps->aspect_ratio_idc = (mfxU8) GetBits(8); + if (sps->aspect_ratio_idc == 255) { + sps->sar_width = (mfxU16) GetBits(16); + sps->sar_height = (mfxU16) GetBits(16); + } + } + + sps->overscan_info_present_flag = (mfxU8) Get1Bit(); + if( sps->overscan_info_present_flag ) + sps->overscan_appropriate_flag = (mfxU8) Get1Bit(); + sps->video_signal_type_present_flag = (mfxU8) Get1Bit(); + if( sps->video_signal_type_present_flag ) { + sps->video_format = (mfxU8) GetBits(3); + sps->video_full_range_flag = (mfxU8) Get1Bit(); + sps->colour_description_present_flag = (mfxU8) Get1Bit(); + if( sps->colour_description_present_flag ) { + sps->colour_primaries = (mfxU8) GetBits(8); + sps->transfer_characteristics = (mfxU8) GetBits(8); + sps->matrix_coefficients = (mfxU8) GetBits(8); + } + } + sps->chroma_loc_info_present_flag = (mfxU8) Get1Bit(); + if( sps->chroma_loc_info_present_flag ) { + sps->chroma_sample_loc_type_top_field = (mfxU8) GetVLCElement(false); + sps->chroma_sample_loc_type_bottom_field = (mfxU8) GetVLCElement(false); + } + sps->timing_info_present_flag = (mfxU8) Get1Bit(); + + if (sps->timing_info_present_flag) + { + sps->num_units_in_tick = GetBits(32); + sps->time_scale = GetBits(32); + sps->fixed_frame_rate_flag = (mfxU8) Get1Bit(); + + if (!sps->num_units_in_tick || !sps->time_scale) + sps->timing_info_present_flag = 0; + } + + sps->nal_hrd_parameters_present_flag = (mfxU8) Get1Bit(); + if( sps->nal_hrd_parameters_present_flag ) + ps=GetHRDParam(sps); + sps->vcl_hrd_parameters_present_flag = (mfxU8) Get1Bit(); + if( sps->vcl_hrd_parameters_present_flag ) + ps=GetHRDParam(sps); + if( sps->nal_hrd_parameters_present_flag || sps->vcl_hrd_parameters_present_flag ) + sps->low_delay_hrd_flag = (mfxU8) Get1Bit(); + sps->pic_struct_present_flag = (mfxU8) Get1Bit(); + sps->bitstream_restriction_flag = (mfxU8) Get1Bit(); + if( sps->bitstream_restriction_flag ) { + sps->motion_vectors_over_pic_boundaries_flag = (mfxU8) Get1Bit(); + sps->max_bytes_per_pic_denom = (mfxU8) GetVLCElement(false); + sps->max_bits_per_mb_denom = (mfxU8) GetVLCElement(false); + sps->log2_max_mv_length_horizontal = (mfxU8) GetVLCElement(false); + sps->log2_max_mv_length_vertical = (mfxU8) GetVLCElement(false); + sps->num_reorder_frames = (mfxU8) GetVLCElement(false); + + mfxI32 value = GetVLCElement(false); + if (value < (mfxI32)sps->num_ref_frames || value < 0) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + sps->max_dec_frame_buffering = (mfxU8) GetVLCElement(false); + } + return ps; +} + +mfxStatus AVCHeadersBitstream::GetHRDParam(AVCSeqParamSet *sps) +{ + mfxStatus ps=MFX_ERR_NONE; + mfxI32 cpb_cnt = GetVLCElement(false) + 1; + + if (cpb_cnt >= 32) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + sps->cpb_cnt = (mfxU8)cpb_cnt; + + sps->bit_rate_scale = (mfxU8) GetBits(4); + sps->cpb_size_scale = (mfxU8) GetBits(4); + for( mfxI32 idx= 0; idx < sps->cpb_cnt; idx++ ) { + sps->bit_rate_value[ idx ] = (mfxU32) (GetVLCElement(false)+1); + sps->cpb_size_value[ idx ] = (mfxU32) ((GetVLCElement(false)+1)); + sps->cbr_flag[ idx ] = (mfxU8) Get1Bit(); + } + sps->initial_cpb_removal_delay_length = (mfxU8)(GetBits(5)+1); + sps->cpb_removal_delay_length = (mfxU8)(GetBits(5)+1); + sps->dpb_output_delay_length = (mfxU8) (GetBits(5)+1); + sps->time_offset_length = (mfxU8) GetBits(5); + return ps; + +} + +// --------------------------------------------------------------------------- +// Read sequence parameter set extension data from bitstream. +// --------------------------------------------------------------------------- +mfxStatus AVCHeadersBitstream::GetSequenceParamSetExtension(AVCSeqParamSetExtension *sps_ex) +{ + // Not all members of the seq param set structure are contained in all + // seq param sets. So start by init all to zero. + mfxStatus ps = MFX_ERR_NONE; + sps_ex->Reset(); + + mfxU32 seq_parameter_set_id = GetVLCElement(false); + sps_ex->seq_parameter_set_id = (mfxU8)seq_parameter_set_id; + if (seq_parameter_set_id > MAX_NUM_SEQ_PARAM_SETS-1) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + mfxU32 aux_format_idc = GetVLCElement(false); + sps_ex->aux_format_idc = (mfxU8)aux_format_idc; + if (aux_format_idc > 3) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + if (sps_ex->aux_format_idc != 1 && sps_ex->aux_format_idc != 2) + sps_ex->aux_format_idc = 0; + + if (sps_ex->aux_format_idc) + { + mfxU32 bit_depth_aux = GetVLCElement(false) + 8; + sps_ex->bit_depth_aux = (mfxU8)bit_depth_aux; + if (bit_depth_aux > 12) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + sps_ex->alpha_incr_flag = (mfxU8)Get1Bit(); + sps_ex->alpha_opaque_value = (mfxU8)GetBits(sps_ex->bit_depth_aux + 1); + sps_ex->alpha_transparent_value = (mfxU8)GetBits(sps_ex->bit_depth_aux + 1); + } + + sps_ex->additional_extension_flag = (mfxU8)Get1Bit(); + + return ps; +} // GetSequenceParamSetExtension + +mfxStatus AVCHeadersBitstream::GetPictureParamSetPart1(AVCPicParamSet *pps) +{ + // Not all members of the pic param set structure are contained in all + // pic param sets. So start by init all to zero. + pps->Reset(); + + // id + mfxU32 pic_parameter_set_id = GetVLCElement(false); + pps->pic_parameter_set_id = (mfxU16)pic_parameter_set_id; + if (pic_parameter_set_id > MAX_NUM_PIC_PARAM_SETS-1) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + // seq param set referred to by this pic param set + mfxU32 seq_parameter_set_id = GetVLCElement(false); + pps->seq_parameter_set_id = (mfxU8)seq_parameter_set_id; + if (seq_parameter_set_id > MAX_NUM_SEQ_PARAM_SETS-1) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + return MFX_ERR_NONE; +} // GetPictureParamSetPart1 + +// Number of bits required to code slice group ID, index is num_slice_groups - 2 +static const mfxU8 SGIdBits[7] = {1,2,2,3,3,3,3}; + +// --------------------------------------------------------------------------- +// Read picture parameter set data from bitstream. +// --------------------------------------------------------------------------- +mfxStatus AVCHeadersBitstream::GetPictureParamSetPart2(AVCPicParamSet *pps, + const AVCSeqParamSet *sps) +{ + pps->entropy_coding_mode = (mfxU8)Get1Bit(); + + + pps->pic_order_present_flag = (mfxU8)Get1Bit(); + + // number of slice groups, bitstream has value - 1 + pps->num_slice_groups = GetVLCElement(false) + 1; + if (pps->num_slice_groups != 1) + { + mfxU32 slice_group; + mfxU32 PicSizeInMapUnits; // for range checks + + PicSizeInMapUnits = sps->frame_width_in_mbs * sps->frame_height_in_mbs; + // TBD: needs adjust for fields + + if (pps->num_slice_groups > MAX_NUM_SLICE_GROUPS) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + mfxU32 slice_group_map_type = GetVLCElement(false); + pps->SliceGroupInfo.slice_group_map_type = (mfxU8)slice_group_map_type; + + if (slice_group_map_type > 6) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + // Get additional, map type dependent slice group data + switch (pps->SliceGroupInfo.slice_group_map_type) + { + case 0: + for (slice_group=0; slice_groupnum_slice_groups; slice_group++) + { + // run length, bitstream has value - 1 + pps->SliceGroupInfo.run_length[slice_group] = GetVLCElement(false) + 1; + + if (pps->SliceGroupInfo.run_length[slice_group] > PicSizeInMapUnits) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + } + break; + case 1: + // no additional info + break; + case 2: + for (slice_group=0; slice_group<(mfxU32)(pps->num_slice_groups-1); slice_group++) + { + pps->SliceGroupInfo.t1.top_left[slice_group] = GetVLCElement(false); + pps->SliceGroupInfo.t1.bottom_right[slice_group] = GetVLCElement(false); + + // check for legal values + if (pps->SliceGroupInfo.t1.top_left[slice_group] > + pps->SliceGroupInfo.t1.bottom_right[slice_group]) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + if (pps->SliceGroupInfo.t1.bottom_right[slice_group] >= PicSizeInMapUnits) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + if ((pps->SliceGroupInfo.t1.top_left[slice_group] % + sps->frame_width_in_mbs) > + (pps->SliceGroupInfo.t1.bottom_right[slice_group] % + sps->frame_width_in_mbs)) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + } + break; + case 3: + case 4: + case 5: + // For map types 3..5, number of slice groups must be 2 + if (pps->num_slice_groups != 2) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + pps->SliceGroupInfo.t2.slice_group_change_direction_flag = (mfxU8)Get1Bit(); + pps->SliceGroupInfo.t2.slice_group_change_rate = GetVLCElement(false) + 1; + if (pps->SliceGroupInfo.t2.slice_group_change_rate > PicSizeInMapUnits) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + break; + case 6: + // mapping of slice group to map unit (macroblock if not fields) is + // per map unit, read from bitstream + { + mfxU32 map_unit; + mfxU32 num_bits; // number of bits used to code each slice group id + + // number of map units, bitstream has value - 1 + pps->SliceGroupInfo.t3.pic_size_in_map_units = GetVLCElement(false) + 1; + if (pps->SliceGroupInfo.t3.pic_size_in_map_units != PicSizeInMapUnits) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + mfxI32 len = MSDK_MAX(1, pps->SliceGroupInfo.t3.pic_size_in_map_units); + + pps->SliceGroupInfo.pSliceGroupIDMap.resize(len); + + // num_bits is Ceil(log2(num_groups)) + num_bits = SGIdBits[pps->num_slice_groups - 2]; + + for (map_unit = 0; + map_unit < pps->SliceGroupInfo.t3.pic_size_in_map_units; + map_unit++) + { + pps->SliceGroupInfo.pSliceGroupIDMap[map_unit] = (mfxU8)GetBits(num_bits); + if (pps->SliceGroupInfo.pSliceGroupIDMap[map_unit] > + pps->num_slice_groups - 1) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + } + } + break; + default: + return MFX_ERR_UNDEFINED_BEHAVIOR; + + } // switch + } // slice group info + + // number of list 0 ref pics used to decode picture, bitstream has value - 1 + pps->num_ref_idx_l0_active = GetVLCElement(false) + 1; + + // number of list 1 ref pics used to decode picture, bitstream has value - 1 + pps->num_ref_idx_l1_active = GetVLCElement(false) + 1; + + if (pps->num_ref_idx_l1_active > MAX_NUM_REF_FRAMES || pps->num_ref_idx_l0_active > MAX_NUM_REF_FRAMES) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + // weighted pediction + pps->weighted_pred_flag = (mfxU8)Get1Bit(); + pps->weighted_bipred_idc = (mfxU8)GetBits(2); + + // default slice QP, bitstream has value - 26 + mfxI32 pic_init_qp = GetVLCElement(true) + 26; + pps->pic_init_qp = (mfxI8)pic_init_qp; + + // default SP/SI slice QP, bitstream has value - 26 + pps->pic_init_qs = (mfxU8)(GetVLCElement(true) + 26); + pps->chroma_qp_index_offset[0] = (mfxI8)GetVLCElement(true); + + pps->deblocking_filter_variables_present_flag = (mfxU8)Get1Bit(); + pps->constrained_intra_pred_flag = (mfxU8)Get1Bit(); + pps->redundant_pic_cnt_present_flag = (mfxU8)Get1Bit(); + if (More_RBSP_Data()) + { + pps->transform_8x8_mode_flag = (mfxU8) Get1Bit(); + if(sps->seq_scaling_matrix_present_flag) + { + //fall-back set rule B + if(Get1Bit()) + { + // 0 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[0],(mfxU8*)default_intra_scaling_list4x4,&pps->type_of_scaling_list_used[0]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[0],(mfxU8*) sps->ScalingLists4x4[0].ScalingListCoeffs); + pps->type_of_scaling_list_used[0] = SCLDEFAULT; + } + // 1 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[1],(mfxU8*) default_intra_scaling_list4x4,&pps->type_of_scaling_list_used[1]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[1],(mfxU8*) pps->ScalingLists4x4[0].ScalingListCoeffs); + pps->type_of_scaling_list_used[1] = SCLDEFAULT; + } + // 2 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[2],(mfxU8*) default_intra_scaling_list4x4,&pps->type_of_scaling_list_used[2]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[2],(mfxU8*) pps->ScalingLists4x4[1].ScalingListCoeffs); + pps->type_of_scaling_list_used[2] = SCLDEFAULT; + } + // 3 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[3],(mfxU8*) default_inter_scaling_list4x4,&pps->type_of_scaling_list_used[3]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[3],(mfxU8*) sps->ScalingLists4x4[3].ScalingListCoeffs); + pps->type_of_scaling_list_used[3] = SCLDEFAULT; + } + // 4 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[4],(mfxU8*) default_inter_scaling_list4x4,&pps->type_of_scaling_list_used[4]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[4],(mfxU8*) pps->ScalingLists4x4[3].ScalingListCoeffs); + pps->type_of_scaling_list_used[4] = SCLDEFAULT; + } + // 5 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[5],(mfxU8*) default_inter_scaling_list4x4,&pps->type_of_scaling_list_used[5]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[5],(mfxU8*) pps->ScalingLists4x4[4].ScalingListCoeffs); + pps->type_of_scaling_list_used[5] = SCLDEFAULT; + } + + if (pps->transform_8x8_mode_flag) + { + // 0 + if(Get1Bit()) + { + GetScalingList8x8(&pps->ScalingLists8x8[0],(mfxU8*)default_intra_scaling_list8x8,&pps->type_of_scaling_list_used[6]); + } + else + { + FillScalingList8x8(&pps->ScalingLists8x8[0],(mfxU8*) sps->ScalingLists8x8[0].ScalingListCoeffs); + pps->type_of_scaling_list_used[6] = SCLDEFAULT; + } + // 1 + if(Get1Bit()) + { + GetScalingList8x8(&pps->ScalingLists8x8[1],(mfxU8*) default_inter_scaling_list8x8,&pps->type_of_scaling_list_used[7]); + } + else + { + FillScalingList8x8(&pps->ScalingLists8x8[1],(mfxU8*) sps->ScalingLists8x8[1].ScalingListCoeffs); + pps->type_of_scaling_list_used[7] = SCLDEFAULT; + } + } + } + else + { + mfxI32 i; + for(i=0; i<6; i++) + { + FillScalingList4x4(&pps->ScalingLists4x4[i],(mfxU8 *)sps->ScalingLists4x4[i].ScalingListCoeffs); + pps->type_of_scaling_list_used[i] = sps->type_of_scaling_list_used[i]; + } + + if (pps->transform_8x8_mode_flag) + { + for(i=0; i<2; i++) + { + FillScalingList8x8(&pps->ScalingLists8x8[i],(mfxU8 *)sps->ScalingLists8x8[i].ScalingListCoeffs); + pps->type_of_scaling_list_used[i] = sps->type_of_scaling_list_used[i]; + } + } + } + } + else + { + //fall-back set rule A + if(Get1Bit()) + { + // 0 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[0],(mfxU8*)default_intra_scaling_list4x4,&pps->type_of_scaling_list_used[0]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[0],(mfxU8*) default_intra_scaling_list4x4); + pps->type_of_scaling_list_used[0] = SCLDEFAULT; + } + // 1 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[1],(mfxU8*) default_intra_scaling_list4x4,&pps->type_of_scaling_list_used[1]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[1],(mfxU8*) pps->ScalingLists4x4[0].ScalingListCoeffs); + pps->type_of_scaling_list_used[1] = SCLDEFAULT; + } + // 2 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[2],(mfxU8*) default_intra_scaling_list4x4,&pps->type_of_scaling_list_used[2]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[2],(mfxU8*) pps->ScalingLists4x4[1].ScalingListCoeffs); + pps->type_of_scaling_list_used[2] = SCLDEFAULT; + } + // 3 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[3],(mfxU8*)default_inter_scaling_list4x4,&pps->type_of_scaling_list_used[3]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[3],(mfxU8*) default_inter_scaling_list4x4); + pps->type_of_scaling_list_used[3] = SCLDEFAULT; + } + // 4 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[4],(mfxU8*) default_inter_scaling_list4x4,&pps->type_of_scaling_list_used[4]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[4],(mfxU8*) pps->ScalingLists4x4[3].ScalingListCoeffs); + pps->type_of_scaling_list_used[4] = SCLDEFAULT; + } + // 5 + if(Get1Bit()) + { + GetScalingList4x4(&pps->ScalingLists4x4[5],(mfxU8*) default_inter_scaling_list4x4,&pps->type_of_scaling_list_used[5]); + } + else + { + FillScalingList4x4(&pps->ScalingLists4x4[5],(mfxU8*) pps->ScalingLists4x4[4].ScalingListCoeffs); + pps->type_of_scaling_list_used[5] = SCLDEFAULT; + } + + if (pps->transform_8x8_mode_flag) + { + // 0 + if(Get1Bit()) + { + GetScalingList8x8(&pps->ScalingLists8x8[0],(mfxU8*)default_intra_scaling_list8x8,&pps->type_of_scaling_list_used[6]); + } + else + { + FillScalingList8x8(&pps->ScalingLists8x8[0],(mfxU8*) default_intra_scaling_list8x8); + pps->type_of_scaling_list_used[6] = SCLDEFAULT; + } + // 1 + if(Get1Bit()) + { + GetScalingList8x8(&pps->ScalingLists8x8[1],(mfxU8*) default_inter_scaling_list8x8,&pps->type_of_scaling_list_used[7]); + } + else + { + FillScalingList8x8(&pps->ScalingLists8x8[1],(mfxU8*) default_inter_scaling_list8x8); + pps->type_of_scaling_list_used[7] = SCLDEFAULT; + } + } + } + else + { + mfxI32 i; + for(i=0; i<6; i++) + { + FillScalingList4x4(&pps->ScalingLists4x4[i],(mfxU8 *)sps->ScalingLists4x4[i].ScalingListCoeffs); + pps->type_of_scaling_list_used[i] = sps->type_of_scaling_list_used[i]; + } + + if (pps->transform_8x8_mode_flag) + { + for(i=0; i<2; i++) + { + FillScalingList8x8(&pps->ScalingLists8x8[i],(mfxU8 *)sps->ScalingLists8x8[i].ScalingListCoeffs); + pps->type_of_scaling_list_used[i] = sps->type_of_scaling_list_used[i]; + } + } + } + } + pps->chroma_qp_index_offset[1] = (mfxI8)GetVLCElement(true); + } + else + { + pps->chroma_qp_index_offset[1] = pps->chroma_qp_index_offset[0]; + mfxI32 i; + for(i=0; i<6; i++) + { + FillScalingList4x4(&pps->ScalingLists4x4[i],(mfxU8 *)sps->ScalingLists4x4[i].ScalingListCoeffs); + pps->type_of_scaling_list_used[i] = sps->type_of_scaling_list_used[i]; + } + + if (pps->transform_8x8_mode_flag) + { + for(i=0; i<2; i++) + { + FillScalingList8x8(&pps->ScalingLists8x8[i],(mfxU8 *)sps->ScalingLists8x8[i].ScalingListCoeffs); + pps->type_of_scaling_list_used[i] = sps->type_of_scaling_list_used[i]; + } + } + } + // calculate level scale matrices + + //start DC first + //to do: reduce the number of matrices (in fact 1 is enough) + mfxI32 i; + // now process other 4x4 matrices + for (i = 0; i < 6; i++) + { + for (mfxI32 j = 0; j < 88; j++) + for (mfxI32 k = 0; k < 16; k++) + { + mfxU32 level_scale = pps->ScalingLists4x4[i].ScalingListCoeffs[k]*pre_norm_adjust4x4[j%6][pre_norm_adjust_index4x4[k]]; + pps->m_LevelScale4x4[i].LevelScaleCoeffs[j][k] = (mfxI16) level_scale; + } + } + + // process remaining 8x8 matrices + for (i = 0; i < 2; i++) + { + for (mfxI32 j = 0; j < 88; j++) + for (mfxI32 k = 0; k < 64; k++) + { + + mfxU32 level_scale = pps->ScalingLists8x8[i].ScalingListCoeffs[k]*pre_norm_adjust8x8[j%6][pre_norm_adjust_index8x8[k]]; + pps->m_LevelScale8x8[i].LevelScaleCoeffs[j][k] = (mfxI16) level_scale; + + } + } + + return MFX_ERR_NONE; +} // GetPictureParamSet + +mfxStatus AVCHeadersBitstream::GetNalUnitPrefix(AVCNalExtension *pExt, mfxU32 ) +{ + mfxStatus ps = MFX_ERR_NONE; + + ps = GetNalUnitExtension(pExt); + + if (ps != MFX_ERR_NONE || !pExt->svc_extension_flag) + return ps; + + return ps; +} + +mfxStatus AVCHeadersBitstream::GetNalUnitExtension(AVCNalExtension *pExt) +{ + pExt->extension_present = 1; + + // decode the type of the extension + pExt->svc_extension_flag = (mfxU8) GetBits(1); + + // decode SVC extension + if (pExt->svc_extension_flag) + { + pExt->svc.idr_flag = (mfxU8) Get1Bit(); + pExt->svc.priority_id = (mfxU8) GetBits(6); + pExt->svc.no_inter_layer_pred_flag = (mfxU8) Get1Bit(); + pExt->svc.dependency_id = (mfxU8) GetBits(3); + pExt->svc.quality_id = (mfxU8) GetBits(4); + pExt->svc.temporal_id = (mfxU8) GetBits(3); + pExt->svc.use_ref_base_pic_flag = (mfxU8) Get1Bit(); + pExt->svc.discardable_flag = (mfxU8) Get1Bit(); + pExt->svc.output_flag = (mfxU8) Get1Bit(); + GetBits(2); + } + // decode MVC extension + else + { + pExt->mvc.non_idr_flag = (mfxU8) Get1Bit(); + pExt->mvc.priority_id = (mfxU16) GetBits(6); + pExt->mvc.view_id = (mfxU16) GetBits(10); + pExt->mvc.temporal_id = (mfxU8) GetBits(3); + pExt->mvc.anchor_pic_flag = (mfxU8) Get1Bit(); + pExt->mvc.inter_view_flag = (mfxU8) Get1Bit(); + GetBits(1); + } + + return MFX_ERR_NONE; + +} + +// --------------------------------------------------------------------------- +// Read H.264 first part of slice header +// +// Reading the rest of the header requires info in the picture and sequence +// parameter sets referred to by this slice header. +// +// Do not print debug messages when IsSearch is true. In that case the function +// is being used to find the next compressed frame, errors may occur and should +// not be reported. +// +// --------------------------------------------------------------------------- +mfxStatus AVCHeadersBitstream::GetSliceHeaderPart1(AVCSliceHeader *hdr) +{ + mfxU32 val; + + // decode NAL extension + if (NAL_UT_CODED_SLICE_EXTENSION == hdr->nal_unit_type) + { + GetNalUnitExtension(&hdr->nal_ext); + + // set the IDR flag + if (hdr->nal_ext.svc_extension_flag) + { + hdr->IdrPicFlag = hdr->nal_ext.svc.idr_flag; + } + else + { + hdr->view_id = hdr->nal_ext.mvc.view_id; + hdr->IdrPicFlag = hdr->nal_ext.mvc.non_idr_flag ^ 1; + } + } + else + { + hdr->IdrPicFlag = (NAL_UT_IDR_SLICE == hdr->nal_unit_type) ? (1) : (0); + hdr->nal_ext.mvc.anchor_pic_flag = (mfxU8) hdr->IdrPicFlag ? 1 : 0; + hdr->nal_ext.mvc.inter_view_flag = (mfxU8) 1; + } + + hdr->first_mb_in_slice = GetVLCElement(false); + if (0 > hdr->first_mb_in_slice) // upper bound is checked in AVCSlice + return MFX_ERR_UNDEFINED_BEHAVIOR; + + // slice type + val = GetVLCElement(false); + if (val > S_INTRASLICE) + { + if (val > S_INTRASLICE + S_INTRASLICE + 1) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + else + { + // Slice type is specifying type of not only this but all remaining + // slices in the picture. Since slice type is always present, this bit + // of info is not used in our implementation. Adjust (just shift range) + // and return type without this extra info. + val -= (S_INTRASLICE + 1); + } + } + + if (val > INTRASLICE) // all other doesn't support + return MFX_ERR_UNDEFINED_BEHAVIOR; + + hdr->slice_type = (EnumSliceCodType)val; + + mfxU32 pic_parameter_set_id = GetVLCElement(false); + hdr->pic_parameter_set_id = (mfxU16)pic_parameter_set_id; + if (pic_parameter_set_id > MAX_NUM_PIC_PARAM_SETS - 1) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + return MFX_ERR_NONE; +} // mfxStatus GetSliceHeaderPart1(AVCSliceHeader *pSliceHeader) + +mfxStatus AVCHeadersBitstream::GetSliceHeaderPart2( + AVCSliceHeader *hdr, // slice header read goes here + const AVCPicParamSet *pps, + const AVCSeqParamSet *sps) // from slice header NAL unit +{ + hdr->frame_num = GetBits(sps->log2_max_frame_num); + + hdr->bottom_field_flag = 0; + if (sps->frame_mbs_only_flag == 0) + { + hdr->field_pic_flag = (mfxU8)Get1Bit(); + hdr->MbaffFrameFlag = !hdr->field_pic_flag && sps->mb_adaptive_frame_field_flag; + if (hdr->field_pic_flag != 0) + { + hdr->bottom_field_flag = (mfxU8)Get1Bit(); + } + } + + // correct frst_mb_in_slice in order to handle MBAFF + if (hdr->MbaffFrameFlag && hdr->first_mb_in_slice) + hdr->first_mb_in_slice <<= 1; + + if (hdr->IdrPicFlag) + { + mfxI32 pic_id = hdr->idr_pic_id = GetVLCElement(false); + if (pic_id < 0 || pic_id > 65535) + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + if (sps->pic_order_cnt_type == 0) + { + hdr->pic_order_cnt_lsb = GetBits(sps->log2_max_pic_order_cnt_lsb); + if (pps->pic_order_present_flag && (!hdr->field_pic_flag)) + hdr->delta_pic_order_cnt_bottom = GetVLCElement(true); + } + + if ((sps->pic_order_cnt_type == 1) && (sps->delta_pic_order_always_zero_flag == 0)) + { + hdr->delta_pic_order_cnt[0] = GetVLCElement(true); + if (pps->pic_order_present_flag && (!hdr->field_pic_flag)) + hdr->delta_pic_order_cnt[1] = GetVLCElement(true); + } + + if (pps->redundant_pic_cnt_present_flag) + { + // redundant pic count + hdr->redundant_pic_cnt = GetVLCElement(false); + if (hdr->redundant_pic_cnt > 127) + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + return MFX_ERR_NONE; +} + +// --------------------------------------------------------------------------- +// Read H.264 second part of slice header +// +// Do not print debug messages when IsSearch is true. In that case the function +// is being used to find the next compressed frame, errors may occur and should +// not be reported. +// --------------------------------------------------------------------------- + +mfxStatus AVCHeadersBitstream::GetSliceHeaderPart3( + AVCSliceHeader *hdr, // slice header read goes here + PredWeightTable *pPredWeight_L0, // L0 weight table goes here + PredWeightTable *pPredWeight_L1, // L1 weight table goes here + RefPicListReorderInfo *pReorderInfo_L0, + RefPicListReorderInfo *pReorderInfo_L1, + AdaptiveMarkingInfo *pAdaptiveMarkingInfo, + const AVCPicParamSet *pps, + const AVCSeqParamSet *sps, + mfxU8 NALRef_idc) // from slice header NAL unit +{ + mfxU8 ref_pic_list_reordering_flag_l0 = 0; + mfxU8 ref_pic_list_reordering_flag_l1 = 0; + + if (BPREDSLICE == hdr->slice_type) + { + // direct mode prediction method + hdr->direct_spatial_mv_pred_flag = (mfxU8)Get1Bit(); + } + + if (PREDSLICE == hdr->slice_type || + S_PREDSLICE == hdr->slice_type || + BPREDSLICE == hdr->slice_type) + { + hdr->num_ref_idx_active_override_flag = (mfxU8)Get1Bit(); + if (hdr->num_ref_idx_active_override_flag != 0) + // ref idx active l0 and l1 + { + hdr->num_ref_idx_l0_active = GetVLCElement(false) + 1; + if (BPREDSLICE == hdr->slice_type) + hdr->num_ref_idx_l1_active = GetVLCElement(false) + 1; + } + else + { + // no overide, use num active from pic param set + hdr->num_ref_idx_l0_active = pps->num_ref_idx_l0_active; + if (BPREDSLICE == hdr->slice_type) + hdr->num_ref_idx_l1_active = pps->num_ref_idx_l1_active; + else + hdr->num_ref_idx_l1_active = 0; + } + } // ref idx override + + if (hdr->num_ref_idx_l1_active > MAX_NUM_REF_FRAMES || hdr->num_ref_idx_l0_active > MAX_NUM_REF_FRAMES) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + if (hdr->slice_type != INTRASLICE && hdr->slice_type != S_INTRASLICE) + { + mfxU32 reordering_of_pic_nums_idc; + mfxU32 reorder_idx; + + // Reference picture list reordering + ref_pic_list_reordering_flag_l0 = (mfxU8)Get1Bit(); + if (ref_pic_list_reordering_flag_l0) + { + bool bOk = true; + + reorder_idx = 0; + reordering_of_pic_nums_idc = 0; + + // Get reorder idc,pic_num pairs until idc==3 + while (bOk) + { + reordering_of_pic_nums_idc = (mfxU8)GetVLCElement(false); + if (reordering_of_pic_nums_idc > 5) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + if (reordering_of_pic_nums_idc == 3) + break; + + if (reorder_idx >= MAX_NUM_REF_FRAMES) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + pReorderInfo_L0->reordering_of_pic_nums_idc[reorder_idx] = + (mfxU8)reordering_of_pic_nums_idc; + pReorderInfo_L0->reorder_value[reorder_idx] = + GetVLCElement(false); + if (reordering_of_pic_nums_idc != 2) + // abs_diff_pic_num is coded minus 1 + pReorderInfo_L0->reorder_value[reorder_idx]++; + reorder_idx++; + } // while + + pReorderInfo_L0->num_entries = reorder_idx; + } // L0 reordering info + else + pReorderInfo_L0->num_entries = 0; + + if (BPREDSLICE == hdr->slice_type) + { + ref_pic_list_reordering_flag_l1 = (mfxU8)Get1Bit(); + if (ref_pic_list_reordering_flag_l1) + { + bool bOk = true; + + // Get reorder idc,pic_num pairs until idc==3 + reorder_idx = 0; + reordering_of_pic_nums_idc = 0; + while (bOk) + { + reordering_of_pic_nums_idc = GetVLCElement(false); + if (reordering_of_pic_nums_idc > 5) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + if (reordering_of_pic_nums_idc == 3) + break; + + if (reorder_idx >= MAX_NUM_REF_FRAMES) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + pReorderInfo_L1->reordering_of_pic_nums_idc[reorder_idx] = + (mfxU8)reordering_of_pic_nums_idc; + pReorderInfo_L1->reorder_value[reorder_idx] = + GetVLCElement(false); + if (reordering_of_pic_nums_idc != 2) + // abs_diff_pic_num is coded minus 1 + pReorderInfo_L1->reorder_value[reorder_idx]++; + reorder_idx++; + } // while + pReorderInfo_L1->num_entries = reorder_idx; + } // L1 reordering info + else + pReorderInfo_L1->num_entries = 0; + + } // B slice + } // reordering info + + // prediction weight table + if ( (pps->weighted_pred_flag && + ((PREDSLICE == hdr->slice_type) || (S_PREDSLICE == hdr->slice_type))) || + ((pps->weighted_bipred_idc == 1) && (BPREDSLICE == hdr->slice_type))) + { + hdr->luma_log2_weight_denom = (mfxU8)GetVLCElement(false); + if (sps->chroma_format_idc != 0) + hdr->chroma_log2_weight_denom = (mfxU8)GetVLCElement(false); + + for (mfxI32 refindex = 0; refindex < hdr->num_ref_idx_l0_active; refindex++) + { + pPredWeight_L0[refindex].luma_weight_flag = (mfxU8)Get1Bit(); + if (pPredWeight_L0[refindex].luma_weight_flag) + { + pPredWeight_L0[refindex].luma_weight = (mfxI8)GetVLCElement(true); + pPredWeight_L0[refindex].luma_offset = (mfxI8)GetVLCElement(true); + } + else + { + pPredWeight_L0[refindex].luma_weight = (mfxI8)(1 << hdr->luma_log2_weight_denom); + pPredWeight_L0[refindex].luma_offset = 0; + } + + if (sps->chroma_format_idc != 0) + { + pPredWeight_L0[refindex].chroma_weight_flag = (mfxU8)Get1Bit(); + if (pPredWeight_L0[refindex].chroma_weight_flag) + { + pPredWeight_L0[refindex].chroma_weight[0] = (mfxI8)GetVLCElement(true); + pPredWeight_L0[refindex].chroma_offset[0] = (mfxI8)GetVLCElement(true); + pPredWeight_L0[refindex].chroma_weight[1] = (mfxI8)GetVLCElement(true); + pPredWeight_L0[refindex].chroma_offset[1] = (mfxI8)GetVLCElement(true); + } + else + { + pPredWeight_L0[refindex].chroma_weight[0] = (mfxI8)(1 << hdr->chroma_log2_weight_denom); + pPredWeight_L0[refindex].chroma_weight[1] = (mfxI8)(1 << hdr->chroma_log2_weight_denom); + pPredWeight_L0[refindex].chroma_offset[0] = 0; + pPredWeight_L0[refindex].chroma_offset[1] = 0; + } + } + } + + if (BPREDSLICE == hdr->slice_type) + { + for (mfxI32 refindex = 0; refindex < hdr->num_ref_idx_l1_active; refindex++) + { + pPredWeight_L1[refindex].luma_weight_flag = (mfxU8)Get1Bit(); + if (pPredWeight_L1[refindex].luma_weight_flag) + { + pPredWeight_L1[refindex].luma_weight = (mfxI8)GetVLCElement(true); + pPredWeight_L1[refindex].luma_offset = (mfxI8)GetVLCElement(true); + } + else + { + pPredWeight_L1[refindex].luma_weight = (mfxI8)(1 << hdr->luma_log2_weight_denom); + pPredWeight_L1[refindex].luma_offset = 0; + } + + if (sps->chroma_format_idc != 0) + { + pPredWeight_L1[refindex].chroma_weight_flag = (mfxU8)Get1Bit(); + if (pPredWeight_L1[refindex].chroma_weight_flag) + { + pPredWeight_L1[refindex].chroma_weight[0] = (mfxI8)GetVLCElement(true); + pPredWeight_L1[refindex].chroma_offset[0] = (mfxI8)GetVLCElement(true); + pPredWeight_L1[refindex].chroma_weight[1] = (mfxI8)GetVLCElement(true); + pPredWeight_L1[refindex].chroma_offset[1] = (mfxI8)GetVLCElement(true); + } + else + { + pPredWeight_L1[refindex].chroma_weight[0] = (mfxI8)(1 << hdr->chroma_log2_weight_denom); + pPredWeight_L1[refindex].chroma_weight[1] = (mfxI8)(1 << hdr->chroma_log2_weight_denom); + pPredWeight_L1[refindex].chroma_offset[0] = 0; + pPredWeight_L1[refindex].chroma_offset[1] = 0; + } + } + } + } // B slice + } // prediction weight table + else + { + hdr->luma_log2_weight_denom = 0; + hdr->chroma_log2_weight_denom = 0; + } + + // dec_ref_pic_marking + pAdaptiveMarkingInfo->num_entries = 0; + + if (NALRef_idc) + { + if (hdr->IdrPicFlag) + { + hdr->no_output_of_prior_pics_flag = (mfxU8)Get1Bit(); + hdr->long_term_reference_flag = (mfxU8)Get1Bit(); + } + else + { + mfxU32 memory_management_control_operation; + mfxU32 num_entries = 0; + + hdr->adaptive_ref_pic_marking_mode_flag = (mfxU8)Get1Bit(); + while (hdr->adaptive_ref_pic_marking_mode_flag != 0) + { + memory_management_control_operation = (mfxU8)GetVLCElement(false); + if (memory_management_control_operation == 0) + break; + + if (memory_management_control_operation > 6) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + pAdaptiveMarkingInfo->mmco[num_entries] = + (mfxU8)memory_management_control_operation; + if (memory_management_control_operation != 5) + pAdaptiveMarkingInfo->value[num_entries*2] = + GetVLCElement(false); + // Only mmco 3 requires 2 values + if (memory_management_control_operation == 3) + pAdaptiveMarkingInfo->value[num_entries*2+1] = + GetVLCElement(false); + num_entries++; + if (num_entries >= MAX_NUM_REF_FRAMES) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + } // while + pAdaptiveMarkingInfo->num_entries = num_entries; + } + } // def_ref_pic_marking + + if (pps->entropy_coding_mode == 1 && // CABAC + (hdr->slice_type != INTRASLICE && hdr->slice_type != S_INTRASLICE)) + hdr->cabac_init_idc = GetVLCElement(false); + else + hdr->cabac_init_idc = 0; + + if (hdr->cabac_init_idc > 2) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + hdr->slice_qp_delta = GetVLCElement(true); + + if (S_PREDSLICE == hdr->slice_type || + S_INTRASLICE == hdr->slice_type) + { + if (S_PREDSLICE == hdr->slice_type) + hdr->sp_for_switch_flag = (mfxU8)Get1Bit(); + hdr->slice_qs_delta = GetVLCElement(true); + } + + if (pps->deblocking_filter_variables_present_flag != 0) + { + // deblock filter flag and offsets + hdr->disable_deblocking_filter_idc = GetVLCElement(false); + if (hdr->disable_deblocking_filter_idc > 2) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + if (hdr->disable_deblocking_filter_idc != 1) + { + hdr->slice_alpha_c0_offset = GetVLCElement(true)<<1; + hdr->slice_beta_offset = GetVLCElement(true)<<1; + + if (hdr->slice_alpha_c0_offset < -12 || hdr->slice_alpha_c0_offset > 12) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + if (hdr->slice_beta_offset < -12 || hdr->slice_beta_offset > 12) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + } + else + { + // set filter offsets to max values to disable filter + hdr->slice_alpha_c0_offset = (mfxI8)(0 - AVC_QP_MAX); + hdr->slice_beta_offset = (mfxI8)(0 - AVC_QP_MAX); + } + } + + if (pps->num_slice_groups > 1 && + pps->SliceGroupInfo.slice_group_map_type >= 3 && + pps->SliceGroupInfo.slice_group_map_type <= 5) + { + mfxU32 num_bits; // number of bits used to code slice_group_change_cycle + mfxU32 val; + mfxU32 pic_size_in_map_units; + mfxU32 max_slice_group_change_cycle=0; + + // num_bits is Ceil(log2(picsizeinmapunits/slicegroupchangerate + 1)) + pic_size_in_map_units = sps->frame_width_in_mbs * sps->frame_height_in_mbs; + + max_slice_group_change_cycle = pic_size_in_map_units / + pps->SliceGroupInfo.t2.slice_group_change_rate; + if (pic_size_in_map_units % + pps->SliceGroupInfo.t2.slice_group_change_rate) + max_slice_group_change_cycle++; + + val = max_slice_group_change_cycle; + num_bits = 0; + while (val) + { + num_bits++; + val >>= 1; + } + hdr->slice_group_change_cycle = GetBits(num_bits); + } + + return MFX_ERR_NONE; +} // GetSliceHeaderPart3() + +void AVCHeadersBitstream::GetScalingList4x4(AVCScalingList4x4 *scl, mfxU8 *def, mfxU8 *scl_type) +{ + mfxU32 lastScale = 8; + mfxU32 nextScale = 8; + bool DefaultMatrix = false; + mfxI32 j; + + for (j = 0; j < 16; j++ ) + { + if (nextScale != 0) + { + mfxI32 delta_scale = GetVLCElement(true); + if (delta_scale < -128 || delta_scale > 127) + throw AVC_exception(MFX_ERR_UNDEFINED_BEHAVIOR); + nextScale = ( lastScale + delta_scale + 256 ) & 0xff; + DefaultMatrix = ( j == 0 && nextScale == 0 ); + } + scl->ScalingListCoeffs[ mp_scan4x4[0][j] ] = ( nextScale == 0 ) ? (mfxU8)lastScale : (mfxU8)nextScale; + lastScale = scl->ScalingListCoeffs[ mp_scan4x4[0][j] ]; + } + if (!DefaultMatrix) + { + *scl_type = SCLREDEFINED; + return; + } + *scl_type= SCLDEFAULT; + FillScalingList4x4(scl,def); + return; +} + +void AVCHeadersBitstream::GetScalingList8x8(AVCScalingList8x8 *scl, mfxU8 *def, mfxU8 *scl_type) +{ + mfxU32 lastScale = 8; + mfxU32 nextScale = 8; + bool DefaultMatrix=false; + mfxI32 j; + + for (j = 0; j < 64; j++ ) + { + if (nextScale != 0) + { + mfxI32 delta_scale = GetVLCElement(true); + if (delta_scale < -128 || delta_scale > 127) + throw AVC_exception(MFX_ERR_UNDEFINED_BEHAVIOR); + nextScale = ( lastScale + delta_scale + 256 ) & 0xff; + DefaultMatrix = ( j == 0 && nextScale == 0 ); + } + scl->ScalingListCoeffs[ hp_scan8x8[0][j] ] = ( nextScale == 0 ) ? (mfxU8)lastScale : (mfxU8)nextScale; + lastScale = scl->ScalingListCoeffs[ hp_scan8x8[0][j] ]; + } + if (!DefaultMatrix) + { + *scl_type=SCLREDEFINED; + return; + } + *scl_type= SCLDEFAULT; + FillScalingList8x8(scl,def); + return; + +} + +void SetDefaultScalingLists(AVCSeqParamSet * sps) +{ + mfxI32 i; + + for (i = 0; i < 6; i += 1) + { + FillFlatScalingList4x4(&sps->ScalingLists4x4[i]); + } + for (i = 0; i < 2; i += 1) + { + FillFlatScalingList8x8(&sps->ScalingLists8x8[i]); + } +} + +mfxStatus DecodeExpGolombOne(mfxU32 **ppBitStream, mfxI32 *pBitOffset, + mfxI32 *pDst, + mfxI32 isSigned) +{ + mfxU32 code; + mfxU32 info = 0; + mfxI32 length = 1; /* for first bit read above*/ + mfxU32 thisChunksLength = 0; + mfxU32 sval; + + /* Fast check for element = 0 */ + avcGetNBits((*ppBitStream), (*pBitOffset), 1, code); + if (code) + { + *pDst = 0; + return MFX_ERR_NONE; + } + + avcGetNBits((*ppBitStream), (*pBitOffset), 8, code); + length += 8; + + /* find nonzero byte */ + while (code == 0) + { + avcGetNBits((*ppBitStream), (*pBitOffset), 8, code); + length += 8; + } + + /* find leading '1' */ + while ((code & 0x80) == 0) + { + code <<= 1; + thisChunksLength++; + } + length -= 8 - thisChunksLength; + + avcUngetNBits((*ppBitStream), (*pBitOffset), 8 - (thisChunksLength + 1)); + + /* Get info portion of codeword */ + if (length) + { + avcGetNBits((*ppBitStream), (*pBitOffset),length, info); + } + + sval = (1 << length) + info - 1; + if (isSigned) + { + if (sval & 1) + *pDst = (mfxI32) ((sval + 1) >> 1); + else + *pDst = -((mfxI32) (sval >> 1)); + } + else + *pDst = (mfxI32) sval; + + return MFX_ERR_NONE; +} + +mfxI32 AVCHeadersBitstream::GetSEI(const HeaderSet & sps, mfxI32 current_sps, AVCSEIPayLoad *spl) +{ + mfxU32 code; + mfxI32 payloadType = 0; + + avcNextBits(m_pbs, m_bitOffset, 8, code); + while (code == 0xFF) + { + /* fixed-pattern bit string using 8 bits written equal to 0xFF */ + avcGetNBits(m_pbs, m_bitOffset, 8, code); + payloadType += 255; + avcNextBits(m_pbs, m_bitOffset, 8, code); + } + + mfxI32 last_payload_type_byte; //Ipp32u integer using 8 bits + avcGetNBits(m_pbs, m_bitOffset, 8, last_payload_type_byte); + + payloadType += last_payload_type_byte; + + mfxI32 payloadSize = 0; + + avcNextBits(m_pbs, m_bitOffset, 8, code); + while( code == 0xFF ) + { + /* fixed-pattern bit string using 8 bits written equal to 0xFF */ + avcGetNBits(m_pbs, m_bitOffset, 8, code); + payloadSize += 255; + avcNextBits(m_pbs, m_bitOffset, 8, code); + } + + mfxI32 last_payload_size_byte; //Ipp32u integer using 8 bits + + avcGetNBits(m_pbs, m_bitOffset, 8, last_payload_size_byte); + payloadSize += last_payload_size_byte; + spl->Reset(); + spl->payLoadSize = payloadSize; + + if (payloadType < 0 || payloadType > SEI_RESERVED) + payloadType = SEI_RESERVED; + + spl->payLoadType = (SEI_TYPE)payloadType; + + if (spl->payLoadSize > BytesLeft()) + { + throw AVC_exception(MFX_ERR_UNDEFINED_BEHAVIOR); + } + + mfxU32 * pbs; + mfxU32 bitOffsetU; + mfxI32 bitOffset; + + pbs = m_pbs; + bitOffsetU = m_bitOffset; + bitOffset = bitOffsetU; + + mfxI32 ret = GetSEIPayload(sps, current_sps, spl); + + for (mfxU32 i = 0; i < spl->payLoadSize; i++) + { + avcSkipNBits(pbs, bitOffset, 8); + } + + m_pbs = pbs; + m_bitOffset = bitOffset; + + return ret; +} + +mfxI32 AVCHeadersBitstream::GetSEIPayload(const HeaderSet & sps, mfxI32 current_sps, AVCSEIPayLoad *spl) +{ + switch (spl->payLoadType) + { + case SEI_RECOVERY_POINT_TYPE: + return recovery_point(sps,current_sps,spl); + default: + return reserved_sei_message(sps,current_sps,spl); + } +} + +mfxI32 AVCHeadersBitstream::reserved_sei_message(const HeaderSet & , mfxI32 current_sps, AVCSEIPayLoad *spl) +{ + for (mfxU32 i = 0; i < spl->payLoadSize; i++) + avcSkipNBits(m_pbs, m_bitOffset, 8) + AlignPointerRight(); + return current_sps; +} + +mfxI32 AVCHeadersBitstream::recovery_point(const HeaderSet & , mfxI32 current_sps, AVCSEIPayLoad *spl) +{ + AVCSEIPayLoad::SEIMessages::RecoveryPoint * recPoint = &(spl->SEI_messages.recovery_point); + + recPoint->recovery_frame_cnt = (mfxU8)GetVLCElement(false); + + recPoint->exact_match_flag = (mfxU8)Get1Bit(); + recPoint->broken_link_flag = (mfxU8)Get1Bit(); + recPoint->changing_slice_group_idc = (mfxU8)GetBits(2); + + if (recPoint->changing_slice_group_idc > 2) + return -1; + + return current_sps; +} + +void HeapObject::Free() +{ +} + +} // namespace ProtectedLibrary diff --git a/vshampor/deshuffler/sample_common/src/avc_nal_spl.cpp b/vshampor/deshuffler/sample_common/src/avc_nal_spl.cpp new file mode 100644 index 0000000..419cee3 --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/avc_nal_spl.cpp @@ -0,0 +1,535 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "sample_defs.h" +#include "avc_structures.h" +#include "avc_nal_spl.h" + +namespace ProtectedLibrary +{ + +static const mfxU32 MFX_TIME_STAMP_FREQUENCY = 90000; // will go to mfxdefs.h +static const mfxU64 MFX_TIME_STAMP_INVALID = (mfxU64)-1; // will go to mfxdefs.h + +inline +mfxF64 GetUmcTimeStamp(mfxU64 ts) +{ + return ts == MFX_TIME_STAMP_INVALID ? -1.0 : ts / (mfxF64)MFX_TIME_STAMP_FREQUENCY; +} + +inline +mfxU64 GetMfxTimeStamp(mfxF64 ts) +{ + return ts < 0.0 ? MFX_TIME_STAMP_INVALID : (mfxU64)(ts * MFX_TIME_STAMP_FREQUENCY + .5); +} + +static mfxU8 start_code_prefix[] = {0, 0, 0, 1}; + +enum +{ + AVC_NAL_UNITTYPE_BITS_MASK = 0x1f +}; + + +inline bool IsHeaderCode(mfxI32 iCode) +{ + return (NAL_UT_SPS == (iCode & AVC_NAL_UNITTYPE_BITS_MASK)) || + (NAL_UT_SPS_EX == (iCode & AVC_NAL_UNITTYPE_BITS_MASK)) || + (NAL_UT_PPS == (iCode & AVC_NAL_UNITTYPE_BITS_MASK)); +} + +inline bool IsVLCCode(mfxI32 iCode) +{ + return ((NAL_UT_SLICE <= (iCode & AVC_NAL_UNITTYPE_BITS_MASK)) && + (NAL_UT_IDR_SLICE >= (iCode & AVC_NAL_UNITTYPE_BITS_MASK))) || + (NAL_UT_AUXILIARY == (iCode & AVC_NAL_UNITTYPE_BITS_MASK)); +} + +static mfxI32 FindStartCode(mfxU8 * (&pb), mfxU32 &nSize) +{ + // there is no data + if (nSize < 4) + return 0; + + // find start code + while ((4 <= nSize) && ((0 != pb[0]) || + (0 != pb[1]) || + (1 != pb[2]))) + { + pb += 1; + nSize -= 1; + } + + if (4 <= nSize) + return ((pb[0] << 24) | (pb[1] << 16) | (pb[2] << 8) | (pb[3])); + + return 0; + +} + +mfxStatus MoveBitstream(mfxBitstream * source, mfxI32 moveSize) +{ + if (!source) + return MFX_ERR_NULL_PTR; + + if (moveSize < 0 && (mfxI32)source->DataOffset + moveSize < 0) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + if (moveSize > 0 && source->DataLength < (mfxU32)moveSize) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + source->DataOffset += moveSize; + source->DataLength -= moveSize; + + return MFX_ERR_NONE; +} + +StartCodeIterator::StartCodeIterator() + : m_code(0) + , m_pts(MFX_TIME_STAMP_INVALID) + , m_pSource(0) + , m_nSourceSize(0) + , m_pSourceBase(0) + , m_nSourceBaseSize(0) + , m_suggestedSize(10 * 1024) +{ + Reset(); +} + +void StartCodeIterator::Reset() +{ + m_code = 0; + m_pts = MFX_TIME_STAMP_INVALID; + m_prev.clear(); +} + +mfxI32 StartCodeIterator::Init(mfxBitstream * source) +{ + Reset(); + + m_pSourceBase = m_pSource = source->Data + source->DataOffset; + m_nSourceBaseSize = m_nSourceSize = source->DataLength; + + mfxI32 iCode = ProtectedLibrary::FindStartCode(m_pSource, m_nSourceSize); + return iCode; +} + +void StartCodeIterator::SetSuggestedSize(mfxU32 size) +{ + if (size > m_suggestedSize) + m_suggestedSize = size; +} + +mfxI32 StartCodeIterator::CheckNalUnitType(mfxBitstream * source) +{ + if (!source) + return 0; + + if (!m_code) + m_prev.clear(); + + mfxU8 * src = source->Data + source->DataOffset; + mfxU32 size = source->DataLength; + + mfxI32 startCodeSize; + mfxI32 iCodeNext = FindStartCode(src, size, startCodeSize); + return iCodeNext; +} + +mfxI32 StartCodeIterator::GetNALUnit(mfxBitstream * src, mfxBitstream * dst) +{ + if (!src) + return EndOfStream(dst); + + if (!m_code) + m_prev.clear(); + + mfxU8 * source = src->Data + src->DataOffset; + mfxU32 size = src->DataLength; + + if (!size) + return 0; + + mfxI32 startCodeSize; + + mfxI32 iCodeNext = FindStartCode(source, size, startCodeSize); + + if (m_prev.size()) + { + if (!iCodeNext) + { + size_t sz = source - (src->Data + src->DataOffset); + if (m_prev.size() + sz > m_suggestedSize) + { + m_prev.clear(); + sz = MSDK_MIN(sz, m_suggestedSize); + } + + m_prev.insert(m_prev.end(), src->Data + src->DataOffset, src->Data + src->DataOffset + sz); + MoveBitstream(src, (mfxI32)sz); + return 0; + } + + source -= startCodeSize; + m_prev.insert(m_prev.end(), src->Data + src->DataOffset, source); + MoveBitstream(src, (mfxI32)(source - (src->Data + src->DataOffset))); + + dst->Data = &(m_prev[0]); + dst->DataLength = (mfxU32)m_prev.size(); + dst->DataOffset = 0; + dst->TimeStamp = m_pts; + mfxI32 code = m_code; + m_code = 0; + m_pts = MFX_TIME_STAMP_INVALID; + return code; + } + + if (!iCodeNext) + { + MoveBitstream(src, (mfxI32)(source - (src->Data + src->DataOffset))); + return 0; + } + + m_pts = src->TimeStamp; + m_code = iCodeNext; + + // move before start code + MoveBitstream(src, (mfxI32)(source - (src->Data + src->DataOffset) - startCodeSize)); + + mfxI32 startCodeSize1; + iCodeNext = FindStartCode(source, size, startCodeSize1); + + MoveBitstream(src, startCodeSize); + + if (!iCodeNext && (src->DataFlag & MFX_BITSTREAM_COMPLETE_FRAME)) + { + iCodeNext = 1; + startCodeSize1 = 0; + } + + if (!iCodeNext) + { + if (m_prev.size()) // assertion: it should be + return 0; + + size_t sz = source - (src->Data + src->DataOffset); + if (sz > m_suggestedSize) + { + sz = m_suggestedSize; + } + + m_prev.insert(m_prev.end(), src->Data + src->DataOffset, src->Data + src->DataOffset + sz); + MoveBitstream(src, (mfxI32)sz); + return 0; + } + + // fill + size_t nal_size = source - (src->Data + src->DataOffset) - startCodeSize1; + + dst->Data = src->Data + src->DataOffset; + dst->DataLength = (mfxU32)nal_size; + dst->DataOffset = 0; + dst->TimeStamp = m_pts; + + MoveBitstream(src, (mfxI32)nal_size); + + mfxI32 code = m_code; + m_code = 0; + + m_pts = MFX_TIME_STAMP_INVALID; + return code; +} + +mfxI32 StartCodeIterator::EndOfStream(mfxBitstream * dst) +{ + if (!m_code) + { + m_prev.clear(); + return 0; + } + + if (m_prev.size()) + { + dst->Data = &(m_prev[0]); + dst->DataLength = (mfxU32)m_prev.size(); + dst->DataOffset = 0; + dst->TimeStamp = m_pts; + mfxI32 code = m_code; + m_code = 0; + m_pts = MFX_TIME_STAMP_INVALID; + return code; + } + + m_code = 0; + return 0; +} + +mfxI32 StartCodeIterator::FindStartCode(mfxU8 * (&pb), mfxU32 & size, mfxI32 & startCodeSize) +{ + mfxU32 zeroCount = 0; + + for (mfxU32 i = 0 ; i < (mfxU32)size; i++, pb++) + { + switch(pb[0]) + { + case 0x00: + zeroCount++; + break; + case 0x01: + if (zeroCount >= 2) + { + startCodeSize = MSDK_MIN(zeroCount + 1, 4); + size -= i + 1; + pb++; // remove 0x01 symbol + zeroCount = 0; + if (size >= 1) + { + return pb[0] & AVC_NAL_UNITTYPE_BITS_MASK; + } + else + { + pb -= startCodeSize; + size += startCodeSize; + startCodeSize = 0; + return 0; + } + } + zeroCount = 0; + break; + default: + zeroCount = 0; + break; + } + } + + zeroCount = MSDK_MIN(zeroCount, 3); + pb -= zeroCount; + size += zeroCount; + zeroCount = 0; + startCodeSize = 0; + return 0; +} + +void BytesSwapper::SwapMemory(mfxU8 *pDestination, mfxU32 &nDstSize, mfxU8 *pSource, mfxU32 nSrcSize) +{ + SwapMemoryAndRemovePreventingBytes(pDestination, nDstSize, pSource, nSrcSize); +} + +NALUnitSplitter::NALUnitSplitter() +{ + memset(&m_bitstream, 0, sizeof(m_bitstream)); +} + +NALUnitSplitter::~NALUnitSplitter() +{ + Release(); +} + +void NALUnitSplitter::Init() +{ + Release(); +} + +void NALUnitSplitter::Reset() +{ + m_pStartCodeIter.Reset(); +} + +void NALUnitSplitter::Release() +{ +} + +mfxI32 NALUnitSplitter::CheckNalUnitType(mfxBitstream * source) +{ + return m_pStartCodeIter.CheckNalUnitType(source); +} + +mfxI32 NALUnitSplitter::GetNalUnits(mfxBitstream * source, mfxBitstream * &destination) +{ + mfxI32 iCode = m_pStartCodeIter.GetNALUnit(source, &m_bitstream); + + if (iCode == 0) + { + destination = 0; + return 0; + } + + destination = &m_bitstream; + return iCode; +} + +/* temporal class definition */ +class H264DwordPointer_ +{ +public: + // Default constructor + H264DwordPointer_(void) + { + m_pDest = NULL; + m_nByteNum = 0; + m_iCur = 0; + } + + H264DwordPointer_ operator = (void *pDest) + { + m_pDest = (mfxU32 *) pDest; + m_nByteNum = 0; + m_iCur = 0; + + return *this; + } + + // Increment operator + H264DwordPointer_ &operator ++ (void) + { + if (4 == ++m_nByteNum) + { + *m_pDest = m_iCur; + m_pDest += 1; + m_nByteNum = 0; + m_iCur = 0; + } + else + m_iCur <<= 8; + + return *this; + } + + mfxU8 operator = (mfxU8 nByte) + { + m_iCur = (m_iCur & ~0x0ff) | ((mfxU32) nByte); + + return nByte; + } + +protected: + mfxU32 *m_pDest; // pointer to destination buffer + mfxU32 m_nByteNum; // number of current byte in dword + mfxU32 m_iCur; // current dword +}; + +class H264SourcePointer_ +{ +public: + // Default constructor + H264SourcePointer_(void) + { + m_pSource = NULL; + m_nRemovedBytes=0; + m_nZeros=0; + } + + H264SourcePointer_ &operator = (mfxU8 *pSource) + { + m_pSource = (mfxU8 *) pSource; + + m_nZeros = 0; + m_nRemovedBytes = 0; + + return *this; + } + + H264SourcePointer_ &operator ++ (void) + { + mfxU8 bCurByte = m_pSource[0]; + + if (0 == bCurByte) + m_nZeros += 1; + else + { + if ((3 == bCurByte) && (2 <= m_nZeros)) + m_nRemovedBytes += 1; + m_nZeros = 0; + } + + m_pSource += 1; + + return *this; + } + + bool IsPrevent(void) + { + if ((3 == m_pSource[0]) && (2 <= m_nZeros)) + return true; + else + return false; + } + + operator mfxU8 (void) + { + return m_pSource[0]; + } + + mfxU32 GetRemovedBytes(void) + { + return m_nRemovedBytes; + } + +protected: + mfxU8 *m_pSource; // pointer to destination buffer + mfxU32 m_nZeros; // number of preceding zeros + mfxU32 m_nRemovedBytes; // number of removed bytes +}; + +void SwapMemoryAndRemovePreventingBytes(mfxU8 *pDestination, mfxU32 &nDstSize, mfxU8 *pSource, mfxU32 nSrcSize) +{ + H264DwordPointer_ pDst; + H264SourcePointer_ pSrc; + size_t i; + + // DwordPointer object is swapping written bytes + // H264SourcePointer_ removes preventing start-code bytes + + // reset pointer(s) + pSrc = pSource; + pDst = pDestination; + + // first two bytes + i = 0; + while (i < (mfxU32) MSDK_MIN(2, nSrcSize)) + { + pDst = (mfxU8) pSrc; + ++pDst; + ++pSrc; + ++i; + } + + // do swapping + while (i < (mfxU32) nSrcSize) + { + if (false == pSrc.IsPrevent()) + { + pDst = (mfxU8) pSrc; + ++pDst; + } + ++pSrc; + ++i; + } + + // write padding bytes + nDstSize = nSrcSize - pSrc.GetRemovedBytes(); + while (nDstSize & 3) + { + pDst = (mfxU8) (0); + ++nDstSize; + ++pDst; + } +} + +} // namespace ProtectedLibrary diff --git a/vshampor/deshuffler/sample_common/src/avc_spl.cpp b/vshampor/deshuffler/sample_common/src/avc_spl.cpp new file mode 100644 index 0000000..9e0366b --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/avc_spl.cpp @@ -0,0 +1,815 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + + +#include +#include + +#include "avc_spl.h" +#include "sample_defs.h" + +namespace ProtectedLibrary +{ + +AVCFrameInfo::AVCFrameInfo() +{ + Reset(); +} + +void AVCFrameInfo::Reset() +{ + m_slice = 0; + m_index = 0; +} + +AVC_Spl::AVC_Spl() + : m_WaitForIDR(true) + , m_currentInfo(0) + , m_pLastSlice(0) + , m_lastNalUnit(0) +{ + Init(); +} + +AVC_Spl::~AVC_Spl() +{ + Close(); +} + +mfxStatus AVC_Spl::Init() +{ + Close(); + + m_pNALSplitter.reset(new NALUnitSplitter()); + m_pNALSplitter->Init(); + + m_WaitForIDR = true; + + m_AUInfo.reset(new AVCFrameInfo()); + + m_currentFrame.resize(BUFFER_SIZE); + + m_pLastSlice = 0; + m_lastNalUnit = 0; + m_currentInfo = 0; + + m_slices.resize(128); + memset(&m_frame, 0, sizeof(m_frame)); + m_frame.Data = &m_currentFrame[0]; + m_frame.Slice = &m_slices[0]; + + return MFX_ERR_NONE; +} + +void AVC_Spl::Close() +{ + m_pLastSlice = 0; + m_lastNalUnit = 0; + m_currentInfo = 0; +} + +mfxStatus AVC_Spl::Reset() +{ + m_pNALSplitter->Reset(); + m_WaitForIDR = true; + m_lastNalUnit = 0; + m_pLastSlice = 0; + m_currentInfo = 0; + return MFX_ERR_NONE; +} + +mfxU8 * AVC_Spl::GetMemoryForSwapping(mfxU32 size) +{ + if (m_swappingMemory.size() <= size + 8) + m_swappingMemory.resize(size + 8); + + return &(m_swappingMemory[0]); +} + +mfxStatus AVC_Spl::DecodeHeader(mfxBitstream * nalUnit) +{ + mfxStatus umcRes = MFX_ERR_NONE; + + AVCHeadersBitstream bitStream; + + try + { + mfxU32 swappingSize = nalUnit->DataLength; + mfxU8 * swappingMemory = GetMemoryForSwapping(swappingSize); + + BytesSwapper::SwapMemory(swappingMemory, swappingSize, nalUnit->Data + nalUnit->DataOffset, nalUnit->DataLength); + + bitStream.Reset(swappingMemory, swappingSize); + + NAL_Unit_Type uNALUnitType; + mfxU8 uNALStorageIDC; + + bitStream.GetNALUnitType(uNALUnitType, uNALStorageIDC); + + switch(uNALUnitType) + { + // sequence parameter set + case NAL_UT_SPS: + { + AVCSeqParamSet sps; + umcRes = bitStream.GetSequenceParamSet(&sps); + if (umcRes == MFX_ERR_NONE) + { + AVCSeqParamSet * temp = m_headers.m_SeqParams.GetHeader(sps.seq_parameter_set_id); + m_headers.m_SeqParams.AddHeader(&sps); + + // Validate the incoming bitstream's image dimensions. + temp = m_headers.m_SeqParams.GetHeader(sps.seq_parameter_set_id); + + m_pNALSplitter->SetSuggestedSize(CalculateSuggestedSize(&sps)); + + if (umcRes != MFX_ERR_NONE) + return umcRes; + } + else + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + break; + + case NAL_UT_SPS_EX: + { + AVCSeqParamSetExtension sps_ex; + umcRes = bitStream.GetSequenceParamSetExtension(&sps_ex); + + if (umcRes == MFX_ERR_NONE) + { + m_headers.m_SeqExParams.AddHeader(&sps_ex); + } + else + return umcRes; + } + break; + + // picture parameter set + case NAL_UT_PPS: + { + AVCPicParamSet pps; + // set illegal id + pps.pic_parameter_set_id = MAX_NUM_PIC_PARAM_SETS; + + // Get id + umcRes = bitStream.GetPictureParamSetPart1(&pps); + if (MFX_ERR_NONE == umcRes) + { + AVCSeqParamSet *pRefsps = m_headers.m_SeqParams.GetHeader(pps.seq_parameter_set_id); + + if (!pRefsps || pRefsps ->seq_parameter_set_id >= MAX_NUM_SEQ_PARAM_SETS) + { + pRefsps = m_headers.m_SeqParamsMvcExt.GetHeader(pps.seq_parameter_set_id); + if (!pRefsps || pRefsps->seq_parameter_set_id >= MAX_NUM_SEQ_PARAM_SETS) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + } + + // Get rest of pic param set + umcRes = bitStream.GetPictureParamSetPart2(&pps, pRefsps); + if (MFX_ERR_NONE == umcRes) + { + m_headers.m_PicParams.AddHeader(&pps); + } + + m_headers.m_SeqParams.SetCurrentID(pps.seq_parameter_set_id); + } + } + break; + + // subset sequence parameter set + case NAL_UNIT_SUBSET_SPS: + { + AVCSeqParamSet sps; + umcRes = bitStream.GetSequenceParamSet(&sps); + if (MFX_ERR_NONE != umcRes) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + // decode additional parameters + if ((AVC_PROFILE_MULTIVIEW_HIGH == sps.profile_idc) || + (AVC_PROFILE_STEREO_HIGH == sps.profile_idc)) + { + AVCSeqParamSet spsMvcExt; + AVCSeqParamSet * sps_temp = &spsMvcExt; + + *sps_temp = sps; + + m_headers.m_SeqParamsMvcExt.AddHeader(&spsMvcExt); + } + } + break; + + // decode a prefix nal unit + case NAL_UNIT_PREFIX: + { + umcRes = bitStream.GetNalUnitPrefix(&m_headers.m_nalExtension, uNALStorageIDC); + if (MFX_ERR_NONE != umcRes) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + } + break; + + default: + break; + } + } + catch(const AVC_exception & ) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + catch(...) + { + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + + return MFX_ERR_NONE; +} + +mfxStatus AVC_Spl::DecodeSEI(mfxBitstream * nalUnit) +{ + if (m_headers.m_SeqParams.GetCurrrentID() == -1) + return MFX_ERR_NONE; + + AVCHeadersBitstream bitStream; + + try + { + mfxU32 swappingSize = nalUnit->DataLength; + mfxU8 * swappingMemory = GetMemoryForSwapping(swappingSize); + + BytesSwapper::SwapMemory(swappingMemory, swappingSize, nalUnit->Data + nalUnit->DataOffset, nalUnit->DataLength); + + bitStream.Reset(swappingMemory, swappingSize); + + NAL_Unit_Type uNALUnitType; + mfxU8 uNALStorageIDC; + + bitStream.GetNALUnitType(uNALUnitType, uNALStorageIDC); + + do + { + AVCSEIPayLoad m_SEIPayLoads; + + bitStream.GetSEI(m_headers.m_SeqParams, + m_headers.m_SeqParams.GetCurrrentID(), &m_SEIPayLoads); + + if (m_SEIPayLoads.payLoadType == SEI_RESERVED) + continue; + + m_headers.m_SEIParams.AddHeader(&m_SEIPayLoads); + } while (bitStream.More_RBSP_Data()); + + } catch(...) + { + // nothing to do just catch it + } + + return MFX_ERR_NONE; +} + +AVCSlice * AVC_Spl::DecodeSliceHeader(mfxBitstream * nalUnit) +{ + m_slicesStorage.push_back(AVCSlice()); + AVCSlice *pSlice = &m_slicesStorage.back(); + + mfxU32 swappingSize = nalUnit->DataLength; + mfxU8 * swappingMemory = GetMemoryForSwapping(swappingSize); + + BytesSwapper::SwapMemory(swappingMemory, swappingSize, nalUnit->Data + nalUnit->DataOffset, nalUnit->DataLength); + + mfxI32 pps_pid = pSlice->RetrievePicParamSetNumber(swappingMemory, swappingSize); + if (pps_pid == -1) + { + return 0; + } + + AVCSEIPayLoad * spl = m_headers.m_SEIParams.GetHeader(SEI_RECOVERY_POINT_TYPE); + + if (m_WaitForIDR) + { + if (pSlice->GetSliceHeader()->slice_type != INTRASLICE && !spl) + { + return 0; + } + } + + pSlice->m_picParamSet = m_headers.m_PicParams.GetHeader(pps_pid); + if (!pSlice->m_picParamSet) + { + return 0; + } + + mfxI32 seq_parameter_set_id = pSlice->m_picParamSet->seq_parameter_set_id; + + if (NAL_UT_CODED_SLICE_EXTENSION == pSlice->GetSliceHeader()->nal_unit_type) + { + pSlice->m_seqParamSet = pSlice->m_seqParamSetMvcEx = m_headers.m_SeqParamsMvcExt.GetHeader(seq_parameter_set_id); + + if (NULL == pSlice->m_seqParamSetMvcEx) + { + return 0; + } + + m_headers.m_SeqParamsMvcExt.SetCurrentID(pSlice->m_seqParamSetMvcEx->seq_parameter_set_id); + m_headers.m_PicParams.SetCurrentID(pSlice->m_picParamSet->pic_parameter_set_id); + } + else + { + pSlice->m_seqParamSetMvcEx = m_headers.m_SeqParamsMvcExt.GetCurrentHeader(); + pSlice->m_seqParamSet = m_headers.m_SeqParams.GetHeader(seq_parameter_set_id); + + m_headers.m_SeqParams.SetCurrentID(pSlice->m_picParamSet->seq_parameter_set_id); + m_headers.m_PicParams.SetCurrentID(pSlice->m_picParamSet->pic_parameter_set_id); + } + + pSlice->m_seqParamSetEx = m_headers.m_SeqExParams.GetHeader(seq_parameter_set_id); + pSlice->m_dTime = nalUnit->TimeStamp; + + if (!pSlice->DecodeHeader(swappingMemory, swappingSize)) + { + return 0; + } + + if (spl && (pSlice->GetSliceHeader()->slice_type != INTRASLICE)) + { + m_headers.m_SEIParams.RemoveHeader(spl->GetID()); + } + + m_WaitForIDR = false; + + return pSlice; +} + + +AVCFrameInfo * AVC_Spl::GetFreeFrame() +{ + return m_AUInfo.get(); +} + +void AVC_Spl::ResetCurrentState() +{ + m_frame.DataLength = 0; + m_frame.SliceNum = 0; + m_frame.FirstFieldSliceNum = 0; + m_AUInfo->Reset(); + if (m_slicesStorage.size() > 1) + { + m_slicesStorage.erase(m_slicesStorage.begin(), --m_slicesStorage.end()); + } +} + +bool AVC_Spl::IsFieldOfOneFrame(AVCFrameInfo * frame, const AVCSliceHeader * slice1, const AVCSliceHeader *slice2) +{ + if (frame && frame->m_index) + return false; + + if ((slice1->nal_ref_idc && !slice2->nal_ref_idc) || + (!slice1->nal_ref_idc && slice2->nal_ref_idc)) + return false; + + if (slice1->field_pic_flag != slice2->field_pic_flag) + return false; + + if (slice1->bottom_field_flag == slice2->bottom_field_flag) + return false; + + return true; +} + +inline bool IsSlicesOfOneAU(const AVCSliceHeader *pOne, const AVCSliceHeader *pTwo) +{ + if (!pOne || !pTwo) + return true; + + if (pOne->view_id != pTwo->view_id) + { + if (pOne->view_id < pTwo->view_id) + { + return true; + } + + return false; + } + + // this function checks two slices are from same picture or not + // 7.4.1.2.4 part of AVC standart + + if ((pOne->frame_num != pTwo->frame_num) || + (pOne->first_mb_in_slice == pTwo->first_mb_in_slice) || + (pOne->pic_parameter_set_id != pTwo->pic_parameter_set_id) || + (pOne->field_pic_flag != pTwo->field_pic_flag) || + (pOne->bottom_field_flag != pTwo->bottom_field_flag)) + return false; + + if ((pOne->nal_ref_idc != pTwo->nal_ref_idc) && + (0 == MSDK_MIN(pOne->nal_ref_idc, pTwo->nal_ref_idc))) + return false; + + if ((pOne->pic_order_cnt_lsb != pTwo->pic_order_cnt_lsb) || + (pOne->delta_pic_order_cnt_bottom != pTwo->delta_pic_order_cnt_bottom)) + return false; + + if ((pOne->delta_pic_order_cnt[0] != pTwo->delta_pic_order_cnt[0]) || + (pOne->delta_pic_order_cnt[1] != pTwo->delta_pic_order_cnt[1])) + return false; + + if (pOne->nal_unit_type != pTwo->nal_unit_type) + { + if ((NAL_UT_IDR_SLICE == pOne->nal_unit_type) || + (NAL_UT_IDR_SLICE == pTwo->nal_unit_type)) + return false; + } + else if (NAL_UT_IDR_SLICE == pOne->nal_unit_type) + { + if (pOne->idr_pic_id != pTwo->idr_pic_id) + return false; + } + + return true; +} + + +mfxStatus AVC_Spl::AddSlice(AVCSlice * pSlice) +{ + m_pLastSlice = 0; + + if (!pSlice) + { + // complete current frame info + return MFX_ERR_NONE; + } + + if (m_currentInfo) + { + AVCSlice * pFirstFrameSlice = m_currentInfo->m_slice; + + if (pFirstFrameSlice && (false == IsSlicesOfOneAU(pFirstFrameSlice->GetSliceHeader(), pSlice->GetSliceHeader()))) + { + // complete frame + if (pSlice->IsField()) + { + if (IsFieldOfOneFrame(m_currentInfo, pFirstFrameSlice->GetSliceHeader(), pSlice->GetSliceHeader())) + { + m_currentInfo->m_index = 1; + m_currentInfo->m_slice = pSlice; + } + else + { + m_currentInfo->m_index = 0; + m_pLastSlice = pSlice; + return MFX_ERR_NONE; + } + } + else + { + m_currentInfo->m_index = 0; + m_pLastSlice = pSlice; + return MFX_ERR_NONE; + } + } + } + else + { + m_currentInfo = GetFreeFrame(); + if (!m_currentInfo) + { + m_pLastSlice = pSlice; + return MFX_ERR_NOT_ENOUGH_BUFFER; + } + m_currentInfo->m_index = 0; + } + + if (!m_currentInfo->m_slice) + m_currentInfo->m_slice = pSlice; + + return MFX_ERR_MORE_DATA; +} + +mfxStatus AVC_Spl::AddNalUnit(mfxBitstream * nalUnit) +{ + static mfxU8 start_code_prefix[] = {0, 0, 1}; + + if (m_frame.DataLength + nalUnit->DataLength + sizeof(start_code_prefix) >= BUFFER_SIZE) + return MFX_ERR_NOT_ENOUGH_BUFFER; + + MSDK_MEMCPY_BUF(m_frame.Data, m_frame.DataLength, BUFFER_SIZE, start_code_prefix, sizeof(start_code_prefix)); + MSDK_MEMCPY_BUF(m_frame.Data, m_frame.DataLength + sizeof(start_code_prefix), BUFFER_SIZE, nalUnit->Data + nalUnit->DataOffset, nalUnit->DataLength); + + m_frame.DataLength += (mfxU32)(nalUnit->DataLength + sizeof(start_code_prefix)); + + return MFX_ERR_NONE; +} + +mfxStatus AVC_Spl::AddSliceNalUnit(mfxBitstream * nalUnit, AVCSlice * slice) +{ + static mfxU8 start_code_prefix[] = {0, 0, 1}; + + mfxU32 sliceLength = (mfxU32)(nalUnit->DataLength + sizeof(start_code_prefix)); + + if (m_frame.DataLength + sliceLength >= BUFFER_SIZE) + return MFX_ERR_NOT_ENOUGH_BUFFER; + + MSDK_MEMCPY_BUF(m_frame.Data, m_frame.DataLength, BUFFER_SIZE, start_code_prefix, sizeof(start_code_prefix)); + MSDK_MEMCPY_BUF(m_frame.Data, m_frame.DataLength + sizeof(start_code_prefix), BUFFER_SIZE, nalUnit->Data + nalUnit->DataOffset, nalUnit->DataLength); + + if (!m_frame.SliceNum) + { + m_frame.TimeStamp = nalUnit->TimeStamp; + } + + m_frame.SliceNum++; + + if (m_slices.size() <= m_frame.SliceNum) + { + m_slices.resize(m_frame.SliceNum + 10); + m_frame.Slice = &m_slices[0]; + } + + SliceSplitterInfo & newSlice = m_slices[m_frame.SliceNum - 1]; + + AVCHeadersBitstream * bs = slice->GetBitStream(); + + newSlice.HeaderLength = (mfxU32)bs->BytesDecoded(); + + // add number of 003 sequence to HeaderLength + for(mfxU8 *ptr = nalUnit->Data + sizeof(start_code_prefix); ptr < nalUnit->Data + sizeof(start_code_prefix) + newSlice.HeaderLength; ptr++) + { + if (ptr[0]==0 && ptr[1]==0 && ptr[2]==3) + { + newSlice.HeaderLength++; + } + } + + newSlice.HeaderLength += sizeof(start_code_prefix) + 1; + + newSlice.DataLength = sliceLength; + newSlice.DataOffset = m_frame.DataLength; + if(IS_I_SLICE(slice->GetSliceHeader()->slice_type)) + newSlice.SliceType = TYPE_I; + else if(IS_P_SLICE(slice->GetSliceHeader()->slice_type)) + newSlice.SliceType = TYPE_P; + else if(IS_B_SLICE(slice->GetSliceHeader()->slice_type)) + newSlice.SliceType = TYPE_B; + + m_frame.DataLength += sliceLength; + + if (!m_currentInfo->m_index) + m_frame.FirstFieldSliceNum++; + + return MFX_ERR_NONE; +} + +mfxStatus AVC_Spl::ProcessNalUnit(mfxI32 nalType, mfxBitstream * nalUnit) +{ + if (!nalUnit) + return MFX_ERR_MORE_DATA; + + switch (nalType) + { + case NAL_UT_IDR_SLICE: + case NAL_UT_SLICE: + case NAL_UT_CODED_SLICE_EXTENSION: + { + AVCSlice * pSlice = DecodeSliceHeader(nalUnit); + if (pSlice) + { + mfxStatus sts = AddSlice(pSlice); + if (sts == MFX_ERR_NOT_ENOUGH_BUFFER) + { + return sts; + } + + if (!m_pLastSlice) + { + AddSliceNalUnit(nalUnit, pSlice); + } + else + { + m_lastNalUnit = nalUnit; + } + + if (sts == MFX_ERR_NONE) + { + return sts; + } + } + } + break; + + case NAL_UT_SPS: + case NAL_UT_PPS: + case NAL_UT_SPS_EX: + case NAL_UNIT_SUBSET_SPS: + case NAL_UNIT_PREFIX: + DecodeHeader(nalUnit); + AddNalUnit(nalUnit); + break; + + case NAL_UT_SEI: + DecodeSEI(nalUnit); + AddNalUnit(nalUnit); + break; + case NAL_UT_AUD: + AddNalUnit(nalUnit); + break; + + case NAL_UT_DPA: + case NAL_UT_DPB: + case NAL_UT_DPC: + case NAL_UT_FD: + case NAL_UT_UNSPECIFIED: + break; + + case NAL_END_OF_STREAM: + case NAL_END_OF_SEQ: + { + AddNalUnit(nalUnit); + } + break; + + default: + break; + }; + + return MFX_ERR_MORE_DATA; +} + +mfxStatus AVC_Spl::GetFrame(mfxBitstream * bs_in, FrameSplitterInfo ** frame) +{ + *frame = 0; + + do + { + if (m_pLastSlice) + { + AVCSlice * pSlice = m_pLastSlice; + mfxStatus sts = AddSlice(pSlice); + if(!m_lastNalUnit) + { + msdk_printf(MSDK_STRING("ERROR: m_lastNalUnit=NULL\n")); + return MFX_ERR_NULL_PTR; + } + AddSliceNalUnit(m_lastNalUnit, pSlice); + m_lastNalUnit = 0; + if (sts == MFX_ERR_NONE) + return MFX_ERR_NONE; + } + + mfxBitstream * destination=NULL; + mfxI32 nalType = m_pNALSplitter->GetNalUnits(bs_in, destination); + mfxStatus sts = ProcessNalUnit(nalType, destination); + + if (sts == MFX_ERR_NONE || (!bs_in && m_frame.SliceNum)) + { + m_currentInfo = 0; + *frame = &m_frame; + return MFX_ERR_NONE; + } + + } while (bs_in && bs_in->DataLength > MINIMAL_DATA_SIZE); + + return MFX_ERR_MORE_DATA; +} + +AVCSlice::AVCSlice() +{ + Reset(); +} + +void AVCSlice::Reset() +{ + m_picParamSet = 0; + m_seqParamSet = 0; + m_seqParamSetMvcEx = 0; + m_seqParamSetEx = 0; + m_dTime=0; + memset(&m_sliceHeader, 0, sizeof(m_sliceHeader)); +} + +AVCSliceHeader * AVCSlice::GetSliceHeader() +{ + return &m_sliceHeader; +} + +mfxI32 AVCSlice::RetrievePicParamSetNumber(mfxU8 *pSource, mfxU32 nSourceSize) +{ + if (!nSourceSize) + return -1; + + m_bitStream.Reset(pSource, nSourceSize); + + mfxStatus umcRes = MFX_ERR_NONE; + + try + { + umcRes = m_bitStream.GetNALUnitType(m_sliceHeader.nal_unit_type, m_sliceHeader.nal_ref_idc); + if (MFX_ERR_NONE != umcRes) + return false; + + // decode first part of slice header + umcRes = m_bitStream.GetSliceHeaderPart1(&m_sliceHeader); + if (MFX_ERR_NONE != umcRes) + return -1; + } catch (...) + { + return -1; + } + + return m_sliceHeader.pic_parameter_set_id; +} + +bool AVCSlice::DecodeHeader(mfxU8 *pSource, mfxU32 nSourceSize) +{ + m_bitStream.Reset(pSource, nSourceSize); + + if (!nSourceSize) + return false; + + mfxStatus umcRes = MFX_ERR_NONE; + // Locals for additional slice data to be read into, the data + // was read and saved from the first slice header of the picture, + // is not supposed to change within the picture, so can be + // discarded when read again here. + try + { + memset(&m_sliceHeader, 0, sizeof(m_sliceHeader)); + + umcRes = m_bitStream.GetNALUnitType(m_sliceHeader.nal_unit_type, m_sliceHeader.nal_ref_idc); + if (MFX_ERR_NONE != umcRes) + return false; + + // decode first part of slice header + umcRes = m_bitStream.GetSliceHeaderPart1(&m_sliceHeader); + if (MFX_ERR_NONE != umcRes) + return false; + + // decode second part of slice header + umcRes = m_bitStream.GetSliceHeaderPart2(&m_sliceHeader, + m_picParamSet, + m_seqParamSet); + if (MFX_ERR_NONE != umcRes) + return false; + + PredWeightTable m_PredWeight[2][MAX_NUM_REF_FRAMES]; + RefPicListReorderInfo ReorderInfoL0; + RefPicListReorderInfo ReorderInfoL1; + AdaptiveMarkingInfo m_AdaptiveMarkingInfo; + + // decode second part of slice header + umcRes = m_bitStream.GetSliceHeaderPart3(&m_sliceHeader, + m_PredWeight[0], + m_PredWeight[1], + &ReorderInfoL0, + &ReorderInfoL1, + &m_AdaptiveMarkingInfo, + m_picParamSet, + m_seqParamSet, + m_sliceHeader.nal_ref_idc); + if (MFX_ERR_NONE != umcRes) + return false; + + if (m_picParamSet->entropy_coding_mode) + m_bitStream.AlignPointerRight(); + } + catch(const AVC_exception & ) + { + return false; + } + catch(...) + { + return false; + } + + return (MFX_ERR_NONE == umcRes); +} + + + mfxStatus AVC_Spl::PostProcessing(FrameSplitterInfo *frame, mfxU32 sliceNum) + { + UNREFERENCED_PARAMETER(frame); + UNREFERENCED_PARAMETER(sliceNum); + return MFX_ERR_NONE; + } + +} // namespace ProtectedLibrary diff --git a/vshampor/deshuffler/sample_common/src/base_allocator.cpp b/vshampor/deshuffler/sample_common/src/base_allocator.cpp new file mode 100644 index 0000000..28e0cb7 --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/base_allocator.cpp @@ -0,0 +1,291 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include +#include +#include "base_allocator.h" +#include "vm/thread_defs.h" + +MFXFrameAllocator::MFXFrameAllocator() +{ + pthis = this; + Alloc = Alloc_; + Lock = Lock_; + Free = Free_; + Unlock = Unlock_; + GetHDL = GetHDL_; +} + +MFXFrameAllocator::~MFXFrameAllocator() +{ +} + +mfxStatus MFXFrameAllocator::Alloc_(mfxHDL pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXFrameAllocator& self = *(MFXFrameAllocator *)pthis; + + return self.AllocFrames(request, response); +} + +mfxStatus MFXFrameAllocator::Lock_(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXFrameAllocator& self = *(MFXFrameAllocator *)pthis; + + return self.LockFrame(mid, ptr); +} + +mfxStatus MFXFrameAllocator::Unlock_(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXFrameAllocator& self = *(MFXFrameAllocator *)pthis; + + return self.UnlockFrame(mid, ptr); +} + +mfxStatus MFXFrameAllocator::Free_(mfxHDL pthis, mfxFrameAllocResponse *response) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXFrameAllocator& self = *(MFXFrameAllocator *)pthis; + + return self.FreeFrames(response); +} + +mfxStatus MFXFrameAllocator::GetHDL_(mfxHDL pthis, mfxMemId mid, mfxHDL *handle) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXFrameAllocator& self = *(MFXFrameAllocator *)pthis; + + return self.GetFrameHDL(mid, handle); +} + +BaseFrameAllocator::BaseFrameAllocator() +{ + mtx.reset(new MSDKMutex()); +} + +BaseFrameAllocator::~BaseFrameAllocator() +{ +} + +mfxStatus BaseFrameAllocator::CheckRequestType(mfxFrameAllocRequest *request) +{ + if (0 == request) + return MFX_ERR_NULL_PTR; + + // check that Media SDK component is specified in request + if ((request->Type & MEMTYPE_FROM_MASK) != 0) + return MFX_ERR_NONE; + else + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus BaseFrameAllocator::AllocFrames(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) +{ + if (0 == request || 0 == response || 0 == request->NumFrameSuggested) + return MFX_ERR_MEMORY_ALLOC; + + if (MFX_ERR_NONE != CheckRequestType(request)) + return MFX_ERR_UNSUPPORTED; + + mfxStatus sts = MFX_ERR_NONE; + + if ( // External Frames + ((request->Type & MFX_MEMTYPE_EXTERNAL_FRAME) && + (request->Type & (MFX_MEMTYPE_FROM_DECODE | MFX_MEMTYPE_FROM_ENC | MFX_MEMTYPE_FROM_PAK))) + // Exception: Internal Frames for FEI ENC / PAK reconstructs + || + ((request->Type & MFX_MEMTYPE_INTERNAL_FRAME) && + (request->Type & (MFX_MEMTYPE_FROM_ENC | MFX_MEMTYPE_FROM_PAK))) + ) + { + bool foundInCache = false; + // external decoder allocations + std::list::iterator + it = m_ExtResponses.begin(), + et = m_ExtResponses.end(); + UniqueResponse checker(*response, request->Info.Width, request->Info.Height, request->Type); + for (; it != et; ++it) + { + // same decoder and same size + if (request->AllocId == it->AllocId && checker(*it)) + { + // check if enough frames were allocated + if (request->NumFrameSuggested > it->NumFrameActual) + return MFX_ERR_MEMORY_ALLOC; + + it->m_refCount++; + // return existing response + *response = (mfxFrameAllocResponse&)*it; + foundInCache = true; + } + } + + if (!foundInCache) + { + sts = AllocImpl(request, response); + if (sts == MFX_ERR_NONE) + { + response->AllocId = request->AllocId; + m_ExtResponses.push_back(UniqueResponse(*response, request->Info.Width, request->Info.Height, UniqueResponse::CropMemoryTypeToStore(request->Type))); + } + } + } + else + { + // internal allocations + + // reserve space before allocation to avoid memory leak + m_responses.push_back(mfxFrameAllocResponse()); + + sts = AllocImpl(request, response); + if (sts == MFX_ERR_NONE) + { + m_responses.back() = *response; + } + else + { + m_responses.pop_back(); + } + } + + return sts; +} + +mfxStatus BaseFrameAllocator::FreeFrames(mfxFrameAllocResponse *response) +{ + AutomaticMutex lock(*mtx); + + if (response == 0) + return MFX_ERR_INVALID_HANDLE; + + mfxStatus sts = MFX_ERR_NONE; + + // check whether response is an external decoder response + std::list::iterator i = + std::find_if( m_ExtResponses.begin(), m_ExtResponses.end(), std::bind1st(IsSame(), *response)); + + if (i != m_ExtResponses.end()) + { + if ((--i->m_refCount) == 0) + { + sts = ReleaseResponse(response); + m_ExtResponses.erase(i); + } + return sts; + } + + // if not found so far, then search in internal responses + std::list::iterator i2 = + std::find_if(m_responses.begin(), m_responses.end(), std::bind1st(IsSame(), *response)); + + if (i2 != m_responses.end()) + { + sts = ReleaseResponse(response); + m_responses.erase(i2); + return sts; + } + + // not found anywhere, report an error + return MFX_ERR_INVALID_HANDLE; +} + +mfxStatus BaseFrameAllocator::Close() +{ + AutomaticMutex lock(*mtx); + std::list ::iterator i; + for (i = m_ExtResponses.begin(); i!= m_ExtResponses.end(); i++) + { + ReleaseResponse(&*i); + } + m_ExtResponses.clear(); + + std::list ::iterator i2; + for (i2 = m_responses.begin(); i2!= m_responses.end(); i2++) + { + ReleaseResponse(&*i2); + } + + return MFX_ERR_NONE; +} + +MFXBufferAllocator::MFXBufferAllocator() +{ + pthis = this; + Alloc = Alloc_; + Lock = Lock_; + Free = Free_; + Unlock = Unlock_; +} + +MFXBufferAllocator::~MFXBufferAllocator() +{ +} + +mfxStatus MFXBufferAllocator::Alloc_(mfxHDL pthis, mfxU32 nbytes, mfxU16 type, mfxMemId *mid) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXBufferAllocator& self = *(MFXBufferAllocator *)pthis; + + return self.AllocBuffer(nbytes, type, mid); +} + +mfxStatus MFXBufferAllocator::Lock_(mfxHDL pthis, mfxMemId mid, mfxU8 **ptr) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXBufferAllocator& self = *(MFXBufferAllocator *)pthis; + + return self.LockBuffer(mid, ptr); +} + +mfxStatus MFXBufferAllocator::Unlock_(mfxHDL pthis, mfxMemId mid) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXBufferAllocator& self = *(MFXBufferAllocator *)pthis; + + return self.UnlockBuffer(mid); +} + +mfxStatus MFXBufferAllocator::Free_(mfxHDL pthis, mfxMemId mid) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXBufferAllocator& self = *(MFXBufferAllocator *)pthis; + + return self.FreeBuffer(mid); +} + diff --git a/vshampor/deshuffler/sample_common/src/brc_routines.cpp b/vshampor/deshuffler/sample_common/src/brc_routines.cpp new file mode 100644 index 0000000..90231dd --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/brc_routines.cpp @@ -0,0 +1,1004 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + + +#include "brc_routines.h" +#include "math.h" +#include "mfxdefs.h" +#include + +#ifndef MFX_VERSION +#error MFX_VERSION not defined +#endif + +#if (MFX_VERSION >= 1024) +#define Saturate(min_val, max_val, val) IPP_MAX((min_val), IPP_MIN((max_val), (val))) +#define BRC_SCENE_CHANGE_RATIO1 20.0 +#define BRC_SCENE_CHANGE_RATIO2 5.0 + +#define IPP_MAX( a, b ) ( ((a) > (b)) ? (a) : (b) ) +#define IPP_MIN( a, b ) ( ((a) < (b)) ? (a) : (b) ) + + +mfxExtBuffer* Hevc_GetExtBuffer(mfxExtBuffer** extBuf, mfxU32 numExtBuf, mfxU32 id) +{ + if (extBuf != 0) + { + for (mfxU16 i = 0; i < numExtBuf; i++) + { + if (extBuf[i] != 0 && extBuf[i]->BufferId == id) // assuming aligned buffers + return (extBuf[i]); + } + } + + return 0; +} + +mfxStatus cBRCParams::Init(mfxVideoParam* par, bool bFielMode) +{ + printf("Sample BRC is used\n"); + MFX_CHECK_NULL_PTR1(par); + MFX_CHECK(par->mfx.RateControlMethod == MFX_RATECONTROL_CBR || + par->mfx.RateControlMethod == MFX_RATECONTROL_VBR, + MFX_ERR_UNDEFINED_BEHAVIOR); + + mfxU32 k = par->mfx.BRCParamMultiplier == 0 ? 1: par->mfx.BRCParamMultiplier; + mfxU32 bpsScale = (par->mfx.CodecId == MFX_CODEC_AVC) ? 10 : 6; + + rateControlMethod = par->mfx.RateControlMethod; + targetbps = (((k*par->mfx.TargetKbps*1000) >> bpsScale) << bpsScale); + maxbps = (((k*par->mfx.MaxKbps*1000) >> bpsScale) << bpsScale); + + maxbps = (par->mfx.RateControlMethod == MFX_RATECONTROL_CBR) ? + targetbps : ((maxbps >= targetbps) ? maxbps : targetbps); + mfxExtCodingOption * pExtCO = (mfxExtCodingOption*)Hevc_GetExtBuffer(par->ExtParam, par->NumExtParam, MFX_EXTBUFF_CODING_OPTION); + + bHRDConformance = (pExtCO && pExtCO->NalHrdConformance == MFX_CODINGOPTION_OFF) ? 0 : 1; + + if (bHRDConformance) + { + bufferSizeInBytes = ((k*par->mfx.BufferSizeInKB*1000) >> 3) << 3; + initialDelayInBytes =((k*par->mfx.InitialDelayInKB*1000) >> 3) << 3; + bRec = 1; + bPanic = 1; + } + MFX_CHECK (par->mfx.FrameInfo.FrameRateExtD != 0 && + par->mfx.FrameInfo.FrameRateExtN != 0, + MFX_ERR_UNDEFINED_BEHAVIOR); + + frameRate = (mfxF64)par->mfx.FrameInfo.FrameRateExtN / (mfxF64)par->mfx.FrameInfo.FrameRateExtD; + + width = par->mfx.FrameInfo.Width; + height =par->mfx.FrameInfo.Height; + + chromaFormat = par->mfx.FrameInfo.ChromaFormat == 0 ? MFX_CHROMAFORMAT_YUV420 : par->mfx.FrameInfo.ChromaFormat ; + bitDepthLuma = par->mfx.FrameInfo.BitDepthLuma == 0 ? 8 : par->mfx.FrameInfo.BitDepthLuma; + quantOffset = 6 * (bitDepthLuma - 8); + + inputBitsPerFrame = targetbps / frameRate; + maxInputBitsPerFrame = maxbps / frameRate; + gopPicSize = par->mfx.GopPicSize*(bFielMode ? 2 : 1); + gopRefDist = par->mfx.GopRefDist*(bFielMode ? 2 : 1); + + mfxExtCodingOption2 * pExtCO2 = (mfxExtCodingOption2*)Hevc_GetExtBuffer(par->ExtParam, par->NumExtParam, MFX_EXTBUFF_CODING_OPTION2); + bPyr = (pExtCO2 && pExtCO2->BRefType == MFX_B_REF_PYRAMID); + maxFrameSizeInBits = pExtCO2 ? pExtCO2->MaxFrameSize*8 : 0; + fAbPeriodLong = 100; + fAbPeriodShort = 5; + dqAbPeriod = 100; + bAbPeriod = 100; + + if (maxFrameSizeInBits) + { + bRec = 1; + bPanic = 1; + } + + if (pExtCO2 + && pExtCO2->MaxQPI <=51 && pExtCO2->MaxQPI > pExtCO2->MinQPI && pExtCO2->MinQPI >=1 + && pExtCO2->MaxQPP <=51 && pExtCO2->MaxQPP > pExtCO2->MinQPP && pExtCO2->MinQPP >=1 + && pExtCO2->MaxQPB <=51 && pExtCO2->MaxQPB > pExtCO2->MinQPB && pExtCO2->MinQPB >=1 ) + { + quantMaxI = pExtCO2->MaxQPI + quantOffset; + quantMinI = pExtCO2->MinQPI; + quantMaxP = pExtCO2->MaxQPP + quantOffset; + quantMinP = pExtCO2->MinQPP; + quantMaxB = pExtCO2->MaxQPB + quantOffset; + quantMinB = pExtCO2->MinQPB; + } + else + { + quantMaxI = quantMaxP = quantMaxB = 51 + quantOffset; + quantMinI = quantMinP = quantMinB = 1; + } + + + + mfxExtCodingOption3 * pExtCO3 = (mfxExtCodingOption3*)Hevc_GetExtBuffer(par->ExtParam, par->NumExtParam, MFX_EXTBUFF_CODING_OPTION3); + if (pExtCO3) + { + WinBRCMaxAvgKbps = pExtCO3->WinBRCMaxAvgKbps*par->mfx.BRCParamMultiplier; + WinBRCSize = pExtCO3->WinBRCSize; + } + return MFX_ERR_NONE; +} + +mfxStatus cBRCParams::GetBRCResetType(mfxVideoParam* par, bool bNewSequence, bool &bBRCReset, bool &bSlidingWindowReset) +{ + bBRCReset = false; + bSlidingWindowReset = false; + + if (bNewSequence) + return MFX_ERR_NONE; + + cBRCParams new_par; + mfxStatus sts = new_par.Init(par); + MFX_CHECK_STS(sts); + + MFX_CHECK(new_par.rateControlMethod == rateControlMethod, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM) ; + MFX_CHECK(new_par.bHRDConformance == bHRDConformance, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM) ; + MFX_CHECK(new_par.frameRate == frameRate, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM); + MFX_CHECK(new_par.width == width, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM); + MFX_CHECK(new_par.height == height, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM); + MFX_CHECK(new_par.chromaFormat == chromaFormat, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM); + MFX_CHECK(new_par.bitDepthLuma == bitDepthLuma, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM); + + if (bHRDConformance) + { + MFX_CHECK(new_par.bufferSizeInBytes == bufferSizeInBytes, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM); + MFX_CHECK(new_par.initialDelayInBytes == initialDelayInBytes, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM); + MFX_CHECK(new_par.targetbps == targetbps, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM); + MFX_CHECK(new_par.maxbps == maxbps, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM); + } + else + { + if (new_par.targetbps != targetbps || new_par.maxbps != maxbps) + { + MFX_CHECK(new_par.rateControlMethod == MFX_RATECONTROL_VBR, MFX_ERR_INCOMPATIBLE_VIDEO_PARAM); + bBRCReset = true; + } + } + + if (new_par.WinBRCMaxAvgKbps != WinBRCMaxAvgKbps) + { + bBRCReset = true; + bSlidingWindowReset = true; + } + + if (new_par.maxFrameSizeInBits != maxFrameSizeInBits) bBRCReset = true; + if (new_par.gopPicSize != gopPicSize) bBRCReset = true; + if (new_par.gopRefDist != gopRefDist) bBRCReset = true; + if (new_par.bPyr != bPyr) bBRCReset = true; + if (new_par.quantMaxI != quantMaxI) bBRCReset = true; + if (new_par.quantMinI != quantMinI) bBRCReset = true; + if (new_par.quantMaxP != quantMaxP) bBRCReset = true; + if (new_par.quantMinP != quantMinP) bBRCReset = true; + if (new_par.quantMaxB != quantMaxB) bBRCReset = true; + if (new_par.quantMinB != quantMinB) bBRCReset = true; + + return MFX_ERR_NONE; +} + + + +enum +{ + MFX_BRC_RECODE_NONE = 0, + MFX_BRC_RECODE_QP = 1, + MFX_BRC_RECODE_PANIC = 2, +}; + + mfxF64 const QSTEP[88] = { + 0.630, 0.707, 0.794, 0.891, 1.000, 1.122, 1.260, 1.414, 1.587, 1.782, 2.000, 2.245, 2.520, + 2.828, 3.175, 3.564, 4.000, 4.490, 5.040, 5.657, 6.350, 7.127, 8.000, 8.980, 10.079, 11.314, + 12.699, 14.254, 16.000, 17.959, 20.159, 22.627, 25.398, 28.509, 32.000, 35.919, 40.317, 45.255, 50.797, + 57.018, 64.000, 71.838, 80.635, 90.510, 101.594, 114.035, 128.000, 143.675, 161.270, 181.019, 203.187, 228.070, + 256.000, 287.350, 322.540, 362.039, 406.375, 456.140, 512.000, 574.701, 645.080, 724.077, 812.749, 912.280, + 1024.000, 1149.401, 1290.159, 1448.155, 1625.499, 1824.561, 2048.000, 2298.802, 2580.318, 2896.309, 3250.997, 3649.121, + 4096.000, 4597.605, 5160.637, 5792.619, 6501.995, 7298.242, 8192.000, 9195.209, 10321.273, 11585.238, 13003.989, 14596.485 + }; + + +mfxI32 QStep2QpFloor(mfxF64 qstep, mfxI32 qpoffset = 0) // QSTEP[qp] <= qstep, return 0<=qp<=51+mQuantOffset +{ + mfxU8 qp = mfxU8(std::upper_bound(QSTEP, QSTEP + 51 + qpoffset, qstep) - QSTEP); + return qp > 0 ? qp - 1 : 0; +} + +mfxI32 Qstep2QP(mfxF64 qstep, mfxI32 qpoffset = 0) // return 0<=qp<=51+mQuantOffset +{ + mfxI32 qp = QStep2QpFloor(qstep, qpoffset); + + // prevent going QSTEP index out of bounds + if (qp >= (mfxI32)(sizeof(QSTEP)/sizeof(mfxF64)) - 1) + return 0; + return (qp == 51 + qpoffset || qstep < (QSTEP[qp] + QSTEP[qp + 1]) / 2) ? qp : qp + 1; +} +mfxF64 QP2Qstep(mfxI32 qp, mfxI32 qpoffset = 0) +{ + return QSTEP[IPP_MIN(51 + qpoffset, qp)]; +} +#define BRC_CLIP(val, minval, maxval) val = Saturate(minval, maxval, val) + + + +mfxF64 cHRD::GetBufferDiviation(mfxU32 targetBitrate) +{ + mfxI64 targetFullness = IPP_MIN(m_delayInBits, m_buffSizeInBits / 2); + mfxI64 minTargetFullness = IPP_MIN(mfxU32(m_buffSizeInBits / 2),targetBitrate * 2); // half bufsize or 2 sec + targetFullness = IPP_MAX(targetFullness , minTargetFullness); + return targetFullness - m_bufFullness; +} + +mfxU16 cHRD::UpdateAndCheckHRD(mfxI32 frameBits, mfxI32 recode, mfxI32 minQuant, mfxI32 maxQuant) +{ + mfxU16 brcStatus = MFX_BRC_OK ; + + if (recode == 0) + { + m_prevBufFullness = m_bufFullness; + m_underflowQuant = minQuant - 1; + m_overflowQuant = maxQuant + 1; + } + else + { // frame is being recoded - restore buffer state + m_bufFullness = m_prevBufFullness; + m_frameNum--; + } + + m_maxFrameSize = (mfxI32)(m_bufFullness - 1); + m_minFrameSize = (!m_bCBR)? 0 : (mfxI32)(m_bufFullness + 1 + 1 + m_inputBitsPerFrame - m_buffSizeInBits); + if (m_minFrameSize < 0) + m_minFrameSize = 0; + + mfxF64 bufFullness = m_bufFullness - frameBits; + + if (bufFullness < 2) + { + bufFullness = m_inputBitsPerFrame; + brcStatus = MFX_BRC_BIG_FRAME; + if (bufFullness > m_buffSizeInBits) + bufFullness = m_buffSizeInBits; + } + else + { + bufFullness += m_inputBitsPerFrame; + if (bufFullness > m_buffSizeInBits - 1) + { + bufFullness = m_buffSizeInBits - 1; + if (m_bCBR) + brcStatus = MFX_BRC_SMALL_FRAME; + } + } + m_frameNum++; + if ( MFX_BRC_RECODE_PANIC == recode) // no use in changing QP + { + if (brcStatus == MFX_BRC_SMALL_FRAME) + brcStatus = MFX_BRC_PANIC_SMALL_FRAME ; + if (brcStatus == MFX_BRC_BIG_FRAME) + brcStatus = MFX_BRC_PANIC_BIG_FRAME ; } + + m_bufFullness = bufFullness; + return brcStatus; +} + +mfxStatus cHRD::UpdateMinMaxQPForRec( mfxU32 brcSts, mfxI32 qp) +{ + MFX_CHECK(brcSts == MFX_BRC_BIG_FRAME || brcSts == MFX_BRC_SMALL_FRAME, MFX_ERR_UNDEFINED_BEHAVIOR); + if (brcSts == MFX_BRC_BIG_FRAME) + m_underflowQuant = qp; + else + m_overflowQuant = qp; + return MFX_ERR_NONE; +} +mfxI32 cHRD::GetTargetSize(mfxU32 brcSts) +{ + if (brcSts != MFX_BRC_BIG_FRAME && brcSts != MFX_BRC_SMALL_FRAME) return 0; + return (brcSts == MFX_BRC_BIG_FRAME) ? m_maxFrameSize * 3 / 4 : m_minFrameSize * 5 / 4; +} + +mfxI32 GetNewQP(mfxF64 totalFrameBits, mfxF64 targetFrameSizeInBits, mfxI32 minQP , mfxI32 maxQP, mfxI32 qp , mfxI32 qp_offset, mfxF64 f_pow, bool bStrict = false, bool bLim = true) +{ + mfxF64 qstep = 0, qstep_new = 0; + mfxI32 qp_new = qp; + + qstep = QP2Qstep(qp, qp_offset); + qstep_new = qstep * pow(totalFrameBits / targetFrameSizeInBits, f_pow); + qp_new = Qstep2QP(qstep_new, qp_offset); + + if (totalFrameBits < targetFrameSizeInBits) // overflow + { + if (qp <= minQP) + { + return qp; // QP change is impossible + } + if (bLim) + qp_new = IPP_MAX (qp_new , (minQP + qp + 1) >> 1); + if (bStrict) + qp_new = IPP_MIN (qp_new, qp - 1); + } + else // underflow + { + if (qp >= maxQP) + { + return qp; // QP change is impossible + } + if (bLim) + qp_new = IPP_MIN (qp_new , (maxQP + qp + 1) >> 1); + if (bStrict) + qp_new = IPP_MAX (qp_new, qp + 1); + } + return BRC_CLIP(qp_new, minQP, maxQP); +} + + + + +void cHRD::Init(mfxU32 buffSizeInBytes, mfxU32 delayInBytes, mfxF64 inputBitsPerFrame, bool bCBR) +{ + m_bufFullness = m_prevBufFullness= delayInBytes << 3; + m_delayInBits = delayInBytes << 3; + m_buffSizeInBits = buffSizeInBytes << 3; + m_inputBitsPerFrame =inputBitsPerFrame; + m_bCBR = bCBR; + + m_underflowQuant = 0; + m_overflowQuant = 999; + m_frameNum = 0; + m_minFrameSize = 0; + m_maxFrameSize = 0; + +} + +void UpdateQPParams(mfxI32 qp, mfxU32 type , BRC_Ctx &ctx, mfxU32 /* rec_num */, mfxI32 minQuant, mfxI32 maxQuant, mfxU32 level) +{ + ctx.Quant = qp; + if (type == MFX_FRAMETYPE_I) + { + ctx.QuantI = qp; + ctx.QuantP = qp + 1; + ctx.QuantB = qp + 2; + } + else if (type == MFX_FRAMETYPE_P) + { + qp -= level; + ctx.QuantI = qp - 1; + ctx.QuantP = qp; + ctx.QuantB = qp + 1; + } + else if (type == MFX_FRAMETYPE_B) + { + level = level > 0 ? level - 1: 0; + qp -= level; + ctx.QuantI = qp - 2; + ctx.QuantP = qp - 1; + ctx.QuantB = qp; + } + BRC_CLIP(ctx.QuantI, minQuant, maxQuant); + BRC_CLIP(ctx.QuantP, minQuant, maxQuant); + BRC_CLIP(ctx.QuantB, minQuant, maxQuant); + //printf("ctx.QuantI %d, ctx.QuantP %d, ctx.QuantB %d, level %d\n", ctx.QuantI, ctx.QuantP, ctx.QuantB, level); +} + +mfxI32 GetRawFrameSize(mfxU32 lumaSize, mfxU16 chromaFormat, mfxU16 bitDepthLuma) +{ + mfxI32 frameSize = lumaSize; + + if (chromaFormat == MFX_CHROMAFORMAT_YUV420) + frameSize += lumaSize / 2; + else if (chromaFormat == MFX_CHROMAFORMAT_YUV422) + frameSize += lumaSize; + else if (chromaFormat == MFX_CHROMAFORMAT_YUV444) + frameSize += lumaSize * 2; + + frameSize = frameSize * bitDepthLuma / 8; + return frameSize*8; //frame size in bits +} + +mfxStatus ExtBRC::Init (mfxVideoParam* par) +{ + mfxStatus sts = MFX_ERR_NONE; + + MFX_CHECK(!m_bInit, MFX_ERR_UNDEFINED_BEHAVIOR); + sts = m_par.Init(par); + MFX_CHECK_STS(sts); + + if (m_par.bHRDConformance) + { + m_hrd.Init(m_par.bufferSizeInBytes, m_par.initialDelayInBytes, m_par.maxInputBitsPerFrame, m_par.rateControlMethod == MFX_RATECONTROL_CBR); + } + memset(&m_ctx, 0, sizeof(m_ctx)); + + m_ctx.fAbLong = m_par.inputBitsPerFrame; + m_ctx.fAbShort = m_par.inputBitsPerFrame; + m_ctx.encOrder = mfxU32(-1); + + mfxI32 rawSize = GetRawFrameSize(m_par.width * m_par.height ,m_par.chromaFormat, m_par.bitDepthLuma); + mfxI32 qp = GetNewQP(rawSize, m_par.inputBitsPerFrame, m_par.quantMinI, m_par.quantMaxI, 1 , m_par.quantOffset, 0.5, false, false); + + UpdateQPParams(qp,MFX_FRAMETYPE_I , m_ctx, 0, m_par.quantMinI, m_par.quantMaxI, 0); + + m_ctx.dQuantAb = qp > 0 ? 1./qp : 1.0; //kw + + if (m_par.WinBRCSize) + { + m_avg.reset(new AVGBitrate(m_par.WinBRCSize, (mfxU32)(m_par.WinBRCMaxAvgKbps*1000.0/m_par.frameRate), (mfxU32)m_par.inputBitsPerFrame) ); + MFX_CHECK_NULL_PTR1(m_avg.get()); + } + + m_bInit = true; + return sts; +} + +mfxU16 GetFrameType(mfxU16 m_frameType, mfxU16 level, mfxU16 gopRegDist) +{ + if (m_frameType & MFX_FRAMETYPE_IDR) + return MFX_FRAMETYPE_I; + else if (m_frameType & MFX_FRAMETYPE_I) + return MFX_FRAMETYPE_I; + else if (m_frameType & MFX_FRAMETYPE_P) + return MFX_FRAMETYPE_P; + else if ((m_frameType & MFX_FRAMETYPE_REF) && (level == 0 || gopRegDist == 1)) + return MFX_FRAMETYPE_P; //low delay B + else + return MFX_FRAMETYPE_B; +} + + +bool isFrameBeforeIntra (mfxU32 order, mfxU32 intraOrder, mfxU32 gopPicSize, mfxU32 gopRefDist) + { + mfxI32 distance0 = gopPicSize*3/4; + mfxI32 distance1 = gopPicSize - gopRefDist*3; + return (order - intraOrder) > (mfxU32)(IPP_MAX(distance0, distance1)); + } +mfxStatus SetRecodeParams(mfxU16 brcStatus, mfxI32 qp, mfxI32 qp_new, mfxI32 minQP, mfxI32 maxQP, BRC_Ctx &ctx, mfxBRCFrameStatus* status) +{ + ctx.bToRecode = 1; + + if (brcStatus == MFX_BRC_BIG_FRAME || brcStatus == MFX_BRC_PANIC_BIG_FRAME ) + { + MFX_CHECK(qp_new >= qp, MFX_ERR_UNDEFINED_BEHAVIOR); + ctx.Quant = qp_new; + ctx.QuantMax = maxQP; + if (brcStatus == MFX_BRC_BIG_FRAME && qp_new > qp) + { + ctx.QuantMin = IPP_MAX(qp + 1, minQP); //limit QP range for recoding + status->BRCStatus = MFX_BRC_BIG_FRAME; + + } + else + { + ctx.QuantMin = minQP; + ctx.bPanic = 1; + status->BRCStatus = MFX_BRC_PANIC_BIG_FRAME; + } + } + else if (brcStatus == MFX_BRC_SMALL_FRAME || brcStatus == MFX_BRC_PANIC_SMALL_FRAME) + { + MFX_CHECK(qp_new <= qp, MFX_ERR_UNDEFINED_BEHAVIOR); + + ctx.Quant = qp_new; + ctx.QuantMin = minQP; //limit QP range for recoding + + if (brcStatus == MFX_BRC_SMALL_FRAME && qp_new < qp) + { + ctx.QuantMax = IPP_MIN (qp - 1, maxQP); + status->BRCStatus = MFX_BRC_SMALL_FRAME; + } + else + { + ctx.QuantMax = maxQP; + status->BRCStatus = MFX_BRC_PANIC_SMALL_FRAME; + ctx.bPanic = 1; + } + } + //printf("recode %d , qp %d new %d, status %d\n", ctx.encOrder, qp, qp_new, status->BRCStatus); + return MFX_ERR_NONE; +} +mfxI32 GetNewQPTotal(mfxF64 bo, mfxF64 dQP, mfxI32 minQP , mfxI32 maxQP, mfxI32 qp, bool bPyr, bool bSC) +{ + mfxU8 mode = (!bPyr) ; + + BRC_CLIP(bo, -1.0, 1.0); + BRC_CLIP(dQP, 1./maxQP, 1./minQP); + dQP = dQP + (1./maxQP - dQP) * bo; + BRC_CLIP(dQP, 1./maxQP, 1./minQP); + mfxI32 quant_new = (mfxI32) (1. / dQP + 0.5); + + //printf(" GetNewQPTotal: bo %f, quant %d, quant_new %d, mode %d\n", bo, qp, quant_new, mode); + if (!bSC) + { + if (mode == 0) // low: qp_diff [-2; 2] + { + if (quant_new >= qp + 5) + quant_new = qp + 2; + else if (quant_new > qp + 3) + quant_new = qp + 1; + else if (quant_new <= qp - 5) + quant_new = qp - 2; + else if (quant_new < qp - 2) + quant_new = qp - 1; + } + else // (mode == 1) midle: qp_diff [-3; 3] + { + if (quant_new >= qp + 5) + quant_new = qp + 3; + else if (quant_new > qp + 3) + quant_new = qp + 2; + else if (quant_new <= qp - 5) + quant_new = qp - 3; + else if (quant_new < qp - 2) + quant_new = qp - 2; + } + + } + else + { + BRC_CLIP (quant_new, qp - 5, qp + 5); + } + return BRC_CLIP (quant_new, minQP, maxQP); +} +// Reduce AB period before intra and increase it after intra (to avoid intra frame affect on the bottom of hrd) +mfxF64 GetAbPeriodCoeff (mfxU32 numInGop, mfxU32 gopPicSize) +{ + const mfxU32 maxForCorrection = 30; + const mfxF64 maxValue = 1.5; + const mfxF64 minValue = 1.0; + + mfxU32 numForCorrection = IPP_MIN (gopPicSize /2, maxForCorrection); + mfxF64 k[maxForCorrection] = {0}; + + if (numInGop >= gopPicSize || gopPicSize < 2) + return 1.0; + + for (mfxU32 i = 0; i < numForCorrection; i ++) + { + k[i] = maxValue - (maxValue - minValue)*i/numForCorrection; + } + if (numInGop < gopPicSize/2) + { + return k [numInGop < numForCorrection ? numInGop : numForCorrection - 1]; + } + else + { + mfxU32 n = gopPicSize - 1 - numInGop; + return 1.0/ k[n < numForCorrection ? n : numForCorrection - 1]; + } + +} + +mfxI32 ExtBRC::GetCurQP (mfxU32 type, mfxI32 layer) +{ + mfxI32 qp = 0; + if (type == MFX_FRAMETYPE_I) + { + qp = m_ctx.QuantI; + BRC_CLIP(qp, m_par.quantMinI, m_par.quantMaxI); + } + else if (type == MFX_FRAMETYPE_P) + { + qp = m_ctx.QuantP + layer; + BRC_CLIP(qp, m_par.quantMinP, m_par.quantMaxP); + } + else + { + qp = m_ctx.QuantB + (layer > 0 ? layer - 1 : 0); + BRC_CLIP(qp, m_par.quantMinB, m_par.quantMaxB); + } + //printf("GetCurQP I %d P %d B %d, min %d max %d type %d \n", m_ctx.QuantI, m_ctx.QuantP, m_ctx.QuantB, m_par.quantMinI, m_par.quantMaxI, type); + + return qp; +} + +mfxStatus ExtBRC::Update(mfxBRCFrameParam* frame_par, mfxBRCFrameCtrl* frame_ctrl, mfxBRCFrameStatus* status) +{ + mfxStatus sts = MFX_ERR_NONE; + + MFX_CHECK_NULL_PTR3(frame_par, frame_ctrl, status); + MFX_CHECK(m_bInit, MFX_ERR_NOT_INITIALIZED); + + mfxU16 &brcSts = status->BRCStatus; + status->MinFrameSize = 0; + + //printf("ExtBRC::Update: m_ctx.encOrder %d , frame_par->EncodedOrder %d, frame_par->NumRecode %d, frame_par->CodedFrameSize %d, qp %d\n", m_ctx.encOrder , frame_par->EncodedOrder, frame_par->NumRecode, frame_par->CodedFrameSize, frame_ctrl->QpY); + + mfxI32 bitsEncoded = frame_par->CodedFrameSize*8; + mfxU32 picType = GetFrameType(frame_par->FrameType, frame_par->PyramidLayer, m_par.gopRefDist); + mfxI32 qpY = frame_ctrl->QpY + m_par.quantOffset; + mfxI32 layer = frame_par->PyramidLayer; + mfxF64 qstep = QP2Qstep(qpY, m_par.quantOffset); + + mfxF64 fAbLong = m_ctx.fAbLong + (bitsEncoded - m_ctx.fAbLong) / m_par.fAbPeriodLong; + mfxF64 fAbShort = m_ctx.fAbShort + (bitsEncoded - m_ctx.fAbShort) / m_par.fAbPeriodShort; + mfxF64 eRate = bitsEncoded * sqrt(qstep); + mfxF64 e2pe = 0; + bool bMaxFrameSizeMode = m_par.maxFrameSizeInBits != 0 && + m_par.rateControlMethod == MFX_RATECONTROL_VBR && + m_par.maxFrameSizeInBits < m_par.inputBitsPerFrame * 2 && + m_ctx.totalDiviation < (-1)*m_par.inputBitsPerFrame*m_par.frameRate; + + if (picType == MFX_FRAMETYPE_I) + e2pe = (m_ctx.eRateSH == 0) ? (BRC_SCENE_CHANGE_RATIO2 + 1) : eRate / m_ctx.eRateSH; + else + e2pe = (m_ctx.eRate == 0) ? (BRC_SCENE_CHANGE_RATIO2 + 1) : eRate / m_ctx.eRate; + + mfxU32 frameSizeLim = 0xfffffff ; // sliding window limitation or external frame size limitation + + bool bSHStart = false; + bool bNeedUpdateQP = false; + + brcSts = MFX_BRC_OK; + + if (m_par.bRec && m_ctx.bToRecode && (m_ctx.encOrder != frame_par->EncodedOrder || frame_par->NumRecode == 0)) + { + //printf("++++++++++++++++++++++++++++++++++\n"); + // Frame must be recoded, but encoder calls BR for another frame + return MFX_ERR_UNDEFINED_BEHAVIOR; + } + if (frame_par->NumRecode == 0 || m_ctx.encOrder != frame_par->EncodedOrder) + { + // Set context for new frame + if (picType == MFX_FRAMETYPE_I) + m_ctx.LastIEncOrder = frame_par->EncodedOrder; + m_ctx.encOrder = frame_par->EncodedOrder; + m_ctx.poc = frame_par->DisplayOrder; + m_ctx.bToRecode = 0; + m_ctx.bPanic = 0; + + if (picType == MFX_FRAMETYPE_I) + { + m_ctx.QuantMin = m_par.quantMinI; + m_ctx.QuantMax = m_par.quantMaxI; + } + else if (picType == MFX_FRAMETYPE_P) + { + m_ctx.QuantMin = m_par.quantMinP; + m_ctx.QuantMax = m_par.quantMaxP; + } + else + { + m_ctx.QuantMin = m_par.quantMinB; + m_ctx.QuantMax = m_par.quantMaxB; + } + m_ctx.Quant = qpY; + + + if (m_ctx.SceneChange && ( m_ctx.poc > m_ctx.SChPoc + 1 || m_ctx.poc == 0)) + m_ctx.SceneChange &= ~16; + + bNeedUpdateQP = true; + + //printf("m_ctx.SceneChange %d, m_ctx.poc %d, m_ctx.SChPoc, m_ctx.poc %d \n", m_ctx.SceneChange, m_ctx.poc, m_ctx.SChPoc, m_ctx.poc); + } + if (e2pe > BRC_SCENE_CHANGE_RATIO2 ) + { + // scene change, resetting BRC statistics + fAbLong = m_ctx.fAbLong = m_par.inputBitsPerFrame; + fAbShort = m_ctx.fAbShort = m_par.inputBitsPerFrame; + m_ctx.SceneChange |= 1; + if (picType != MFX_FRAMETYPE_B) + { + bSHStart = true; + m_ctx.SceneChange |= 16; + m_ctx.eRateSH = eRate; + //if ((frame_par->DisplayOrder - m_ctx.SChPoc) >= IPP_MIN((mfxU32)(m_par.frameRate), m_par.gopRefDist)) + { + m_ctx.dQuantAb = 1./m_ctx.Quant; + } + m_ctx.SChPoc = frame_par->DisplayOrder; + //printf("!!!!!!!!!!!!!!!!!!!!! %d m_ctx.SceneChange %d, order %d\n", frame_par->EncodedOrder, m_ctx.SceneChange, frame_par->DisplayOrder); + } + + } + if (m_par.bHRDConformance) + { + //check hrd + brcSts = m_hrd.UpdateAndCheckHRD(bitsEncoded,frame_par->NumRecode, m_ctx.QuantMin,m_ctx.QuantMax); + //printf("--UpdateAndCheckHRD (%d) brcSts %d, panic %d\n", frame_par->EncodedOrder,brcSts, m_ctx.bPanic); + MFX_CHECK(brcSts == MFX_BRC_OK || (!m_ctx.bPanic), MFX_ERR_NOT_ENOUGH_BUFFER); + if (brcSts == MFX_BRC_BIG_FRAME || brcSts == MFX_BRC_SMALL_FRAME) + m_hrd.UpdateMinMaxQPForRec(brcSts, qpY); + else + bNeedUpdateQP = true; + status->MinFrameSize = m_hrd.GetMinFrameSize() + 7; + //printf("%d: poc %d, size %d QP %d (%d %d), HRD sts %d, maxFrameSize %d, type %d \n",frame_par->EncodedOrder, frame_par->DisplayOrder, bitsEncoded, m_ctx.Quant, m_ctx.QuantMin, m_ctx.QuantMax, brcSts, m_hrd.GetMaxFrameSize(), frame_par->FrameType); + } + if (m_avg.get()) + { + frameSizeLim = IPP_MIN (frameSizeLim, m_avg->GetMaxFrameSize(m_ctx.bPanic, bSHStart || picType == MFX_FRAMETYPE_I, frame_par->NumRecode)); + } + if (m_par.maxFrameSizeInBits) + { + frameSizeLim = IPP_MIN (frameSizeLim, m_par.maxFrameSizeInBits); + } + //printf("frameSizeLim %d (%d)\n", frameSizeLim, bitsEncoded); + + if (frame_par->NumRecode < 2) + // Check other condions for recoding (update qp is it is needed) + { + mfxF64 targetFrameSize = IPP_MAX((mfxF64)m_par.inputBitsPerFrame, fAbLong); + mfxF64 maxFrameSize = (m_ctx.encOrder == 0 ? 6.0 : (bSHStart || picType == MFX_FRAMETYPE_I) ? 8.0 : 4.0) * targetFrameSize*(m_par.bPyr ? 1.5 : 1.0) ; + mfxI32 quantMax = m_ctx.QuantMax; + mfxI32 quantMin = m_ctx.QuantMin; + mfxI32 quant = qpY; + + maxFrameSize = IPP_MIN(maxFrameSize,frameSizeLim); + + if (m_par.bHRDConformance) + { + if (bSHStart || picType == MFX_FRAMETYPE_I) + maxFrameSize = IPP_MIN(maxFrameSize, 3.5/9.* m_hrd.GetMaxFrameSize() + 5.5/9.*targetFrameSize); + else + maxFrameSize = IPP_MIN(maxFrameSize, 2.5/9. * m_hrd.GetMaxFrameSize() + 6.5/9. * targetFrameSize); + + quantMax = IPP_MIN(m_hrd.GetMaxQuant(), quantMax); + quantMin = IPP_MAX(m_hrd.GetMinQuant(), quantMin); + } + maxFrameSize = IPP_MAX(maxFrameSize, targetFrameSize); + + if (bitsEncoded > maxFrameSize && quant < quantMax) + { + + mfxI32 quant_new = GetNewQP(bitsEncoded, (mfxU32)maxFrameSize, quantMin , quantMax, quant ,m_par.quantOffset, 1); + if (quant_new > quant ) + { + bNeedUpdateQP = false; + //printf(" recode 1-0: %d: k %5f bitsEncoded %d maxFrameSize %d, targetSize %d, fAbLong %f, inputBitsPerFrame %d, qp %d new %d\n",frame_par->EncodedOrder, bitsEncoded/maxFrameSize, (int)bitsEncoded, (int)maxFrameSize,(int)targetFrameSize, fAbLong, m_par.inputBitsPerFrame, quant, quant_new); + if (quant_new > GetCurQP (picType, layer)) + { + UpdateQPParams(bMaxFrameSizeMode? quant_new - 1 : quant_new ,picType, m_ctx, 0, quantMin , quantMax, layer); + fAbLong = m_ctx.fAbLong = m_par.inputBitsPerFrame; + fAbShort = m_ctx.fAbShort = m_par.inputBitsPerFrame; + m_ctx.dQuantAb = 1./quant_new; + } + + if (m_par.bRec) + { + SetRecodeParams(MFX_BRC_BIG_FRAME,quant,quant_new, quantMin, quantMax, m_ctx, status); + return sts; + } + } //(quant_new > quant) + } //bitsEncoded > maxFrameSize + + if (bitsEncoded > maxFrameSize && quant == quantMax && + picType != MFX_FRAMETYPE_I && m_par.bPanic && + (!m_ctx.bPanic) && isFrameBeforeIntra(m_ctx.encOrder, m_ctx.LastIEncOrder, m_par.gopPicSize, m_par.gopRefDist)) + { + //skip frames before intra + SetRecodeParams(MFX_BRC_PANIC_BIG_FRAME,quant,quant, quantMin ,quantMax, m_ctx, status); + return sts; + } + if (m_par.bHRDConformance && frame_par->NumRecode == 0 && (quant < quantMax)) + { + mfxF64 FAMax = 1./9. * m_hrd.GetMaxFrameSize() + 8./9. * fAbLong; + + if (fAbShort > FAMax) + { + mfxI32 quant_new = GetNewQP(fAbShort, FAMax, quantMin , quantMax, quant ,m_par.quantOffset, 0.5); + //printf(" recode 2-0: %d: FAMax %f, fAbShort %f, quant_new %d\n",frame_par->EncodedOrder, FAMax, fAbShort, quant_new); + + if (quant_new > quant) + { + bNeedUpdateQP = false; + if (quant_new > GetCurQP (picType, layer)) + { + UpdateQPParams(quant_new ,picType, m_ctx, 0, quantMin , quantMax, layer); + fAbLong = m_ctx.fAbLong = m_par.inputBitsPerFrame; + fAbShort = m_ctx.fAbShort = m_par.inputBitsPerFrame; + m_ctx.dQuantAb = 1./quant_new; + } + if (m_par.bRec) + { + SetRecodeParams(MFX_BRC_BIG_FRAME,quant,quant_new, quantMin, quantMax, m_ctx, status); + return sts; + } + }//quant_new > quant + } + }//m_par.bHRDConformance + } + if (((m_par.bHRDConformance && brcSts != MFX_BRC_OK) || (bitsEncoded > (mfxI32)frameSizeLim)) && m_par.bRec) + { + mfxI32 quant = m_ctx.Quant; + mfxI32 quant_new = quant; + if (bitsEncoded > (mfxI32)frameSizeLim) + { + brcSts = MFX_BRC_BIG_FRAME; + quant_new = GetNewQP(bitsEncoded, frameSizeLim, m_ctx.QuantMin , m_ctx.QuantMax,quant,m_par.quantOffset, 1, true); + } + else if (brcSts == MFX_BRC_BIG_FRAME || brcSts == MFX_BRC_SMALL_FRAME) + { + quant_new = GetNewQP(bitsEncoded, m_hrd.GetTargetSize(brcSts), m_ctx.QuantMin , m_ctx.QuantMax,quant,m_par.quantOffset, 1, true); + } + if (quant_new != quant) + { + if (brcSts == MFX_BRC_SMALL_FRAME) + { + quant_new = IPP_MAX(quant_new, quant-2); + brcSts = MFX_BRC_PANIC_SMALL_FRAME; + } + // Idea is to check a sign mismatch, 'true' if both are negative or positive + if ((quant_new - qpY) * (quant_new - GetCurQP (picType, layer)) > 0) + { + UpdateQPParams(quant_new ,picType, m_ctx, 0, m_ctx.QuantMin , m_ctx.QuantMax, layer); + } + bNeedUpdateQP = false; + } + SetRecodeParams(brcSts,quant,quant_new, m_ctx.QuantMin , m_ctx.QuantMax, m_ctx, status); + } + else + { + // no recoding are needed. Save context params + + mfxF64 k = 1./m_ctx.Quant; + mfxF64 dqAbPeriod = m_par.dqAbPeriod; + if (m_ctx.bToRecode) + dqAbPeriod = (k < m_ctx.dQuantAb)? 16:25; + + if (bNeedUpdateQP) + { + m_ctx.dQuantAb += (k - m_ctx.dQuantAb)/dqAbPeriod; + BRC_CLIP(m_ctx.dQuantAb, 1./m_ctx.QuantMax , 1./m_ctx.QuantMin); + + m_ctx.fAbLong = fAbLong; + m_ctx.fAbShort = fAbShort; + } + + bool oldScene = false; + if ((m_ctx.SceneChange & 16) && (m_ctx.poc < m_ctx.SChPoc) && (e2pe < .01) && (mfxF64)bitsEncoded < 1.5*fAbLong) + oldScene = true; + //printf("-- m_ctx.eRate %f, eRate %f, e2pe %f\n", m_ctx.eRate, eRate, e2pe ); + + if (picType != MFX_FRAMETYPE_B) + { + m_ctx.LastNonBFrameSize = bitsEncoded; + if (picType == MFX_FRAMETYPE_I) + m_ctx.eRateSH = eRate; + else + m_ctx.eRate = eRate; + + } + + if (m_avg.get()) + { + m_avg->UpdateSlidingWindow(bitsEncoded, m_ctx.encOrder, m_ctx.bPanic, bSHStart || picType == MFX_FRAMETYPE_I,frame_par->NumRecode); + } + + m_ctx.totalDiviation += ((mfxF64)bitsEncoded - m_par.inputBitsPerFrame); + + //printf("-- %d (%d)) Total diviation %d, old scene %d, bNeedUpdateQP %d, m_ctx.Quant %d, type %d\n", frame_par->EncodedOrder, frame_par->DisplayOrder,m_ctx.totalDiviation, oldScene , bNeedUpdateQP, m_ctx.Quant,picType); + + if (!m_ctx.bPanic&& (!oldScene) && bNeedUpdateQP) + { + mfxI32 quant_new = m_ctx.Quant; + //Update QP + + mfxF64 totDiv = m_ctx.totalDiviation; + mfxF64 dequant_new = m_ctx.dQuantAb*pow(m_par.inputBitsPerFrame/m_ctx.fAbLong, 1.2); + mfxF64 bAbPreriod = m_par.bAbPeriod; + + if (m_par.bHRDConformance && totDiv > 0 ) + { + if (m_par.rateControlMethod == MFX_RATECONTROL_VBR) + { + totDiv = IPP_MAX(totDiv, m_hrd.GetBufferDiviation(m_par.targetbps)); + } + bAbPreriod = (mfxF64)(m_par.bPyr? 4 : 3)*(mfxF64)m_hrd.GetMaxFrameSize() / m_par.inputBitsPerFrame*GetAbPeriodCoeff(m_ctx.encOrder - m_ctx.LastIEncOrder, m_par.gopPicSize) ; + BRC_CLIP(bAbPreriod , m_par.bAbPeriod/10, m_par.bAbPeriod); + } + quant_new = GetNewQPTotal(totDiv / bAbPreriod / (mfxF64)m_par.inputBitsPerFrame, dequant_new, m_ctx.QuantMin, m_ctx.QuantMax, m_ctx.Quant, m_par.bPyr && m_par.bRec, bSHStart && m_ctx.bToRecode == 0); + //printf(" ===%d quant old %d quant_new %d, bitsEncoded %d m_ctx.QuantMin %d m_ctx.QuantMax %d\n", frame_par->EncodedOrder, m_ctx.Quant, quant_new, bitsEncoded, m_ctx.QuantMin, m_ctx.QuantMax); + + if (bMaxFrameSizeMode) + { + mfxF64 targetMax = ((mfxF64)m_par.maxFrameSizeInBits*((bSHStart || picType == MFX_FRAMETYPE_I) ? 0.95 : 0.9)); + mfxF64 targetMin = ((mfxF64)m_par.maxFrameSizeInBits*((bSHStart || picType == MFX_FRAMETYPE_I) ? 0.9 : 0.8 /*0.75 : 0.5*/)); + mfxI32 QuantNewMin = GetNewQP(bitsEncoded, targetMax, m_ctx.QuantMin, m_ctx.QuantMax, m_ctx.Quant, m_par.quantOffset, 1,false, false); + mfxI32 QuantNewMax = GetNewQP(bitsEncoded, targetMin, m_ctx.QuantMin, m_ctx.QuantMax, m_ctx.Quant, m_par.quantOffset, 1,false, false); + mfxI32 quant_corrected = m_ctx.Quant; + + if (quant_corrected < QuantNewMin - 3) + quant_corrected += 2; + if (quant_corrected < QuantNewMin) + quant_corrected ++; + else if (quant_corrected > QuantNewMax + 3) + quant_corrected -= 2; + else if (quant_corrected > QuantNewMax) + quant_corrected--; + + //printf(" QuantNewMin %d, QuantNewMax %d, m_ctx.Quant %d, new %d (%d)\n", QuantNewMin, QuantNewMax, m_ctx.Quant, quant_corrected, quant_new); + + quant_new = BRC_CLIP(quant_corrected, m_ctx.QuantMin, m_ctx.QuantMax); + } + if ((quant_new - m_ctx.Quant)* (quant_new - GetCurQP (picType, layer)) > 0) // this check is actual for async scheme + { + //printf(" Update QP %d: totalDiviation %f, bAbPreriod %f (%f), QP %d (%d %d), qp_new %d (qpY %d), type %d, dequant_new %f (%f) , m_ctx.fAbLong %f, m_par.inputBitsPerFrame %f (%f)\n",frame_par->EncodedOrder,totDiv , bAbPreriod, GetAbPeriodCoeff(m_ctx.encOrder - m_ctx.LastIEncOrder, m_par.gopPicSize), m_ctx.Quant, m_ctx.QuantMin, m_ctx.QuantMax,quant_new, qpY, picType, 1.0/dequant_new, 1.0/m_ctx.dQuantAb, m_ctx.fAbLong, m_par.inputBitsPerFrame, m_par.inputBitsPerFrame/m_ctx.fAbLong, m_par.inputBitsPerFrame/m_ctx.fAbLong); + UpdateQPParams(quant_new ,picType, m_ctx, 0, m_ctx.QuantMin , m_ctx.QuantMax, layer); + } + } + m_ctx.bToRecode = 0; + } + return sts; + +} + +mfxStatus ExtBRC::GetFrameCtrl (mfxBRCFrameParam* par, mfxBRCFrameCtrl* ctrl) +{ + MFX_CHECK_NULL_PTR2(par, ctrl); + MFX_CHECK(m_bInit, MFX_ERR_NOT_INITIALIZED); + + mfxI32 qp = 0; + if (par->EncodedOrder == m_ctx.encOrder) + { + qp = m_ctx.Quant; + } + else + { + mfxU16 type = GetFrameType(par->FrameType,par->PyramidLayer, m_par.gopRefDist); + qp = GetCurQP (type, par->PyramidLayer); + } + ctrl->QpY = qp - m_par.quantOffset; + //printf("ctrl->QpY %d, qp %d quantOffset %d\n", ctrl->QpY , qp , m_par.quantOffset); + return MFX_ERR_NONE; +} + +mfxStatus ExtBRC::Reset(mfxVideoParam *par ) +{ + mfxStatus sts = MFX_ERR_NONE; + MFX_CHECK_NULL_PTR1(par); + MFX_CHECK(m_bInit, MFX_ERR_NOT_INITIALIZED); + + mfxExtEncoderResetOption * pRO = (mfxExtEncoderResetOption *)Hevc_GetExtBuffer(par->ExtParam, par->NumExtParam, MFX_EXTBUFF_ENCODER_RESET_OPTION); + if (pRO && pRO->StartNewSequence == MFX_CODINGOPTION_ON) + { + Close(); + sts = Init(par); + } + else + { + bool brcReset = false; + bool slidingWindowReset = false; + + sts = m_par.GetBRCResetType(par, false, brcReset, slidingWindowReset); + MFX_CHECK_STS(sts); + + if (brcReset) + { + sts = m_par.Init(par); + MFX_CHECK_STS(sts); + + m_ctx.Quant = (mfxI32)(1. / m_ctx.dQuantAb * pow(m_ctx.fAbLong / m_par.inputBitsPerFrame, 0.32) + 0.5); + BRC_CLIP(m_ctx.Quant, m_par.quantMinI, m_par.quantMaxI); + + UpdateQPParams(m_ctx.Quant, MFX_FRAMETYPE_I, m_ctx, 0, m_par.quantMinI, m_par.quantMaxI, 0); + + m_ctx.dQuantAb = 1. / m_ctx.Quant; + m_ctx.fAbLong = m_par.inputBitsPerFrame; + m_ctx.fAbShort = m_par.inputBitsPerFrame; + + if (slidingWindowReset) + { + m_avg.reset(new AVGBitrate(m_par.WinBRCSize, (mfxU32)(m_par.WinBRCMaxAvgKbps*1000.0 / m_par.frameRate), (mfxU32)m_par.inputBitsPerFrame)); + MFX_CHECK_NULL_PTR1(m_avg.get()); + } + } + } + return sts; +} + +#endif \ No newline at end of file diff --git a/vshampor/deshuffler/sample_common/src/d3d11_allocator.cpp b/vshampor/deshuffler/sample_common/src/d3d11_allocator.cpp new file mode 100644 index 0000000..9f1a77b --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/d3d11_allocator.cpp @@ -0,0 +1,21 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + diff --git a/vshampor/deshuffler/sample_common/src/d3d11_device.cpp b/vshampor/deshuffler/sample_common/src/d3d11_device.cpp new file mode 100644 index 0000000..9f1a77b --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/d3d11_device.cpp @@ -0,0 +1,21 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + diff --git a/vshampor/deshuffler/sample_common/src/d3d_allocator.cpp b/vshampor/deshuffler/sample_common/src/d3d_allocator.cpp new file mode 100644 index 0000000..39ffb28 --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/d3d_allocator.cpp @@ -0,0 +1,22 @@ +/******************************************************************************\ +Copyright (c) 2005-2017, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" +#include "sample_defs.h" + diff --git a/vshampor/deshuffler/sample_common/src/d3d_device.cpp b/vshampor/deshuffler/sample_common/src/d3d_device.cpp new file mode 100644 index 0000000..9f1a77b --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/d3d_device.cpp @@ -0,0 +1,21 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + diff --git a/vshampor/deshuffler/sample_common/src/decode_render.cpp b/vshampor/deshuffler/sample_common/src/decode_render.cpp new file mode 100644 index 0000000..9f1a77b --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/decode_render.cpp @@ -0,0 +1,21 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + diff --git a/vshampor/deshuffler/sample_common/src/general_allocator.cpp b/vshampor/deshuffler/sample_common/src/general_allocator.cpp new file mode 100644 index 0000000..fd0030e --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/general_allocator.cpp @@ -0,0 +1,138 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + +#include "general_allocator.h" + +#include +#include "vaapi_allocator.h" + +#include "sysmem_allocator.h" + +#include "sample_defs.h" + +// Wrapper on standard allocator for concurrent allocation of +// D3D and system surfaces +GeneralAllocator::GeneralAllocator() +{ +}; +GeneralAllocator::~GeneralAllocator() +{ +}; +mfxStatus GeneralAllocator::Init(mfxAllocatorParams *pParams) +{ + mfxStatus sts = MFX_ERR_NONE; + + +#ifdef LIBVA_SUPPORT + vaapiAllocatorParams *vaapiAllocParams = dynamic_cast(pParams); + if (vaapiAllocParams) + m_D3DAllocator.reset(new vaapiFrameAllocator); +#endif + + if (m_D3DAllocator.get()) + { + sts = m_D3DAllocator.get()->Init(pParams); + MSDK_CHECK_STATUS(sts, "m_D3DAllocator.get failed"); + } + + m_SYSAllocator.reset(new SysMemFrameAllocator); + sts = m_SYSAllocator.get()->Init(0); + MSDK_CHECK_STATUS(sts, "m_SYSAllocator.get failed"); + + return sts; +} +mfxStatus GeneralAllocator::Close() +{ + mfxStatus sts = MFX_ERR_NONE; + if (m_D3DAllocator.get()) + { + sts = m_D3DAllocator.get()->Close(); + MSDK_CHECK_STATUS(sts, "m_D3DAllocator.get failed"); + } + + sts = m_SYSAllocator.get()->Close(); + MSDK_CHECK_STATUS(sts, "m_SYSAllocator.get failed"); + + return sts; +} + +mfxStatus GeneralAllocator::LockFrame(mfxMemId mid, mfxFrameData *ptr) +{ + if (isD3DMid(mid) && m_D3DAllocator.get()) + return m_D3DAllocator.get()->Lock(m_D3DAllocator.get(), mid, ptr); + else + return m_SYSAllocator.get()->Lock(m_SYSAllocator.get(),mid, ptr); +} +mfxStatus GeneralAllocator::UnlockFrame(mfxMemId mid, mfxFrameData *ptr) +{ + if (isD3DMid(mid) && m_D3DAllocator.get()) + return m_D3DAllocator.get()->Unlock(m_D3DAllocator.get(), mid, ptr); + else + return m_SYSAllocator.get()->Unlock(m_SYSAllocator.get(),mid, ptr); +} + +mfxStatus GeneralAllocator::GetFrameHDL(mfxMemId mid, mfxHDL *handle) +{ + if (isD3DMid(mid) && m_D3DAllocator.get()) + return m_D3DAllocator.get()->GetHDL(m_D3DAllocator.get(), mid, handle); + else + return m_SYSAllocator.get()->GetHDL(m_SYSAllocator.get(), mid, handle); +} + +mfxStatus GeneralAllocator::ReleaseResponse(mfxFrameAllocResponse *response) +{ + // try to ReleaseResponse via D3D allocator + if (isD3DMid(response->mids[0]) && m_D3DAllocator.get()) + return m_D3DAllocator.get()->Free(m_D3DAllocator.get(),response); + else + return m_SYSAllocator.get()->Free(m_SYSAllocator.get(), response); +} +mfxStatus GeneralAllocator::AllocImpl(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) +{ + mfxStatus sts; + if ((request->Type & MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET || request->Type & MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET) && m_D3DAllocator.get()) + { + sts = m_D3DAllocator.get()->Alloc(m_D3DAllocator.get(), request, response); + MSDK_CHECK_NOT_EQUAL(MFX_ERR_NONE, sts, sts); + StoreFrameMids(true, response); + } + else + { + sts = m_SYSAllocator.get()->Alloc(m_SYSAllocator.get(), request, response); + MSDK_CHECK_NOT_EQUAL(MFX_ERR_NONE, sts, sts); + StoreFrameMids(false, response); + } + return sts; +} +void GeneralAllocator::StoreFrameMids(bool isD3DFrames, mfxFrameAllocResponse *response) +{ + for (mfxU32 i = 0; i < response->NumFrameActual; i++) + m_Mids.insert(std::pair(response->mids[i], isD3DFrames)); +} +bool GeneralAllocator::isD3DMid(mfxHDL mid) +{ + std::map::iterator it; + it = m_Mids.find(mid); + if (it == m_Mids.end()) + return false; // sys mem allocator will check validity of mid further + else + return it->second; +} diff --git a/vshampor/deshuffler/sample_common/src/mfx_buffering.cpp b/vshampor/deshuffler/sample_common/src/mfx_buffering.cpp new file mode 100644 index 0000000..d2e5c6c --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/mfx_buffering.cpp @@ -0,0 +1,204 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + +#include + +#include + +CBuffering::CBuffering(): + m_SurfacesNumber(0), + m_OutputSurfacesNumber(0), + m_pSurfaces(NULL), + m_pVppSurfaces(NULL), + m_FreeSurfacesPool(&m_Mutex), + m_FreeVppSurfacesPool(&m_Mutex), + m_UsedSurfacesPool(&m_Mutex), + m_UsedVppSurfacesPool(&m_Mutex), + m_pFreeOutputSurfaces(NULL), + m_OutputSurfacesPool(&m_Mutex), + m_DeliveredSurfacesPool(&m_Mutex) +{ +} + +CBuffering::~CBuffering() +{ +} + +mfxStatus +CBuffering::AllocBuffers(mfxU32 SurfaceNumber) +{ + if (!SurfaceNumber) return MFX_ERR_MEMORY_ALLOC; + + if (!m_OutputSurfacesNumber) { // true - if Vpp isn't enabled + m_OutputSurfacesNumber = SurfaceNumber; + } + m_SurfacesNumber = SurfaceNumber; + + m_pSurfaces = (msdkFrameSurface*)calloc(m_SurfacesNumber, sizeof(msdkFrameSurface)); + if (!m_pSurfaces) return MFX_ERR_MEMORY_ALLOC; + + msdkOutputSurface* p = NULL; + msdkOutputSurface* tail = NULL; + + m_pFreeOutputSurfaces = (msdkOutputSurface*)calloc(1, sizeof(msdkOutputSurface)); + if (!m_pFreeOutputSurfaces) return MFX_ERR_MEMORY_ALLOC; + + tail = m_pFreeOutputSurfaces; + + for (mfxU32 i = 1; i < m_OutputSurfacesNumber; ++i) { + p = (msdkOutputSurface*)calloc(1, sizeof(msdkOutputSurface)); + if (!p) return MFX_ERR_MEMORY_ALLOC; + tail->next = p; + tail = p; + } + + ResetBuffers(); + return MFX_ERR_NONE; +} + +mfxStatus +CBuffering::AllocVppBuffers(mfxU32 VppSurfaceNumber) +{ + m_OutputSurfacesNumber = VppSurfaceNumber; + m_pVppSurfaces = (msdkFrameSurface*)calloc(m_OutputSurfacesNumber, sizeof(msdkFrameSurface)); + if (!m_pVppSurfaces) return MFX_ERR_MEMORY_ALLOC; + + ResetVppBuffers(); + return MFX_ERR_NONE; +} + +void +CBuffering::AllocOutputBuffer() +{ + AutomaticMutex lock(m_Mutex); + + m_pFreeOutputSurfaces = (msdkOutputSurface*)calloc(1, sizeof(msdkOutputSurface)); +} + +static void +FreeList(msdkOutputSurface*& head) { + msdkOutputSurface* next; + while (head) { + next = head->next; + free(head); + head = next; + } +} + +void +CBuffering::FreeBuffers() +{ + if (m_pSurfaces) { + free(m_pSurfaces); + m_pSurfaces = NULL; + } + + if (m_pVppSurfaces) { + free(m_pVppSurfaces); + m_pVppSurfaces = NULL; + } + + FreeList(m_pFreeOutputSurfaces); + FreeList(m_OutputSurfacesPool.m_pSurfacesHead); + FreeList(m_DeliveredSurfacesPool.m_pSurfacesHead); + + m_UsedSurfacesPool.m_pSurfacesHead = NULL; + m_UsedSurfacesPool.m_pSurfacesTail = NULL; + m_UsedVppSurfacesPool.m_pSurfacesHead = NULL; + m_UsedVppSurfacesPool.m_pSurfacesTail = NULL; + + m_FreeSurfacesPool.m_pSurfaces = NULL; + m_FreeVppSurfacesPool.m_pSurfaces = NULL; +} + +void +CBuffering::ResetBuffers() +{ + mfxU32 i; + msdkFrameSurface* pFreeSurf = m_FreeSurfacesPool.m_pSurfaces = m_pSurfaces; + + for (i = 0; i < m_SurfacesNumber; ++i) { + if (i < (m_SurfacesNumber-1)) { + pFreeSurf[i].next = &(pFreeSurf[i+1]); + pFreeSurf[i+1].prev = &(pFreeSurf[i]); + } + } +} + +void +CBuffering::ResetVppBuffers() +{ + mfxU32 i; + msdkFrameSurface* pFreeVppSurf = m_FreeVppSurfacesPool.m_pSurfaces = m_pVppSurfaces; + + for (i = 0; i < m_OutputSurfacesNumber; ++i) { + if (i < (m_OutputSurfacesNumber-1)) { + pFreeVppSurf[i].next = &(pFreeVppSurf[i+1]); + pFreeVppSurf[i+1].prev = &(pFreeVppSurf[i]); + } + } +} + +void +CBuffering::SyncFrameSurfaces() +{ + AutomaticMutex lock(m_Mutex); + msdkFrameSurface *prev; + msdkFrameSurface *next; + prev = next = NULL; + msdkFrameSurface *cur = m_UsedSurfacesPool.m_pSurfacesHead; + + while (cur) { + if (cur->frame.Data.Locked || cur->render_lock) { + // frame is still locked: just moving to the next one + cur = cur->next; + } else { + // frame was unlocked: moving it to the free surfaces array + m_UsedSurfacesPool.DetachSurfaceUnsafe(cur); + m_FreeSurfacesPool.AddSurfaceUnsafe(cur); + + cur = next; + } + } +} + +void +CBuffering::SyncVppFrameSurfaces() +{ + AutomaticMutex lock(m_Mutex); + msdkFrameSurface *prev; + msdkFrameSurface *next; + prev = next = NULL; + msdkFrameSurface *cur = m_UsedVppSurfacesPool.m_pSurfacesHead; + + while (cur) { + if (cur->frame.Data.Locked || cur->render_lock) { + // frame is still locked: just moving to the next one + cur = cur->next; + } else { + // frame was unlocked: moving it to the free surfaces array + m_UsedVppSurfacesPool.DetachSurfaceUnsafe(cur); + m_FreeVppSurfacesPool.AddSurfaceUnsafe(cur); + + cur = next; + } + } +} diff --git a/vshampor/deshuffler/sample_common/src/parameters_dumper.cpp b/vshampor/deshuffler/sample_common/src/parameters_dumper.cpp new file mode 100644 index 0000000..2879c2a --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/parameters_dumper.cpp @@ -0,0 +1,1037 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "parameters_dumper.h" +#include +#include +#include +#include + +#include "vm/strings_defs.h" + +#include "mfxstructures.h" +#include "mfxvideo.h" +#include "mfxvideo++.h" +#include "mfxjpeg.h" +#include "mfxplugin.h" + +#include "sample_types.h" +#include "mfxvp8.h" +#include "mfxmvc.h" +#include "mfxla.h" + +#ifndef MFX_VERSION +#error MFX_VERSION not defined +#endif + +#define START_PROC_ARRAY(arrName) for(unsigned int arrIdx=0;arrIdx<(sizeof(info.arrName)/sizeof(info.arrName[0]));arrIdx++){ +#define START_PROC_ARRAY_SIZE(arrName,numElems) for(unsigned int arrIdx=0;arrIdxBufferId,4); + std::string strName(name); + prefix+=msdk_string(strName.begin(),strName.end()); + + // Serializing header + { + mfxExtBuffer& info = *pExtBuffer; + SERIALIZE_INFO(BufferId); + SERIALIZE_INFO(BufferSz); + } + + // Serializing particular Ext buffer. + switch(pExtBuffer->BufferId) + { + case MFX_EXTBUFF_THREADS_PARAM: + { + mfxExtThreadsParam& info = *(mfxExtThreadsParam*)pExtBuffer; + SERIALIZE_INFO(NumThread); + SERIALIZE_INFO(SchedulingType); + SERIALIZE_INFO(Priority); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_JPEG_QT: + { + mfxExtJPEGQuantTables& info = *(mfxExtJPEGQuantTables*)pExtBuffer; + SERIALIZE_INFO_ARRAY(reserved); + SERIALIZE_INFO(NumTable); + SERIALIZE_INFO_ARRAY(Qm[4]); + } + break; + case MFX_EXTBUFF_JPEG_HUFFMAN: + { + mfxExtJPEGHuffmanTables& info = *(mfxExtJPEGHuffmanTables*)pExtBuffer; + SERIALIZE_INFO_ARRAY(reserved); + SERIALIZE_INFO(NumDCTable); + SERIALIZE_INFO(NumACTable); + START_PROC_ARRAY(DCTables) + SERIALIZE_INFO_ARRAY_ELEMENT(DCTables,Bits); + SERIALIZE_INFO_ARRAY_ELEMENT(DCTables,Values); + END_PROC_ARRAY + START_PROC_ARRAY(DCTables) + SERIALIZE_INFO_ARRAY_ELEMENT(ACTables,Bits); + SERIALIZE_INFO_ARRAY_ELEMENT(ACTables,Values); + END_PROC_ARRAY + } + break; + case MFX_EXTBUFF_LOOKAHEAD_CTRL: + { + mfxExtLAControl& info = *(mfxExtLAControl*)pExtBuffer; + SERIALIZE_INFO(LookAheadDepth); + SERIALIZE_INFO(DependencyDepth); + SERIALIZE_INFO(DownScaleFactor); + SERIALIZE_INFO(BPyramid); + SERIALIZE_INFO_ARRAY(reserved1); + SERIALIZE_INFO(NumOutStream); + START_PROC_ARRAY(OutStream) + SERIALIZE_INFO_ELEMENT(OutStream,Width); + SERIALIZE_INFO_ELEMENT(OutStream,Height); + SERIALIZE_INFO_ARRAY_ELEMENT(OutStream,reserved2); + END_PROC_ARRAY + } + break; + case MFX_EXTBUFF_LOOKAHEAD_STAT: + { + mfxExtLAFrameStatistics& info = *(mfxExtLAFrameStatistics*)pExtBuffer; + SERIALIZE_INFO_ARRAY(reserved); + SERIALIZE_INFO(NumStream); + SERIALIZE_INFO(NumFrame); + //DO_MANUALLY: mfxLAFrameInfo *FrameStat; //frame statistics + //DO_MANUALLY: mfxFrameSurface1 *OutSurface; //reordered surface + } + break; + case MFX_EXTBUFF_MVC_SEQ_DESC: + { + mfxExtMVCSeqDesc& info = *(mfxExtMVCSeqDesc*)pExtBuffer; + SERIALIZE_INFO(NumView); + SERIALIZE_INFO(NumViewAlloc); + //DO_MANUALLY: mfxMVCViewDependency *View; + SERIALIZE_INFO(NumViewId); + SERIALIZE_INFO(NumViewIdAlloc); + SERIALIZE_INFO_MEMORY(ViewId,NumViewId); + SERIALIZE_INFO(NumOP); + SERIALIZE_INFO(NumOPAlloc); + //DO_MANUALLY: mfxMVCOperationPoint *OP; + SERIALIZE_INFO(NumRefsTotal); + SERIALIZE_INFO_ARRAY(Reserved); + } + break; + case MFX_EXTBUFF_MVC_TARGET_VIEWS: + { + mfxExtMVCTargetViews & info = *(mfxExtMVCTargetViews *)pExtBuffer; + SERIALIZE_INFO(TemporalId); + SERIALIZE_INFO(NumView); + SERIALIZE_INFO_ARRAY(ViewId); + } + break; + case MFX_EXTBUFF_VPP_PICSTRUCT_DETECTION: + { + // No structure accociated with MFX_EXTBUFF_VPP_PICSTRUCT_DETECTION + } + break; + case MFX_EXTBUFF_CODING_OPTION: + { + mfxExtCodingOption& info = *(mfxExtCodingOption*)pExtBuffer; + SERIALIZE_INFO(reserved1); + SERIALIZE_INFO(RateDistortionOpt); + SERIALIZE_INFO(MECostType); + SERIALIZE_INFO(MESearchType); + SERIALIZE_INFO(MVSearchWindow.x); + SERIALIZE_INFO(MVSearchWindow.y); + SERIALIZE_INFO(EndOfSequence); + SERIALIZE_INFO(FramePicture); + SERIALIZE_INFO(CAVLC); + SERIALIZE_INFO_ARRAY(reserved2); + SERIALIZE_INFO(RecoveryPointSEI); + SERIALIZE_INFO(ViewOutput); + SERIALIZE_INFO(NalHrdConformance); + SERIALIZE_INFO(SingleSeiNalUnit); + SERIALIZE_INFO(VuiVclHrdParameters); + SERIALIZE_INFO(RefPicListReordering); + SERIALIZE_INFO(ResetRefList); + SERIALIZE_INFO(RefPicMarkRep); + SERIALIZE_INFO(FieldOutput); + SERIALIZE_INFO(IntraPredBlockSize); + SERIALIZE_INFO(InterPredBlockSize); + SERIALIZE_INFO(MVPrecision); + SERIALIZE_INFO(MaxDecFrameBuffering); + SERIALIZE_INFO(AUDelimiter); + SERIALIZE_INFO(EndOfStream); + SERIALIZE_INFO(PicTimingSEI); + SERIALIZE_INFO(VuiNalHrdParameters); + } + break; + case MFX_EXTBUFF_CODING_OPTION2: + { + mfxExtCodingOption2& info = *(mfxExtCodingOption2*)pExtBuffer; + SERIALIZE_INFO(IntRefType); + SERIALIZE_INFO(IntRefCycleSize); + SERIALIZE_INFO(IntRefQPDelta); + SERIALIZE_INFO(MaxFrameSize); + SERIALIZE_INFO(MaxSliceSize); + SERIALIZE_INFO(BitrateLimit); + SERIALIZE_INFO(MBBRC); + SERIALIZE_INFO(ExtBRC); + SERIALIZE_INFO(LookAheadDepth); + SERIALIZE_INFO(Trellis); + SERIALIZE_INFO(RepeatPPS); + SERIALIZE_INFO(BRefType); + SERIALIZE_INFO(AdaptiveI); + SERIALIZE_INFO(AdaptiveB); + SERIALIZE_INFO(LookAheadDS); + SERIALIZE_INFO(NumMbPerSlice); + SERIALIZE_INFO(SkipFrame); + SERIALIZE_INFO(MinQPI); + SERIALIZE_INFO(MaxQPI); + SERIALIZE_INFO(MinQPP); + SERIALIZE_INFO(MaxQPP); + SERIALIZE_INFO(MinQPB); + SERIALIZE_INFO(MaxQPB); + SERIALIZE_INFO(FixedFrameRate); + SERIALIZE_INFO(DisableDeblockingIdc); + SERIALIZE_INFO(DisableVUI); + SERIALIZE_INFO(BufferingPeriodSEI); + SERIALIZE_INFO(EnableMAD); + SERIALIZE_INFO(UseRawRef); + } + break; + case MFX_EXTBUFF_CODING_OPTION3: + { + mfxExtCodingOption3& info = *(mfxExtCodingOption3*)pExtBuffer; + SERIALIZE_INFO(NumSliceI); + SERIALIZE_INFO(NumSliceP); + SERIALIZE_INFO(NumSliceB); + SERIALIZE_INFO(WinBRCMaxAvgKbps); + SERIALIZE_INFO(WinBRCSize); + SERIALIZE_INFO(QVBRQuality); + SERIALIZE_INFO(EnableMBQP); + SERIALIZE_INFO(IntRefCycleDist); + SERIALIZE_INFO(DirectBiasAdjustment); + SERIALIZE_INFO(GlobalMotionBiasAdjustment); + SERIALIZE_INFO(MVCostScalingFactor); + SERIALIZE_INFO(MBDisableSkipMap); + SERIALIZE_INFO(WeightedPred); + SERIALIZE_INFO(WeightedBiPred); + SERIALIZE_INFO(AspectRatioInfoPresent); + SERIALIZE_INFO(OverscanInfoPresent); + SERIALIZE_INFO(OverscanAppropriate); + SERIALIZE_INFO(TimingInfoPresent); + SERIALIZE_INFO(BitstreamRestriction); + SERIALIZE_INFO(LowDelayHrd); + SERIALIZE_INFO(MotionVectorsOverPicBoundaries); + SERIALIZE_INFO(ScenarioInfo); + SERIALIZE_INFO(ContentInfo); + SERIALIZE_INFO(PRefType); + SERIALIZE_INFO(FadeDetection); + SERIALIZE_INFO(GPB); + SERIALIZE_INFO(MaxFrameSizeI); + SERIALIZE_INFO(MaxFrameSizeP); +#if (MFX_VERSION >= MFX_VERSION_NEXT) + SERIALIZE_INFO(Log2MaxMvLengthHorizontal); + SERIALIZE_INFO(Log2MaxMvLengthVertical); +#else + SERIALIZE_INFO_ARRAY(reserved1); +#endif + SERIALIZE_INFO(EnableQPOffset); + SERIALIZE_INFO_ARRAY(QPOffset); + SERIALIZE_INFO_ARRAY(NumRefActiveP); + SERIALIZE_INFO_ARRAY(NumRefActiveBL0); + SERIALIZE_INFO_ARRAY(NumRefActiveBL1); + SERIALIZE_INFO(BRCPanicMode); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_VPP_DONOTUSE: + { + mfxExtVPPDoNotUse& info = *(mfxExtVPPDoNotUse*)pExtBuffer; + SERIALIZE_INFO(NumAlg); + SERIALIZE_INFO_MEMORY(AlgList,NumAlg); + } + break; + case MFX_EXTBUFF_VPP_DENOISE: + { + mfxExtVPPDenoise& info = *(mfxExtVPPDenoise*)pExtBuffer; + SERIALIZE_INFO(DenoiseFactor); + } + break; + case MFX_EXTBUFF_VPP_DETAIL: + { + mfxExtVPPDetail& info = *(mfxExtVPPDetail*)pExtBuffer; + SERIALIZE_INFO(DetailFactor); + } + break; + case MFX_EXTBUFF_VPP_PROCAMP: + { + mfxExtVPPProcAmp& info = *(mfxExtVPPProcAmp*)pExtBuffer; + SERIALIZE_INFO(Brightness); + SERIALIZE_INFO(Contrast); + SERIALIZE_INFO(Hue); + SERIALIZE_INFO(Saturation); + } + break; + case MFX_EXTBUFF_VPP_AUXDATA: + { + mfxExtVppAuxData& info = *(mfxExtVppAuxData*)pExtBuffer; + SERIALIZE_INFO(SpatialComplexity); + SERIALIZE_INFO(TemporalComplexity); + SERIALIZE_INFO(PicStruct); + SERIALIZE_INFO_ARRAY(reserved); + SERIALIZE_INFO(SceneChangeRate); + SERIALIZE_INFO(RepeatedFrame); + } + break; + case MFX_EXTBUFF_CODING_OPTION_SPSPPS: + { + mfxExtCodingOptionSPSPPS& info = *(mfxExtCodingOptionSPSPPS*)pExtBuffer; + SERIALIZE_INFO(SPSBuffer); + SERIALIZE_INFO(PPSBuffer); + SERIALIZE_INFO(SPSBufSize); + SERIALIZE_INFO(PPSBufSize); + SERIALIZE_INFO(SPSId); + SERIALIZE_INFO(PPSId); + } + break; + case MFX_EXTBUFF_CODING_OPTION_VPS: + { + mfxExtCodingOptionVPS& info = *(mfxExtCodingOptionVPS*)pExtBuffer; + SERIALIZE_INFO(VPSBuffer); + SERIALIZE_INFO(reserved1); + SERIALIZE_INFO(VPSBufSize); + SERIALIZE_INFO(VPSId); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_VPP_VIDEO_SIGNAL_INFO: + { + mfxExtVideoSignalInfo& info = *(mfxExtVideoSignalInfo*)pExtBuffer; + SERIALIZE_INFO(VideoFormat); + SERIALIZE_INFO(VideoFullRange); + SERIALIZE_INFO(ColourDescriptionPresent); + SERIALIZE_INFO(ColourPrimaries); + SERIALIZE_INFO(TransferCharacteristics); + SERIALIZE_INFO(MatrixCoefficients); + } + break; + case MFX_EXTBUFF_VPP_DOUSE: + { + mfxExtVPPDoUse& info = *(mfxExtVPPDoUse*)pExtBuffer; + SERIALIZE_INFO(NumAlg); + SERIALIZE_INFO(AlgList); + } + break; + case MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION: + { + mfxExtOpaqueSurfaceAlloc& info = *(mfxExtOpaqueSurfaceAlloc*)pExtBuffer; + SERIALIZE_INFO_ARRAY(reserved1); + SERIALIZE_INFO_ARRAY(In.reserved2); + SERIALIZE_INFO(In.Type); + SERIALIZE_INFO(In.NumSurface); + SERIALIZE_INFO_ARRAY(Out.reserved2); + SERIALIZE_INFO(Out.Type); + SERIALIZE_INFO(Out.NumSurface); + } + break; + case MFX_EXTBUFF_AVC_REFLIST_CTRL: + { + mfxExtAVCRefListCtrl& info = *(mfxExtAVCRefListCtrl*)pExtBuffer; + SERIALIZE_INFO(NumRefIdxL0Active); + SERIALIZE_INFO(NumRefIdxL1Active); + START_PROC_ARRAY(PreferredRefList) + SERIALIZE_INFO_ELEMENT(PreferredRefList,FrameOrder); + SERIALIZE_INFO_ELEMENT(PreferredRefList,PicStruct); + SERIALIZE_INFO_ELEMENT(PreferredRefList,ViewId); + SERIALIZE_INFO_ELEMENT(PreferredRefList,LongTermIdx); + SERIALIZE_INFO_ARRAY_ELEMENT(PreferredRefList,reserved); + END_PROC_ARRAY + + START_PROC_ARRAY(RejectedRefList) + SERIALIZE_INFO_ELEMENT(RejectedRefList,FrameOrder); + SERIALIZE_INFO_ELEMENT(RejectedRefList,PicStruct); + SERIALIZE_INFO_ELEMENT(RejectedRefList,ViewId); + SERIALIZE_INFO_ELEMENT(RejectedRefList,LongTermIdx); + SERIALIZE_INFO_ARRAY_ELEMENT(RejectedRefList,reserved); + END_PROC_ARRAY + + START_PROC_ARRAY(LongTermRefList) + SERIALIZE_INFO_ELEMENT(LongTermRefList,FrameOrder); + SERIALIZE_INFO_ELEMENT(LongTermRefList,PicStruct); + SERIALIZE_INFO_ELEMENT(LongTermRefList,ViewId); + SERIALIZE_INFO_ELEMENT(LongTermRefList,LongTermIdx); + SERIALIZE_INFO_ARRAY_ELEMENT(LongTermRefList,reserved); + END_PROC_ARRAY + + SERIALIZE_INFO(ApplyLongTermIdx); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION: + { + mfxExtVPPFrameRateConversion& info = *(mfxExtVPPFrameRateConversion*)pExtBuffer; + SERIALIZE_INFO(Algorithm); + SERIALIZE_INFO(reserved); + SERIALIZE_INFO_ARRAY(reserved2); + } + break; + case MFX_EXTBUFF_VPP_IMAGE_STABILIZATION: + { + mfxExtVPPImageStab& info = *(mfxExtVPPImageStab*)pExtBuffer; + SERIALIZE_INFO(Mode); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_PICTURE_TIMING_SEI: + { + mfxExtPictureTimingSEI& info = *(mfxExtPictureTimingSEI*)pExtBuffer; + SERIALIZE_INFO_ARRAY(reserved); + + SERIALIZE_INFO(TimeStamp[0].ClockTimestampFlag); + SERIALIZE_INFO(TimeStamp[0].CtType); + SERIALIZE_INFO(TimeStamp[0].NuitFieldBasedFlag); + SERIALIZE_INFO(TimeStamp[0].CountingType); + SERIALIZE_INFO(TimeStamp[0].FullTimestampFlag); + SERIALIZE_INFO(TimeStamp[0].DiscontinuityFlag); + SERIALIZE_INFO(TimeStamp[0].CntDroppedFlag); + SERIALIZE_INFO(TimeStamp[0].NFrames); + SERIALIZE_INFO(TimeStamp[0].SecondsFlag); + SERIALIZE_INFO(TimeStamp[0].MinutesFlag); + SERIALIZE_INFO(TimeStamp[0].HoursFlag); + SERIALIZE_INFO(TimeStamp[0].SecondsValue); + SERIALIZE_INFO(TimeStamp[0].MinutesValue); + SERIALIZE_INFO(TimeStamp[0].HoursValue); + SERIALIZE_INFO(TimeStamp[0].TimeOffset); + + SERIALIZE_INFO(TimeStamp[1].ClockTimestampFlag); + SERIALIZE_INFO(TimeStamp[1].CtType); + SERIALIZE_INFO(TimeStamp[1].NuitFieldBasedFlag); + SERIALIZE_INFO(TimeStamp[1].CountingType); + SERIALIZE_INFO(TimeStamp[1].FullTimestampFlag); + SERIALIZE_INFO(TimeStamp[1].DiscontinuityFlag); + SERIALIZE_INFO(TimeStamp[1].CntDroppedFlag); + SERIALIZE_INFO(TimeStamp[1].NFrames); + SERIALIZE_INFO(TimeStamp[1].SecondsFlag); + SERIALIZE_INFO(TimeStamp[1].MinutesFlag); + SERIALIZE_INFO(TimeStamp[1].HoursFlag); + SERIALIZE_INFO(TimeStamp[1].SecondsValue); + SERIALIZE_INFO(TimeStamp[1].MinutesValue); + SERIALIZE_INFO(TimeStamp[1].HoursValue); + SERIALIZE_INFO(TimeStamp[1].TimeOffset); + + SERIALIZE_INFO(TimeStamp[2].ClockTimestampFlag); + SERIALIZE_INFO(TimeStamp[2].CtType); + SERIALIZE_INFO(TimeStamp[2].NuitFieldBasedFlag); + SERIALIZE_INFO(TimeStamp[2].CountingType); + SERIALIZE_INFO(TimeStamp[2].FullTimestampFlag); + SERIALIZE_INFO(TimeStamp[2].DiscontinuityFlag); + SERIALIZE_INFO(TimeStamp[2].CntDroppedFlag); + SERIALIZE_INFO(TimeStamp[2].NFrames); + SERIALIZE_INFO(TimeStamp[2].SecondsFlag); + SERIALIZE_INFO(TimeStamp[2].MinutesFlag); + SERIALIZE_INFO(TimeStamp[2].HoursFlag); + SERIALIZE_INFO(TimeStamp[2].SecondsValue); + SERIALIZE_INFO(TimeStamp[2].MinutesValue); + SERIALIZE_INFO(TimeStamp[2].HoursValue); + SERIALIZE_INFO(TimeStamp[2].TimeOffset); + } + break; + case MFX_EXTBUFF_AVC_TEMPORAL_LAYERS: + { + mfxExtAvcTemporalLayers& info = *(mfxExtAvcTemporalLayers*)pExtBuffer; + SERIALIZE_INFO_ARRAY(reserved1); + SERIALIZE_INFO(reserved2); + SERIALIZE_INFO(BaseLayerPID); + SERIALIZE_INFO(Layer[0].Scale); + SERIALIZE_INFO_ARRAY(Layer[0].reserved); + SERIALIZE_INFO(Layer[1].Scale); + SERIALIZE_INFO_ARRAY(Layer[1].reserved); + SERIALIZE_INFO(Layer[2].Scale); + SERIALIZE_INFO_ARRAY(Layer[2].reserved); + SERIALIZE_INFO(Layer[3].Scale); + SERIALIZE_INFO_ARRAY(Layer[3].reserved); + SERIALIZE_INFO(Layer[4].Scale); + SERIALIZE_INFO_ARRAY(Layer[4].reserved); + SERIALIZE_INFO(Layer[5].Scale); + SERIALIZE_INFO_ARRAY(Layer[5].reserved); + SERIALIZE_INFO(Layer[6].Scale); + SERIALIZE_INFO_ARRAY(Layer[6].reserved); + SERIALIZE_INFO(Layer[7].Scale); + SERIALIZE_INFO_ARRAY(Layer[7].reserved); + } + break; + case MFX_EXTBUFF_ENCODER_CAPABILITY: + { + mfxExtEncoderCapability& info = *(mfxExtEncoderCapability*)pExtBuffer; + SERIALIZE_INFO(MBPerSec); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_ENCODER_RESET_OPTION: + { + mfxExtEncoderResetOption& info = *(mfxExtEncoderResetOption*)pExtBuffer; + SERIALIZE_INFO(StartNewSequence); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_ENCODED_FRAME_INFO: + { + mfxExtAVCEncodedFrameInfo& info = *(mfxExtAVCEncodedFrameInfo*)pExtBuffer; + SERIALIZE_INFO(FrameOrder); + SERIALIZE_INFO(PicStruct); + SERIALIZE_INFO(LongTermIdx); + SERIALIZE_INFO(MAD); + SERIALIZE_INFO(BRCPanicMode); + SERIALIZE_INFO(QP); + SERIALIZE_INFO(SecondFieldOffset); + SERIALIZE_INFO_ARRAY(reserved); + SERIALIZE_INFO(FrameOrder); + SERIALIZE_INFO(PicStruct); + SERIALIZE_INFO(LongTermIdx); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_VPP_COMPOSITE: + { + mfxExtVPPComposite& info = *(mfxExtVPPComposite*)pExtBuffer; + SERIALIZE_INFO(Y); + SERIALIZE_INFO(R); + SERIALIZE_INFO(U); + SERIALIZE_INFO(G); + SERIALIZE_INFO(V); + SERIALIZE_INFO(B); + SERIALIZE_INFO_ARRAY(reserved1); + SERIALIZE_INFO(NumInputStream); + for(int i=0;i= 1022 + SERIALIZE_INFO(ROIMode); +#endif //MFX_VERSION >= 1022 + SERIALIZE_INFO_ARRAY(reserved1); + START_PROC_ARRAY_SIZE(ROI,NumROI) + SERIALIZE_INFO_ELEMENT(ROI,Left); + SERIALIZE_INFO_ELEMENT(ROI,Top); + SERIALIZE_INFO_ELEMENT(ROI,Right); + SERIALIZE_INFO_ELEMENT(ROI,Bottom); + SERIALIZE_INFO_ELEMENT(ROI,Priority); +#if MFX_VERSION >= 1022 + SERIALIZE_INFO_ELEMENT(ROI,DeltaQP); +#endif //MFX_VERSION >= 1022 + + SERIALIZE_INFO_ARRAY_ELEMENT(ROI,reserved2); + END_PROC_ARRAY + } + break; + case MFX_EXTBUFF_VPP_DEINTERLACING: + { + mfxExtVPPDeinterlacing& info = *(mfxExtVPPDeinterlacing*)pExtBuffer; + SERIALIZE_INFO(Mode); + SERIALIZE_INFO(TelecinePattern); + SERIALIZE_INFO(TelecineLocation); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_AVC_REFLISTS: + { + mfxExtAVCRefLists& info = *(mfxExtAVCRefLists*)pExtBuffer; + SERIALIZE_INFO(NumRefIdxL0Active); + SERIALIZE_INFO(NumRefIdxL1Active); + SERIALIZE_INFO_ARRAY(reserved); + + START_PROC_ARRAY(RefPicList0) + SERIALIZE_INFO_ELEMENT(RefPicList0,FrameOrder); + SERIALIZE_INFO_ELEMENT(RefPicList0,PicStruct); + SERIALIZE_INFO_ELEMENT(RefPicList0,reserved); + END_PROC_ARRAY + + START_PROC_ARRAY(RefPicList1) + SERIALIZE_INFO_ELEMENT(RefPicList1,FrameOrder); + SERIALIZE_INFO_ELEMENT(RefPicList1,PicStruct); + SERIALIZE_INFO_ELEMENT(RefPicList1,reserved); + END_PROC_ARRAY + } + break; + case MFX_EXTBUFF_VPP_FIELD_PROCESSING: + { + mfxExtVPPFieldProcessing& info = *(mfxExtVPPFieldProcessing*)pExtBuffer; + SERIALIZE_INFO(Mode); + SERIALIZE_INFO(InField); + SERIALIZE_INFO(OutField); + SERIALIZE_INFO_ARRAY(reserved); + } + break; +#if MFX_VERSION >= 1022 + case MFX_EXTBUFF_DEC_VIDEO_PROCESSING: + { + mfxExtDecVideoProcessing& info = *(mfxExtDecVideoProcessing*)pExtBuffer; + SERIALIZE_INFO(In.CropX); + SERIALIZE_INFO(In.CropY); + SERIALIZE_INFO(In.CropW); + SERIALIZE_INFO(In.CropH); + SERIALIZE_INFO_ARRAY(In.reserved); + SERIALIZE_INFO(Out.FourCC); + SERIALIZE_INFO(Out.ChromaFormat); + SERIALIZE_INFO(Out.Width); + SERIALIZE_INFO(Out.Height); + SERIALIZE_INFO(Out.CropX); + SERIALIZE_INFO(Out.CropY); + SERIALIZE_INFO(Out.CropW); + SERIALIZE_INFO(Out.CropH); + SERIALIZE_INFO_ARRAY(Out.reserved); + } + break; +#endif //MFX_VERSION >= 1022 + case MFX_EXTBUFF_CHROMA_LOC_INFO: + { + mfxExtChromaLocInfo& info = *(mfxExtChromaLocInfo*)pExtBuffer; + SERIALIZE_INFO(ChromaLocInfoPresentFlag); + SERIALIZE_INFO(ChromaSampleLocTypeTopField); + SERIALIZE_INFO(ChromaSampleLocTypeBottomField); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_MBQP: + { + mfxExtMBQP& info = *(mfxExtMBQP*)pExtBuffer; + SERIALIZE_INFO_ARRAY(reserved); + SERIALIZE_INFO(NumQPAlloc); + SERIALIZE_INFO_MEMORY(QP,NumQPAlloc); + SERIALIZE_INFO(reserved2); + } + break; + case MFX_EXTBUFF_HEVC_TILES: + { + mfxExtHEVCTiles& info = *(mfxExtHEVCTiles*)pExtBuffer; + SERIALIZE_INFO(NumTileRows); + SERIALIZE_INFO(NumTileColumns); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_MB_DISABLE_SKIP_MAP: + { + mfxExtMBDisableSkipMap& info = *(mfxExtMBDisableSkipMap*)pExtBuffer; + SERIALIZE_INFO_ARRAY(reserved); + SERIALIZE_INFO(MapSize); + SERIALIZE_INFO_MEMORY(Map,MapSize); + SERIALIZE_INFO(reserved2); + } + break; + case MFX_EXTBUFF_HEVC_PARAM: + { + mfxExtHEVCParam& info = *(mfxExtHEVCParam*)pExtBuffer; + SERIALIZE_INFO(PicWidthInLumaSamples); + SERIALIZE_INFO(PicHeightInLumaSamples); + SERIALIZE_INFO(GeneralConstraintFlags); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_DECODED_FRAME_INFO: + { + mfxExtDecodedFrameInfo& info = *(mfxExtDecodedFrameInfo*)pExtBuffer; + SERIALIZE_INFO(FrameType); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_TIME_CODE: + { + mfxExtTimeCode& info = *(mfxExtTimeCode*)pExtBuffer; + SERIALIZE_INFO(DropFrameFlag); + SERIALIZE_INFO(TimeCodeHours); + SERIALIZE_INFO(TimeCodeMinutes); + SERIALIZE_INFO(TimeCodeSeconds); + SERIALIZE_INFO(TimeCodePictures); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_HEVC_REGION: + { + mfxExtHEVCRegion& info = *(mfxExtHEVCRegion*)pExtBuffer; + SERIALIZE_INFO(RegionId); + SERIALIZE_INFO(RegionType); + SERIALIZE_INFO(RegionEncoding); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_PRED_WEIGHT_TABLE: + { + mfxExtPredWeightTable& info = *(mfxExtPredWeightTable*)pExtBuffer; + SERIALIZE_INFO(LumaLog2WeightDenom); + SERIALIZE_INFO(ChromaLog2WeightDenom); + SERIALIZE_INFO_ARRAY(LumaWeightFlag[0]); + SERIALIZE_INFO_ARRAY(LumaWeightFlag[1]); + SERIALIZE_INFO_ARRAY(ChromaWeightFlag[0]); + SERIALIZE_INFO_ARRAY(ChromaWeightFlag[1]); + //DO_MANUALLY: Weights[2][32][3][2]; + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_DIRTY_RECTANGLES: + { + mfxExtDirtyRect& info = *(mfxExtDirtyRect*)pExtBuffer; + SERIALIZE_INFO(NumRect); + SERIALIZE_INFO_ARRAY(reserved1); + + START_PROC_ARRAY_SIZE(Rect,NumRect) + SERIALIZE_INFO_ELEMENT(Rect,Left); + SERIALIZE_INFO_ELEMENT(Rect,Top); + SERIALIZE_INFO_ELEMENT(Rect,Right); + SERIALIZE_INFO_ELEMENT(Rect,Bottom); + SERIALIZE_INFO_ARRAY_ELEMENT(Rect,reserved2); + END_PROC_ARRAY + } + break; + case MFX_EXTBUFF_MOVING_RECTANGLES: + { + mfxExtMoveRect& info = *(mfxExtMoveRect*)pExtBuffer; + SERIALIZE_INFO(NumRect); + SERIALIZE_INFO_ARRAY(reserved1); + + START_PROC_ARRAY_SIZE(Rect,NumRect) + SERIALIZE_INFO_ELEMENT(Rect,DestLeft); + SERIALIZE_INFO_ELEMENT(Rect,DestTop); + SERIALIZE_INFO_ELEMENT(Rect,DestRight); + SERIALIZE_INFO_ELEMENT(Rect,DestBottom); + SERIALIZE_INFO_ELEMENT(Rect,SourceLeft); + SERIALIZE_INFO_ELEMENT(Rect,SourceTop); + SERIALIZE_INFO_ARRAY_ELEMENT(Rect,reserved2); + END_PROC_ARRAY + } + break; + case MFX_EXTBUFF_VPP_ROTATION: + { + mfxExtVPPRotation& info = *(mfxExtVPPRotation*)pExtBuffer; + SERIALIZE_INFO(Angle); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_ENCODED_SLICES_INFO: + { + mfxExtEncodedSlicesInfo& info = *(mfxExtEncodedSlicesInfo*)pExtBuffer; + SERIALIZE_INFO(SliceSizeOverflow); + SERIALIZE_INFO(NumSliceNonCopliant); + SERIALIZE_INFO(NumEncodedSlice); + SERIALIZE_INFO(NumSliceSizeAlloc); + SERIALIZE_INFO_MEMORY(SliceSize,NumSliceSizeAlloc); + SERIALIZE_INFO(reserved1); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_VPP_SCALING: + { + mfxExtVPPScaling& info = *(mfxExtVPPScaling*)pExtBuffer; + SERIALIZE_INFO(ScalingMode); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_VPP_MIRRORING: + { + mfxExtVPPMirroring& info = *(mfxExtVPPMirroring*)pExtBuffer; + SERIALIZE_INFO(Type); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_MV_OVER_PIC_BOUNDARIES: + { + mfxExtMVOverPicBoundaries& info = *(mfxExtMVOverPicBoundaries*)pExtBuffer; + SERIALIZE_INFO(StickTop); + SERIALIZE_INFO(StickBottom); + SERIALIZE_INFO(StickLeft); + SERIALIZE_INFO(StickRight); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_VPP_COLORFILL: + { + mfxExtVPPColorFill& info = *(mfxExtVPPColorFill*)pExtBuffer; + SERIALIZE_INFO(Enable); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + case MFX_EXTBUFF_VP8_CODING_OPTION: + { + mfxExtVP8CodingOption& info = *(mfxExtVP8CodingOption*)pExtBuffer; + SERIALIZE_INFO(Version); + SERIALIZE_INFO(EnableMultipleSegments); + SERIALIZE_INFO(LoopFilterType); + SERIALIZE_INFO_ARRAY(LoopFilterLevel); + SERIALIZE_INFO(SharpnessLevel); + SERIALIZE_INFO(NumTokenPartitions); + SERIALIZE_INFO_ARRAY(LoopFilterRefTypeDelta); + SERIALIZE_INFO_ARRAY(LoopFilterMbModeDelta); + SERIALIZE_INFO_ARRAY(SegmentQPDelta); + SERIALIZE_INFO_ARRAY(CoeffTypeQPDelta); + SERIALIZE_INFO(WriteIVFHeaders); + SERIALIZE_INFO(NumFramesForIVFHeader); + SERIALIZE_INFO_ARRAY(reserved); + } + break; + } + // End of autogenerated code +} + +void CParametersDumper::SerializeVPPCompInputStream(msdk_ostream& sstr,msdk_string prefix,mfxVPPCompInputStream& info) +{ + SERIALIZE_INFO(DstX); + SERIALIZE_INFO(DstY); + SERIALIZE_INFO(DstW); + SERIALIZE_INFO(DstH); + + SERIALIZE_INFO(LumaKeyEnable); + SERIALIZE_INFO(LumaKeyMin); + SERIALIZE_INFO(LumaKeyMax); + + SERIALIZE_INFO(GlobalAlphaEnable); + SERIALIZE_INFO(GlobalAlpha); + + SERIALIZE_INFO(PixelAlphaEnable); + + SERIALIZE_INFO_ARRAY(reserved2); +} + + +void CParametersDumper::SerializeVideoParamStruct(msdk_ostream& sstr,msdk_string sectionName,mfxVideoParam& info,bool shouldUseVPPSection) +{ + msdk_string prefix=MSDK_STRING(""); + + sstr<> l && ss2 >> r) + { + if (l != r) + { + msdk_printf(MSDK_STRING("%s changed to %s \n"), l.c_str(), r.c_str()); + } + else + { + continue; + } + } +} diff --git a/vshampor/deshuffler/sample_common/src/plugin_utils.cpp b/vshampor/deshuffler/sample_common/src/plugin_utils.cpp new file mode 100644 index 0000000..cf1a4da --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/plugin_utils.cpp @@ -0,0 +1,199 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + +#include "plugin_utils.h" +#include "mfxvp8.h" +#include +#include + +bool AreGuidsEqual(const mfxPluginUID& guid1, const mfxPluginUID& guid2) +{ + for(size_t i = 0; i != sizeof(mfxPluginUID); i++) + { + if (guid1.Data[i] != guid2.Data[i]) + return false; + } + return true; +} + +mfxStatus ConvertStringToGuid(const msdk_string & strGuid, mfxPluginUID & mfxGuid) +{ + mfxStatus sts = MFX_ERR_NONE; + + // Check if symbolic GUID value + std::map uid; + uid[MSDK_STRING("hevcd_sw")] = MFX_PLUGINID_HEVCD_SW; + uid[MSDK_STRING("hevcd_hw")] = MFX_PLUGINID_HEVCD_HW; + + uid[MSDK_STRING("hevce_sw")] = MFX_PLUGINID_HEVCE_SW; + uid[MSDK_STRING("hevce_gacc")] = MFX_PLUGINID_HEVCE_GACC; + uid[MSDK_STRING("hevce_hw")] = MFX_PLUGINID_HEVCE_HW; + + uid[MSDK_STRING("vp8d_hw")] = MFX_PLUGINID_VP8D_HW; + uid[MSDK_STRING("vp8e_hw")] = MFX_PLUGINID_VP8E_HW; + + uid[MSDK_STRING("vp9d_hw")] = MFX_PLUGINID_VP9D_HW; + uid[MSDK_STRING("vp9e_hw")] = MFX_PLUGINID_VP9E_HW; + + uid[MSDK_STRING("camera_hw")] = MFX_PLUGINID_CAMERA_HW; + + uid[MSDK_STRING("capture_hw")] = MFX_PLUGINID_CAPTURE_HW; + + uid[MSDK_STRING("ptir_hw")] = MFX_PLUGINID_ITELECINE_HW; + uid[MSDK_STRING("h264_la_hw")] = MFX_PLUGINID_H264LA_HW; + uid[MSDK_STRING("aacd")] = MFX_PLUGINID_AACD; + uid[MSDK_STRING("aace")] = MFX_PLUGINID_AACE; + + uid[MSDK_STRING("hevce_fei_hw")] = MFX_PLUGINID_HEVCE_FEI_HW; + + if (uid.find(strGuid) == uid.end()) + { + mfxGuid = MSDK_PLUGINGUID_NULL; + sts = MFX_ERR_UNKNOWN; + } + else + { + mfxGuid = uid[strGuid]; + sts = MFX_ERR_NONE; + } + + // Check if plain GUID value + if (sts) + { + if (strGuid.size() != 32) + { + sts = MFX_ERR_UNKNOWN; + } + else + { + for (size_t i = 0; i < 16; i++) + { + unsigned int xx = 0; + msdk_stringstream ss; + ss << std::hex << strGuid.substr(i * 2, 2); + ss >> xx; + mfxGuid.Data[i] = (mfxU8)xx; + } + sts = MFX_ERR_NONE; + } + } + return sts; +} + +const mfxPluginUID & msdkGetPluginUID(mfxIMPL impl, msdkComponentType type, mfxU32 uCodecid) +{ + if (impl == MFX_IMPL_SOFTWARE) + { + switch(type) + { + case MSDK_VDECODE: + switch(uCodecid) + { + case MFX_CODEC_HEVC: + return MFX_PLUGINID_HEVCD_SW; + } + break; + case MSDK_VENCODE: + switch(uCodecid) + { + case MFX_CODEC_HEVC: + return MFX_PLUGINID_HEVCE_SW; + } + break; + } + } + else if (impl |= MFX_IMPL_HARDWARE) + { + switch(type) + { +// On Android implementaion of all decoders is placed in libmfx +// That's why we don't need default plugins for these codecs +#if !defined(ANDROID) + case MSDK_VDECODE: + switch(uCodecid) + { + case MFX_CODEC_HEVC: + return MFX_PLUGINID_HEVCD_HW; + case MFX_CODEC_VP8: + return MFX_PLUGINID_VP8D_HW; + case MFX_CODEC_VP9: + return MFX_PLUGINID_VP9D_HW; + } + break; +#endif + case MSDK_VENCODE: + switch(uCodecid) + { + case MFX_CODEC_HEVC: + return MFX_PLUGINID_HEVCE_HW; + case MFX_CODEC_VP8: + return MFX_PLUGINID_VP8E_HW; + } + break; +#if MFX_VERSION >= 1027 + case (MSDK_VENCODE | MSDK_FEI): + switch (uCodecid) + { + case MFX_CODEC_HEVC: + return MFX_PLUGINID_HEVC_FEI_ENCODE; + } + break; +#endif + case MSDK_VENC: + switch(uCodecid) + { + case MFX_CODEC_HEVC: + return MFX_PLUGINID_HEVCE_FEI_HW; // HEVC FEI uses ENC interface + } + break; + } + } + + return MSDK_PLUGINGUID_NULL; +} + +sPluginParams ParsePluginGuid(msdk_char* strPluginGuid) +{ + sPluginParams pluginParams; + mfxPluginUID uid; + mfxStatus sts = ConvertStringToGuid(strPluginGuid, uid); + + if (sts == MFX_ERR_NONE) + { + pluginParams.type = MFX_PLUGINLOAD_TYPE_GUID; + pluginParams.pluginGuid = uid; + } + + return pluginParams; +} + +sPluginParams ParsePluginPath(msdk_char* strPluginGuid) +{ + sPluginParams pluginParams; + + msdk_char tmpVal[MSDK_MAX_FILENAME_LEN]; + msdk_opt_read(strPluginGuid, tmpVal); + + MSDK_MAKE_BYTE_STRING(tmpVal, pluginParams.strPluginPath); + pluginParams.type = MFX_PLUGINLOAD_TYPE_FILE; + + return pluginParams; +} diff --git a/vshampor/deshuffler/sample_common/src/sample_utils.cpp b/vshampor/deshuffler/sample_common/src/sample_utils.cpp new file mode 100644 index 0000000..fb52f6a --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/sample_utils.cpp @@ -0,0 +1,2349 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + +#include +#include +#include +#include +#include + +#include "vm/strings_defs.h" +#include "time_statistics.h" +#include "sample_defs.h" +#include "sample_utils.h" +#include "mfxcommon.h" +#include "mfxjpeg.h" +#include "mfxvp8.h" + +#pragma warning( disable : 4748 ) + +msdk_tick CTimer::frequency = 0; +msdk_tick CTimeStatisticsReal::frequency = 0; + +mfxStatus CopyBitstream2(mfxBitstream *dest, mfxBitstream *src) +{ + if (!dest || !src) + return MFX_ERR_NULL_PTR; + + if (!dest->DataLength) + { + dest->DataOffset = 0; + } + else + { + memmove(dest->Data, dest->Data + dest->DataOffset, dest->DataLength); + dest->DataOffset = 0; + } + + if (src->DataLength > dest->MaxLength - dest->DataLength - dest->DataOffset) + return MFX_ERR_NOT_ENOUGH_BUFFER; + + MSDK_MEMCPY_BITSTREAM(*dest, dest->DataOffset, src->Data, src->DataLength); + dest->DataLength = src->DataLength; + + dest->DataFlag = src->DataFlag; + + //common Extended buffer will be for src and dest bit streams + dest->EncryptedData = src->EncryptedData; + + return MFX_ERR_NONE; +} + + +CSmplYUVReader::CSmplYUVReader() +{ + m_bInited = false; + m_ColorFormat = MFX_FOURCC_YV12; + shouldShiftP010High = false; +} + +mfxStatus CSmplYUVReader::Init(std::list inputs, mfxU32 ColorFormat, bool shouldShiftP010) +{ + Close(); + + if( MFX_FOURCC_NV12 != ColorFormat && + MFX_FOURCC_YV12 != ColorFormat && + MFX_FOURCC_I420 != ColorFormat && + MFX_FOURCC_YUY2 != ColorFormat && + MFX_FOURCC_RGB4 != ColorFormat && + MFX_FOURCC_BGR4 != ColorFormat && + MFX_FOURCC_P010 != ColorFormat && + MFX_FOURCC_P210 != ColorFormat) + { + return MFX_ERR_UNSUPPORTED; + } + + if(MFX_FOURCC_P010 == ColorFormat) + { + shouldShiftP010High = shouldShiftP010; + } + + if (!inputs.size()) + { + return MFX_ERR_UNSUPPORTED; + } + + for (ls_iterator it = inputs.begin(); it != inputs.end(); it++) + { + FILE *f = 0; + MSDK_FOPEN(f, (*it).c_str(), MSDK_STRING("rb")); + MSDK_CHECK_POINTER(f, MFX_ERR_NULL_PTR); + + m_files.push_back(f); + } + + m_ColorFormat = ColorFormat; + + m_bInited = true; + + return MFX_ERR_NONE; +} + +CSmplYUVReader::~CSmplYUVReader() +{ + Close(); +} + +void CSmplYUVReader::Close() +{ + for (mfxU32 i = 0; i < m_files.size(); i++) + { + fclose(m_files[i]); + } + m_files.clear(); + m_bInited = false; +} + +void CSmplYUVReader::Reset() +{ + for (mfxU32 i = 0; i < m_files.size(); i++) + { + fseek(m_files[i], 0, SEEK_SET); + } +} + +mfxStatus CSmplYUVReader::LoadNextFrame(mfxFrameSurface1* pSurface) +{ + // check if reader is initialized + MSDK_CHECK_ERROR(m_bInited, false, MFX_ERR_NOT_INITIALIZED); + MSDK_CHECK_POINTER(pSurface, MFX_ERR_NULL_PTR); + + mfxU32 nBytesRead; + mfxU16 w, h, i, pitch; + mfxU8 *ptr, *ptr2; + mfxFrameInfo& pInfo = pSurface->Info; + mfxFrameData& pData = pSurface->Data; + + mfxU32 vid = pInfo.FrameId.ViewId; + + if (vid > m_files.size()) + { + return MFX_ERR_UNSUPPORTED; + } + + if (pInfo.CropH > 0 && pInfo.CropW > 0) + { + w = pInfo.CropW; + h = pInfo.CropH; + } + else + { + w = pInfo.Width; + h = pInfo.Height; + } + + mfxU32 nBytesPerPixel = (pInfo.FourCC == MFX_FOURCC_P010 || pInfo.FourCC == MFX_FOURCC_P210) ? 2 : 1; + + if (MFX_FOURCC_YUY2 == pInfo.FourCC || MFX_FOURCC_RGB4 == pInfo.FourCC || MFX_FOURCC_BGR4 == pInfo.FourCC) + { + //Packed format: Luminance and chrominance are on the same plane + switch (m_ColorFormat) + { + case MFX_FOURCC_RGB4: + case MFX_FOURCC_BGR4: + + pitch = pData.Pitch; + ptr = MSDK_MIN( MSDK_MIN(pData.R, pData.G), pData.B); + ptr = ptr + pInfo.CropX + pInfo.CropY * pData.Pitch; + + for(i = 0; i < h; i++) + { + nBytesRead = (mfxU32)fread(ptr + i * pitch, 1, 4*w, m_files[vid]); + + if ((mfxU32)4*w != nBytesRead) + { + return MFX_ERR_MORE_DATA; + } + } + break; + case MFX_FOURCC_YUY2: + pitch = pData.Pitch; + ptr = pData.Y + pInfo.CropX + pInfo.CropY * pData.Pitch; + + for(i = 0; i < h; i++) + { + nBytesRead = (mfxU32)fread(ptr + i * pitch, 1, 2*w, m_files[vid]); + + if ((mfxU32)2*w != nBytesRead) + { + return MFX_ERR_MORE_DATA; + } + } + break; + default: + return MFX_ERR_UNSUPPORTED; + } + } + else if (MFX_FOURCC_NV12 == pInfo.FourCC || MFX_FOURCC_YV12 == pInfo.FourCC || MFX_FOURCC_P010 == pInfo.FourCC || MFX_FOURCC_P210 == pInfo.FourCC) + { + pitch = pData.Pitch; + ptr = pData.Y + pInfo.CropX + pInfo.CropY * pData.Pitch; + + // read luminance plane + for(i = 0; i < h; i++) + { + nBytesRead = (mfxU32)fread(ptr + i * pitch, nBytesPerPixel, w, m_files[vid]); + + if (w != nBytesRead) + { + return MFX_ERR_MORE_DATA; + } + + // Shifting data if required + if((MFX_FOURCC_P010 == pInfo.FourCC || MFX_FOURCC_P210 == pInfo.FourCC) && shouldShiftP010High) + { + mfxU16* shortPtr = (mfxU16*)(ptr + i * pitch); + for(int idx = 0; idx < w; idx++) + { + shortPtr[idx]<<=6; + } + } + } + + // read chroma planes + switch (m_ColorFormat) // color format of data in the input file + { + case MFX_FOURCC_I420: + case MFX_FOURCC_YV12: + switch (pInfo.FourCC) + { + case MFX_FOURCC_NV12: + + mfxU8 buf[2048]; // maximum supported chroma width for nv12 + mfxU32 j, dstOffset[2]; + w /= 2; + h /= 2; + ptr = pData.UV + pInfo.CropX + (pInfo.CropY / 2) * pitch; + if (w > 2048) + { + return MFX_ERR_UNSUPPORTED; + } + + if (m_ColorFormat == MFX_FOURCC_I420) { + dstOffset[0] = 0; + dstOffset[1] = 1; + } else { + dstOffset[0] = 1; + dstOffset[1] = 0; + } + + // load first chroma plane: U (input == I420) or V (input == YV12) + for (i = 0; i < h; i++) + { + nBytesRead = (mfxU32)fread(buf, 1, w, m_files[vid]); + if (w != nBytesRead) + { + return MFX_ERR_MORE_DATA; + } + for (j = 0; j < w; j++) + { + ptr[i * pitch + j * 2 + dstOffset[0]] = buf[j]; + } + } + + // load second chroma plane: V (input == I420) or U (input == YV12) + for (i = 0; i < h; i++) + { + + nBytesRead = (mfxU32)fread(buf, 1, w, m_files[vid]); + + if (w != nBytesRead) + { + return MFX_ERR_MORE_DATA; + } + for (j = 0; j < w; j++) + { + ptr[i * pitch + j * 2 + dstOffset[1]] = buf[j]; + } + } + + break; + case MFX_FOURCC_YV12: + w /= 2; + h /= 2; + pitch /= 2; + + if (m_ColorFormat == MFX_FOURCC_I420) { + ptr = pData.U + (pInfo.CropX / 2) + (pInfo.CropY / 2) * pitch; + ptr2 = pData.V + (pInfo.CropX / 2) + (pInfo.CropY / 2) * pitch; + } else { + ptr = pData.V + (pInfo.CropX / 2) + (pInfo.CropY / 2) * pitch; + ptr2 = pData.U + (pInfo.CropX / 2) + (pInfo.CropY / 2) * pitch; + } + + for(i = 0; i < h; i++) + { + + nBytesRead = (mfxU32)fread(ptr + i * pitch, 1, w, m_files[vid]); + + if (w != nBytesRead) + { + return MFX_ERR_MORE_DATA; + } + } + for(i = 0; i < h; i++) + { + nBytesRead = (mfxU32)fread(ptr2 + i * pitch, 1, w, m_files[vid]); + + if (w != nBytesRead) + { + return MFX_ERR_MORE_DATA; + } + } + break; + default: + return MFX_ERR_UNSUPPORTED; + } + break; + case MFX_FOURCC_NV12: + case MFX_FOURCC_P010: + case MFX_FOURCC_P210: + if (MFX_FOURCC_P210 != pInfo.FourCC) + { + h /= 2; + } + ptr = pData.UV + pInfo.CropX + (pInfo.CropY / 2) * pitch; + for(i = 0; i < h; i++) + { + nBytesRead = (mfxU32)fread(ptr + i * pitch, nBytesPerPixel, w, m_files[vid]); + + if (w != nBytesRead) + { + return MFX_ERR_MORE_DATA; + } + + // Shifting data if required + if((MFX_FOURCC_P010 == pInfo.FourCC || MFX_FOURCC_P210 == pInfo.FourCC) && shouldShiftP010High) + { + mfxU16* shortPtr = (mfxU16*)(ptr + i * pitch); + for(int idx = 0; idx < w; idx++) + { + shortPtr[idx]<<=6; + } + } + } + + break; + default: + return MFX_ERR_UNSUPPORTED; + } + } + + return MFX_ERR_NONE; +} + +CSmplBitstreamWriter::CSmplBitstreamWriter() +{ + m_fSource = NULL; + m_bInited = false; + m_nProcessedFramesNum = 0; +} + +CSmplBitstreamWriter::~CSmplBitstreamWriter() +{ + Close(); +} + +void CSmplBitstreamWriter::Close() +{ + if (m_fSource) + { + fclose(m_fSource); + m_fSource = NULL; + } + + m_bInited = false; +} + +mfxStatus CSmplBitstreamWriter::Init(const msdk_char *strFileName) +{ + MSDK_CHECK_POINTER(strFileName, MFX_ERR_NULL_PTR); + if (!msdk_strlen(strFileName)) + return MFX_ERR_NONE; + + Close(); + + //init file to write encoded data + MSDK_FOPEN(m_fSource, strFileName, MSDK_STRING("wb+")); + MSDK_CHECK_POINTER(m_fSource, MFX_ERR_NULL_PTR); + + m_sFile = msdk_string(strFileName); + //set init state to true in case of success + m_bInited = true; + return MFX_ERR_NONE; +} + +mfxStatus CSmplBitstreamWriter::Reset() +{ + return Init(m_sFile.c_str()); +} + +mfxStatus CSmplBitstreamWriter::WriteNextFrame(mfxBitstream *pMfxBitstream, bool isPrint) +{ + // check if writer is initialized + MSDK_CHECK_ERROR(m_bInited, false, MFX_ERR_NOT_INITIALIZED); + MSDK_CHECK_POINTER(pMfxBitstream, MFX_ERR_NULL_PTR); + + mfxU32 nBytesWritten = 0; + + nBytesWritten = (mfxU32)fwrite(pMfxBitstream->Data + pMfxBitstream->DataOffset, 1, pMfxBitstream->DataLength, m_fSource); + MSDK_CHECK_NOT_EQUAL(nBytesWritten, pMfxBitstream->DataLength, MFX_ERR_UNDEFINED_BEHAVIOR); + + // mark that we don't need bit stream data any more + pMfxBitstream->DataLength = 0; + + m_nProcessedFramesNum++; + + // print encoding progress to console every certain number of frames (not to affect performance too much) + if (isPrint && (1 == m_nProcessedFramesNum || (0 == (m_nProcessedFramesNum % 100)))) + { + msdk_printf(MSDK_STRING("Frame number: %u\r"), m_nProcessedFramesNum); + } + + return MFX_ERR_NONE; +} + + +CSmplBitstreamDuplicateWriter::CSmplBitstreamDuplicateWriter() + : CSmplBitstreamWriter() +{ + m_fSourceDuplicate = NULL; + m_bJoined = false; +} + +mfxStatus CSmplBitstreamDuplicateWriter::InitDuplicate(const msdk_char *strFileName) +{ + MSDK_CHECK_POINTER(strFileName, MFX_ERR_NULL_PTR); + MSDK_CHECK_ERROR(msdk_strlen(strFileName), 0, MFX_ERR_NOT_INITIALIZED); + + if (m_fSourceDuplicate) + { + fclose(m_fSourceDuplicate); + m_fSourceDuplicate = NULL; + } + MSDK_FOPEN(m_fSourceDuplicate, strFileName, MSDK_STRING("wb+")); + MSDK_CHECK_POINTER(m_fSourceDuplicate, MFX_ERR_NULL_PTR); + + m_bJoined = false; // mark we own the file handle + + return MFX_ERR_NONE; +} + +mfxStatus CSmplBitstreamDuplicateWriter::JoinDuplicate(CSmplBitstreamDuplicateWriter *pJoinee) +{ + MSDK_CHECK_POINTER(pJoinee, MFX_ERR_NULL_PTR); + MSDK_CHECK_ERROR(pJoinee->m_fSourceDuplicate, NULL, MFX_ERR_NOT_INITIALIZED); + + m_fSourceDuplicate = pJoinee->m_fSourceDuplicate; + m_bJoined = true; // mark we do not own the file handle + + return MFX_ERR_NONE; +} + +mfxStatus CSmplBitstreamDuplicateWriter::WriteNextFrame(mfxBitstream *pMfxBitstream, bool isPrint) +{ + MSDK_CHECK_ERROR(m_fSourceDuplicate, NULL, MFX_ERR_NOT_INITIALIZED); + MSDK_CHECK_POINTER(pMfxBitstream, MFX_ERR_NULL_PTR); + + mfxU32 nBytesWritten = (mfxU32)fwrite(pMfxBitstream->Data + pMfxBitstream->DataOffset, 1, pMfxBitstream->DataLength, m_fSourceDuplicate); + MSDK_CHECK_NOT_EQUAL(nBytesWritten, pMfxBitstream->DataLength, MFX_ERR_UNDEFINED_BEHAVIOR); + + CSmplBitstreamWriter::WriteNextFrame(pMfxBitstream, isPrint); + + return MFX_ERR_NONE; +} + +void CSmplBitstreamDuplicateWriter::Close() +{ + if (m_fSourceDuplicate && !m_bJoined) + { + fclose(m_fSourceDuplicate); + } + + m_fSourceDuplicate = NULL; + m_bJoined = false; + + CSmplBitstreamWriter::Close(); +} + +CSmplBitstreamReader::CSmplBitstreamReader() +{ + m_fSource = NULL; + m_bInited = false; +} + +CSmplBitstreamReader::~CSmplBitstreamReader() +{ + Close(); +} + +void CSmplBitstreamReader::Close() +{ + if (m_fSource) + { + fclose(m_fSource); + m_fSource = NULL; + } + + m_bInited = false; +} + +void CSmplBitstreamReader::Reset() +{ + if (!m_bInited) + return; + + fseek(m_fSource, 0, SEEK_SET); +} + +mfxStatus CSmplBitstreamReader::Init(const msdk_char *strFileName) +{ + MSDK_CHECK_POINTER(strFileName, MFX_ERR_NULL_PTR); + if (!msdk_strlen(strFileName)) + return MFX_ERR_NONE; + + Close(); + + //open file to read input stream + MSDK_FOPEN(m_fSource, strFileName, MSDK_STRING("rb")); + MSDK_CHECK_POINTER(m_fSource, MFX_ERR_NULL_PTR); + + m_bInited = true; + return MFX_ERR_NONE; +} + +mfxStatus CSmplBitstreamReader::ReadNextFrame(mfxBitstream *pBS) +{ + if (!m_bInited) + return MFX_ERR_NOT_INITIALIZED; + + MSDK_CHECK_POINTER(pBS, MFX_ERR_NULL_PTR); + + mfxU32 nBytesRead = 0; + + memmove(pBS->Data, pBS->Data + pBS->DataOffset, pBS->DataLength); + pBS->DataOffset = 0; + nBytesRead = (mfxU32)fread(pBS->Data + pBS->DataLength, 1, pBS->MaxLength - pBS->DataLength, m_fSource); + + if (0 == nBytesRead) + { + return MFX_ERR_MORE_DATA; + } + + pBS->DataLength += nBytesRead; + + return MFX_ERR_NONE; +} + + +mfxU32 CJPEGFrameReader::FindMarker(mfxBitstream *pBS,mfxU32 startOffset,CJPEGFrameReader::JPEGMarker marker) +{ + for (mfxU32 i = startOffset; i + sizeof(mfxU16) <= pBS->DataLength; i++) + { + if ( *(mfxU16*)(pBS->Data + i)==(mfxU16)marker) + { + return i; + } + } + return 0xFFFFFFFF; +} + +mfxStatus CJPEGFrameReader::ReadNextFrame(mfxBitstream *pBS) +{ + mfxStatus sts = MFX_ERR_NONE; + mfxU32 offsetSOI=0xFFFFFFFF; + + pBS->DataFlag = MFX_BITSTREAM_COMPLETE_FRAME; + + while ((offsetSOI = FindMarker(pBS,pBS->DataOffset,CJPEGFrameReader::SOI))==0xFFFFFFFF && sts == MFX_ERR_NONE) + { + sts = CSmplBitstreamReader::ReadNextFrame(pBS); + } + + //--- Finding EOI of frame, to make sure that it is complete + while (FindMarker(pBS,offsetSOI,CJPEGFrameReader::EOI)==0xFFFFFFFF && sts == MFX_ERR_NONE) + { + sts = CSmplBitstreamReader::ReadNextFrame(pBS); + } + + return sts; +} + +CIVFFrameReader::CIVFFrameReader() +{ + MSDK_ZERO_MEMORY(m_hdr); +} + +#define READ_BYTES(pBuf, size)\ +{\ + mfxU32 nBytesRead = (mfxU32)fread(pBuf, 1, size, m_fSource);\ + if (nBytesRead !=size)\ + return MFX_ERR_MORE_DATA;\ +}\ + +mfxStatus CIVFFrameReader::Init(const msdk_char *strFileName) +{ + mfxStatus sts = CSmplBitstreamReader::Init(strFileName); + MSDK_CHECK_STATUS(sts, "CSmplBitstreamReader::Init failed"); + + // read and skip IVF header + READ_BYTES(&m_hdr.dkif, sizeof(m_hdr.dkif)); + READ_BYTES(&m_hdr.version, sizeof(m_hdr.version)); + READ_BYTES(&m_hdr.header_len, sizeof(m_hdr.header_len)); + READ_BYTES(&m_hdr.codec_FourCC, sizeof(m_hdr.codec_FourCC)); + READ_BYTES(&m_hdr.width, sizeof(m_hdr.width)); + READ_BYTES(&m_hdr.height, sizeof(m_hdr.height)); + READ_BYTES(&m_hdr.frame_rate, sizeof(m_hdr.frame_rate)); + READ_BYTES(&m_hdr.time_scale, sizeof(m_hdr.time_scale)); + READ_BYTES(&m_hdr.num_frames, sizeof(m_hdr.num_frames)); + READ_BYTES(&m_hdr.unused, sizeof(m_hdr.unused)); + MSDK_CHECK_NOT_EQUAL(fseek(m_fSource, m_hdr.header_len, SEEK_SET), 0, MFX_ERR_UNSUPPORTED); + + // check header + MSDK_CHECK_NOT_EQUAL(MFX_MAKEFOURCC('D','K','I','F'), m_hdr.dkif, MFX_ERR_UNSUPPORTED); + if ((m_hdr.codec_FourCC != MFX_MAKEFOURCC('V','P','8','0')) && + (m_hdr.codec_FourCC != MFX_MAKEFOURCC('V','P','9','0'))) + { + return MFX_ERR_UNSUPPORTED; + } + + return MFX_ERR_NONE; +} + +// reads a complete frame into given bitstream +mfxStatus CIVFFrameReader::ReadNextFrame(mfxBitstream *pBS) +{ + MSDK_CHECK_POINTER(pBS, MFX_ERR_NULL_PTR); + + memmove(pBS->Data, pBS->Data + pBS->DataOffset, pBS->DataLength); + pBS->DataOffset = 0; + pBS->DataFlag = MFX_BITSTREAM_COMPLETE_FRAME; + + /*bytes pos-(pos+3) size of frame in bytes (not including the 12-byte header) + bytes (pos+4)-(pos+11) 64-bit presentation timestamp + bytes (pos+12)-(pos+12+nBytesInFrame) frame data + */ + + mfxU32 nBytesInFrame = 0; + mfxU64 nTimeStamp = 0; + + // read frame size + READ_BYTES(&nBytesInFrame, sizeof(nBytesInFrame)); + // read time stamp + READ_BYTES(&nTimeStamp, sizeof(nTimeStamp)); + //check if bitstream has enough space to hold the frame + if (nBytesInFrame > pBS->MaxLength - pBS->DataLength - pBS->DataOffset) + return MFX_ERR_NOT_ENOUGH_BUFFER; + + // read frame data + READ_BYTES(pBS->Data + pBS->DataOffset + pBS->DataLength, nBytesInFrame); + pBS->DataLength += nBytesInFrame; + + // it is application's responsibility to make sure the bitstream contains a single complete frame and nothing else + // application has to provide input pBS with pBS->DataLength = 0 + + return MFX_ERR_NONE; +} + + +CSmplYUVWriter::CSmplYUVWriter() +{ + m_bInited = false; + m_bIsMultiView = false; + m_fDest = NULL; + m_fDestMVC = NULL; + m_numCreatedFiles = 0; + m_nViews = 0; +}; + +mfxStatus CSmplYUVWriter::Init(const msdk_char *strFileName, const mfxU32 numViews) +{ + MSDK_CHECK_POINTER(strFileName, MFX_ERR_NULL_PTR); + MSDK_CHECK_ERROR(msdk_strlen(strFileName), 0, MFX_ERR_NOT_INITIALIZED); + + m_sFile = msdk_string(strFileName); + m_nViews = numViews; + + Close(); + + //open file to write decoded data + + if (!m_bIsMultiView) + { + MSDK_FOPEN(m_fDest, m_sFile.c_str(), MSDK_STRING("wb")); + MSDK_CHECK_POINTER(m_fDest, MFX_ERR_NULL_PTR); + ++m_numCreatedFiles; + } + else + { + mfxU32 i; + + MSDK_CHECK_ERROR(numViews, 0, MFX_ERR_NOT_INITIALIZED); + + m_fDestMVC = new FILE*[numViews]; + for (i = 0; i < numViews; ++i) + { + MSDK_FOPEN(m_fDestMVC[i], FormMVCFileName(m_sFile.c_str(), i).c_str(), MSDK_STRING("wb")); + MSDK_CHECK_POINTER(m_fDestMVC[i], MFX_ERR_NULL_PTR); + ++m_numCreatedFiles; + } + } + + m_bInited = true; + + return MFX_ERR_NONE; +} + +mfxStatus CSmplYUVWriter::Reset() +{ + if (!m_bInited) + return MFX_ERR_NONE; + + return Init(m_sFile.c_str(), m_nViews); +} + +CSmplYUVWriter::~CSmplYUVWriter() +{ + Close(); +} + +void CSmplYUVWriter::Close() +{ + if (m_fDest) + { + fclose(m_fDest); + m_fDest = NULL; + } + + if (m_fDestMVC) + { + mfxU32 i = 0; + for (i = 0; i < m_numCreatedFiles; ++i) + { + if (m_fDestMVC[i] != NULL) + { + fclose(m_fDestMVC[i]); + m_fDestMVC[i] = NULL; + } + } + delete m_fDestMVC; + m_fDestMVC = NULL; + } + + m_numCreatedFiles = 0; + m_bInited = false; +} + +mfxStatus CSmplYUVWriter::WriteNextFrame(mfxFrameSurface1 *pSurface) +{ + MSDK_CHECK_ERROR(m_bInited, false, MFX_ERR_NOT_INITIALIZED); + MSDK_CHECK_POINTER(pSurface, MFX_ERR_NULL_PTR); + + mfxFrameInfo &pInfo = pSurface->Info; + mfxFrameData &pData = pSurface->Data; + + mfxU32 i, h, w; + mfxU32 vid = pInfo.FrameId.ViewId; + + // Temporary buffer to convert MS-P010 to P010 + std::vector tmp; + + if (!m_bIsMultiView) + { + MSDK_CHECK_POINTER(m_fDest, MFX_ERR_NULL_PTR); + } + else + { + MSDK_CHECK_POINTER(m_fDestMVC, MFX_ERR_NULL_PTR); + MSDK_CHECK_POINTER(m_fDestMVC[vid], MFX_ERR_NULL_PTR); + } + + FILE* dstFile = m_bIsMultiView ? m_fDestMVC[vid] : m_fDest; + + switch (pInfo.FourCC) + { + case MFX_FOURCC_YV12: + case MFX_FOURCC_NV12: + for (i = 0; i < pInfo.CropH; i++) + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.Y + (pInfo.CropY * pData.Pitch + pInfo.CropX)+ i * pData.Pitch, 1, pInfo.CropW, dstFile), + pInfo.CropW, MFX_ERR_UNDEFINED_BEHAVIOR); + } + break; + case MFX_FOURCC_P010: + case MFX_FOURCC_P210: + { + for (i = 0; i < pInfo.CropH; i++) + { + mfxU16* shortPtr = (mfxU16*)(pData.Y + (pInfo.CropY * pData.Pitch + pInfo.CropX) + i * pData.Pitch); + if (pInfo.Shift) + { + // Convert MS-P010 to P010 and write + tmp.resize(pData.Pitch); + + for (int idx = 0; idx < pInfo.CropW; idx++) + { + tmp[idx] = shortPtr[idx] >> 6; + } + + MSDK_CHECK_NOT_EQUAL( + fwrite(&tmp[0], 1, (mfxU32)pInfo.CropW * 2, dstFile), + (mfxU32)pInfo.CropW * 2, MFX_ERR_UNDEFINED_BEHAVIOR); + + } + else + { + MSDK_CHECK_NOT_EQUAL( + fwrite(shortPtr, 1, (mfxU32)pInfo.CropW * 2, dstFile), + (mfxU32)pInfo.CropW * 2, MFX_ERR_UNDEFINED_BEHAVIOR); + } + } + + break; + } + case MFX_FOURCC_RGB4: + case 100: //DXGI_FORMAT_AYUV + case MFX_FOURCC_A2RGB10: + // Implementation for RGB4 and A2RGB10 in the next switch below + break; + + default: + return MFX_ERR_UNSUPPORTED; + } + switch (pInfo.FourCC) + { + case MFX_FOURCC_YV12: + { + for (i = 0; i < (mfxU32) pInfo.CropH/2; i++) + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.V + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX / 2)+ i * pData.Pitch, 1, pInfo.CropW, dstFile), + (mfxU32)pInfo.CropW/2, MFX_ERR_UNDEFINED_BEHAVIOR); + } + for (i = 0; i < (mfxU32)pInfo.CropH/2; i++) + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.U + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX / 2)+ i * pData.Pitch / 2, 1, pInfo.CropW/2, dstFile), + (mfxU32)pInfo.CropW/2, MFX_ERR_UNDEFINED_BEHAVIOR); + } + break; + } + case MFX_FOURCC_NV12: + { + for (i = 0; i < (mfxU32) pInfo.CropH/2; i++) + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.UV + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX / 2) + i * pData.Pitch, 1, pInfo.CropW, dstFile), + pInfo.CropW, MFX_ERR_UNDEFINED_BEHAVIOR); + } + break; + } + case MFX_FOURCC_P010: + case MFX_FOURCC_P210: + { + mfxU32 height = pInfo.FourCC == MFX_FOURCC_P010 ? (mfxU32)pInfo.CropH / 2 : (mfxU32)pInfo.CropH; + + for (i = 0; i < height; i++) + { + mfxU16* shortPtr = (mfxU16*)(pData.UV + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX) + i * pData.Pitch); + if (pInfo.Shift) + { + // Convert MS-P010 to P010 and write + tmp.resize(pData.Pitch); + + for (int idx = 0; idx < pInfo.CropW; idx++) + { + tmp[idx] = shortPtr[idx] >> 6; + } + + MSDK_CHECK_NOT_EQUAL( + fwrite(&tmp[0], 1, (mfxU32)pInfo.CropW * 2, dstFile), + (mfxU32)pInfo.CropW * 2, MFX_ERR_UNDEFINED_BEHAVIOR); + + } + else + { + MSDK_CHECK_NOT_EQUAL( + fwrite(shortPtr, 1, (mfxU32)pInfo.CropW * 2, dstFile), + (mfxU32)pInfo.CropW * 2, MFX_ERR_UNDEFINED_BEHAVIOR); + } + } + break; + } + + case MFX_FOURCC_RGB4: + case 100: //DXGI_FORMAT_AYUV + case MFX_FOURCC_A2RGB10: + { + mfxU8* ptr; + + if (pInfo.CropH > 0 && pInfo.CropW > 0) + { + w = pInfo.CropW; + h = pInfo.CropH; + } + else + { + w = pInfo.Width; + h = pInfo.Height; + } + + ptr = MSDK_MIN( MSDK_MIN(pData.R, pData.G), pData.B); + ptr = ptr + pInfo.CropX + pInfo.CropY * pData.Pitch; + + for(i = 0; i < h; i++) + { + MSDK_CHECK_NOT_EQUAL(fwrite(ptr + i * pData.Pitch, 1, 4*w, dstFile), 4*w, MFX_ERR_UNDEFINED_BEHAVIOR); + } + fflush(dstFile); + break; + } + + default: + return MFX_ERR_UNSUPPORTED; + } + + return MFX_ERR_NONE; +} + +mfxStatus CSmplYUVWriter::WriteNextFrameI420(mfxFrameSurface1 *pSurface) +{ + MSDK_CHECK_ERROR(m_bInited, false, MFX_ERR_NOT_INITIALIZED); + MSDK_CHECK_POINTER(pSurface, MFX_ERR_NULL_PTR); + + mfxFrameInfo &pInfo = pSurface->Info; + mfxFrameData &pData = pSurface->Data; + + mfxU32 i, j, h, w; + mfxU32 vid = pInfo.FrameId.ViewId; + + if (!m_bIsMultiView) + { + MSDK_CHECK_POINTER(m_fDest, MFX_ERR_NULL_PTR); + } + else + { + MSDK_CHECK_POINTER(m_fDestMVC, MFX_ERR_NULL_PTR); + MSDK_CHECK_POINTER(m_fDestMVC[vid], MFX_ERR_NULL_PTR); + } + + // Write Y + switch (pInfo.FourCC) + { + case MFX_FOURCC_YV12: + case MFX_FOURCC_NV12: + { + for (i = 0; i < pInfo.CropH; i++) + { + if (!m_bIsMultiView) + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.Y + (pInfo.CropY * pData.Pitch + pInfo.CropX)+ i * pData.Pitch, 1, pInfo.CropW, m_fDest), + pInfo.CropW, MFX_ERR_UNDEFINED_BEHAVIOR); + } + else + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.Y + (pInfo.CropY * pData.Pitch + pInfo.CropX)+ i * pData.Pitch, 1, pInfo.CropW, m_fDestMVC[vid]), + pInfo.CropW, MFX_ERR_UNDEFINED_BEHAVIOR); + } + } + break; + } + default: + { + msdk_printf(MSDK_STRING("ERROR: I420 output is accessible only for NV12 and YV12.\n")); + return MFX_ERR_UNSUPPORTED; + } + } + + // Write U and V + switch (pInfo.FourCC) + { + case MFX_FOURCC_YV12: + { + for (i = 0; i < (mfxU32) pInfo.CropH/2; i++) + { + if (!m_bIsMultiView) + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.U + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX / 2)+ i * pData.Pitch / 2, 1, pInfo.CropW/2, m_fDest), + (mfxU32)pInfo.CropW/2, MFX_ERR_UNDEFINED_BEHAVIOR); + } + else + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.U + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX / 2)+ i * pData.Pitch / 2, 1, pInfo.CropW/2, m_fDestMVC[vid]), + (mfxU32)pInfo.CropW/2, MFX_ERR_UNDEFINED_BEHAVIOR); + } + } + for (i = 0; i < (mfxU32)pInfo.CropH/2; i++) + { + if (!m_bIsMultiView) + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.V + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX / 2)+ i * pData.Pitch / 2, 1, pInfo.CropW/2, m_fDest), + (mfxU32)pInfo.CropW/2, MFX_ERR_UNDEFINED_BEHAVIOR); + } + else + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.V + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX / 2)+ i * pData.Pitch / 2, 1, pInfo.CropW/2, m_fDestMVC[vid]), + (mfxU32)pInfo.CropW/2, MFX_ERR_UNDEFINED_BEHAVIOR); + } + } + break; + } + case MFX_FOURCC_NV12: + { + h = pInfo.CropH / 2; + w = pInfo.CropW; + for (i = 0; i < h; i++) + { + for (j = 0; j < w; j += 2) + { + if (!m_bIsMultiView) + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.UV + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX) + i * pData.Pitch + j, 1, 1, m_fDest), + 1, MFX_ERR_UNDEFINED_BEHAVIOR); + } + else + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.UV + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX) + i * pData.Pitch + j, 1, 1, m_fDestMVC[vid]), + 1, MFX_ERR_UNDEFINED_BEHAVIOR); + } + } + } + for (i = 0; i < h; i++) + { + for (j = 1; j < w; j += 2) + { + if (!m_bIsMultiView) + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.UV + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX)+ i * pData.Pitch + j, 1, 1, m_fDest), + 1, MFX_ERR_UNDEFINED_BEHAVIOR); + } + else + { + MSDK_CHECK_NOT_EQUAL( + fwrite(pData.UV + (pInfo.CropY * pData.Pitch / 2 + pInfo.CropX)+ i * pData.Pitch + j, 1, 1, m_fDestMVC[vid]), + 1, MFX_ERR_UNDEFINED_BEHAVIOR); + } + } + } + break; + } + default: + { + msdk_printf(MSDK_STRING("ERROR: I420 output is accessible only for NV12 and YV12.\n")); + return MFX_ERR_UNSUPPORTED; + } + } + + return MFX_ERR_NONE; +} + +mfxStatus ConvertFrameRate(mfxF64 dFrameRate, mfxU32* pnFrameRateExtN, mfxU32* pnFrameRateExtD) +{ + MSDK_CHECK_POINTER(pnFrameRateExtN, MFX_ERR_NULL_PTR); + MSDK_CHECK_POINTER(pnFrameRateExtD, MFX_ERR_NULL_PTR); + + mfxU32 fr; + + fr = (mfxU32)(dFrameRate + .5); + + if (fabs(fr - dFrameRate) < 0.0001) + { + *pnFrameRateExtN = fr; + *pnFrameRateExtD = 1; + return MFX_ERR_NONE; + } + + fr = (mfxU32)(dFrameRate * 1.001 + .5); + + if (fabs(fr * 1000 - dFrameRate * 1001) < 10) + { + *pnFrameRateExtN = fr * 1000; + *pnFrameRateExtD = 1001; + return MFX_ERR_NONE; + } + + *pnFrameRateExtN = (mfxU32)(dFrameRate * 10000 + .5); + *pnFrameRateExtD = 10000; + + return MFX_ERR_NONE; +} + +mfxF64 CalculateFrameRate(mfxU32 nFrameRateExtN, mfxU32 nFrameRateExtD) +{ + if (nFrameRateExtN && nFrameRateExtD) + return (mfxF64)nFrameRateExtN / nFrameRateExtD; + else + return 0; +} + +mfxU16 GetFreeSurfaceIndex(mfxFrameSurface1* pSurfacesPool, mfxU16 nPoolSize) +{ + if (pSurfacesPool) + { + for (mfxU16 i = 0; i < nPoolSize; i++) + { + if (0 == pSurfacesPool[i].Data.Locked) + { + return i; + } + } + } + + return MSDK_INVALID_SURF_IDX; +} + +mfxU16 GetFreeSurface(mfxFrameSurface1* pSurfacesPool, mfxU16 nPoolSize) +{ + mfxU32 SleepInterval = 10; // milliseconds + + mfxU16 idx = MSDK_INVALID_SURF_IDX; + + CTimer t; + t.Start(); + //wait if there's no free surface + do + { + idx = GetFreeSurfaceIndex(pSurfacesPool, nPoolSize); + + if (MSDK_INVALID_SURF_IDX != idx) + { + break; + } + else + { + MSDK_SLEEP(SleepInterval); + } + } while ( t.GetTime() < MSDK_SURFACE_WAIT_INTERVAL / 1000 ); + + if(idx==MSDK_INVALID_SURF_IDX) + { + msdk_printf(MSDK_STRING("ERROR: No free surfaces in pool (during long period)\n")); + } + + return idx; +} + +mfxStatus InitMfxBitstream(mfxBitstream* pBitstream, mfxU32 nSize) +{ + //check input params + MSDK_CHECK_POINTER(pBitstream, MFX_ERR_NULL_PTR); + MSDK_CHECK_ERROR(nSize, 0, MFX_ERR_NOT_INITIALIZED); + + //prepare pBitstream + WipeMfxBitstream(pBitstream); + + //prepare buffer + pBitstream->Data = new mfxU8[nSize]; + MSDK_CHECK_POINTER(pBitstream->Data, MFX_ERR_MEMORY_ALLOC); + + pBitstream->MaxLength = nSize; + + return MFX_ERR_NONE; +} + +mfxStatus ExtendMfxBitstream(mfxBitstream* pBitstream, mfxU32 nSize) +{ + MSDK_CHECK_POINTER(pBitstream, MFX_ERR_NULL_PTR); + + MSDK_CHECK_ERROR(nSize <= pBitstream->MaxLength, true, MFX_ERR_UNSUPPORTED); + + mfxU8* pData = new mfxU8[nSize]; + MSDK_CHECK_POINTER(pData, MFX_ERR_MEMORY_ALLOC); + + memmove(pData, pBitstream->Data + pBitstream->DataOffset, pBitstream->DataLength); + + WipeMfxBitstream(pBitstream); + + pBitstream->Data = pData; + pBitstream->DataOffset = 0; + pBitstream->MaxLength = nSize; + + return MFX_ERR_NONE; +} + +void WipeMfxBitstream(mfxBitstream* pBitstream) +{ + if(pBitstream) + { + //free allocated memory + MSDK_SAFE_DELETE_ARRAY(pBitstream->Data); + } +} + +std::basic_string CodecIdToStr(mfxU32 nFourCC) +{ + std::basic_string fcc; + for (size_t i = 0; i < 4; i++) + { + fcc.push_back((msdk_char)*(i + (char*)&nFourCC)); + } + return fcc; +} + +PartiallyLinearFNC::PartiallyLinearFNC() +: m_pX() +, m_pY() +, m_nPoints() +, m_nAllocated() +{ +} + +PartiallyLinearFNC::~PartiallyLinearFNC() +{ + delete []m_pX; + m_pX = NULL; + delete []m_pY; + m_pY = NULL; +} + +void PartiallyLinearFNC::AddPair(mfxF64 x, mfxF64 y) +{ + //duplicates searching + for (mfxU32 i = 0; i < m_nPoints; i++) + { + if (m_pX[i] == x) + return; + } + if (m_nPoints == m_nAllocated) + { + m_nAllocated += 20; + mfxF64 * pnew; + pnew = new mfxF64[m_nAllocated]; + //memcpy_s(pnew, sizeof(mfxF64)*m_nAllocated, m_pX, sizeof(mfxF64) * m_nPoints); + MSDK_MEMCPY_BUF(pnew,0,sizeof(mfxF64)*m_nAllocated, m_pX,sizeof(mfxF64) * m_nPoints); + delete [] m_pX; + m_pX = pnew; + + pnew = new mfxF64[m_nAllocated]; + //memcpy_s(pnew, sizeof(mfxF64)*m_nAllocated, m_pY, sizeof(mfxF64) * m_nPoints); + MSDK_MEMCPY_BUF(pnew,0,sizeof(mfxF64)*m_nAllocated, m_pY,sizeof(mfxF64) * m_nPoints); + delete [] m_pY; + m_pY = pnew; + } + m_pX[m_nPoints] = x; + m_pY[m_nPoints] = y; + + m_nPoints ++; +} + +mfxF64 PartiallyLinearFNC::at(mfxF64 x) +{ + if (m_nPoints < 2) + { + return 0; + } + bool bwasmin = false; + bool bwasmax = false; + + mfxU32 maxx = 0; + mfxU32 minx = 0; + mfxU32 i; + + for (i=0; i < m_nPoints; i++) + { + if (m_pX[i] <= x && (!bwasmin || m_pX[i] > m_pX[maxx])) + { + maxx = i; + bwasmin = true; + } + if (m_pX[i] > x && (!bwasmax || m_pX[i] < m_pX[minx])) + { + minx = i; + bwasmax = true; + } + } + + //point on the left + if (!bwasmin) + { + for (i=0; i < m_nPoints; i++) + { + if (m_pX[i] > m_pX[minx] && (!bwasmin || m_pX[i] < m_pX[minx])) + { + maxx = i; + bwasmin = true; + } + } + } + //point on the right + if (!bwasmax) + { + for (i=0; i < m_nPoints; i++) + { + if (m_pX[i] < m_pX[maxx] && (!bwasmax || m_pX[i] > m_pX[minx])) + { + minx = i; + bwasmax = true; + } + } + } + + //linear interpolation + return (x - m_pX[minx])*(m_pY[maxx] - m_pY[minx]) / (m_pX[maxx] - m_pX[minx]) + m_pY[minx]; +} + +mfxU16 CalculateDefaultBitrate(mfxU32 nCodecId, mfxU32 nTargetUsage, mfxU32 nWidth, mfxU32 nHeight, mfxF64 dFrameRate) +{ + PartiallyLinearFNC fnc; + mfxF64 bitrate = 0; + + switch (nCodecId) + { + case MFX_CODEC_AVC : + { + fnc.AddPair(0, 0); + fnc.AddPair(25344, 225); + fnc.AddPair(101376, 1000); + fnc.AddPair(414720, 4000); + fnc.AddPair(2058240, 5000); + break; + } + case MFX_CODEC_MPEG2: + { + fnc.AddPair(0, 0); + fnc.AddPair(414720, 12000); + break; + } + default: + { + fnc.AddPair(0, 0); + fnc.AddPair(414720, 12000); + break; + } + } + + mfxF64 at = nWidth * nHeight * dFrameRate / 30.0; + + if (!at) + return 0; + + switch (nTargetUsage) + { + case MFX_TARGETUSAGE_BEST_QUALITY : + { + bitrate = (&fnc)->at(at); + break; + } + case MFX_TARGETUSAGE_BEST_SPEED : + { + bitrate = (&fnc)->at(at) * 0.5; + break; + } + case MFX_TARGETUSAGE_BALANCED : + default: + { + bitrate = (&fnc)->at(at) * 0.75; + break; + } + } + + return (mfxU16)bitrate; +} + +mfxU16 StrToTargetUsage(msdk_string strInput) +{ + std::map tu; + tu[MSDK_STRING("quality")] = (mfxU16)MFX_TARGETUSAGE_1; + tu[MSDK_STRING("veryslow")] = (mfxU16)MFX_TARGETUSAGE_1; + tu[MSDK_STRING("slower")] = (mfxU16)MFX_TARGETUSAGE_2; + tu[MSDK_STRING("slow")] = (mfxU16)MFX_TARGETUSAGE_3; + tu[MSDK_STRING("medium")] = (mfxU16)MFX_TARGETUSAGE_4; + tu[MSDK_STRING("balanced")] = (mfxU16)MFX_TARGETUSAGE_4; + tu[MSDK_STRING("fast")] = (mfxU16)MFX_TARGETUSAGE_5; + tu[MSDK_STRING("faster")] = (mfxU16)MFX_TARGETUSAGE_6; + tu[MSDK_STRING("veryfast")] = (mfxU16)MFX_TARGETUSAGE_7; + tu[MSDK_STRING("speed")] = (mfxU16)MFX_TARGETUSAGE_7; + tu[MSDK_STRING("1")] = (mfxU16)MFX_TARGETUSAGE_1; + tu[MSDK_STRING("2")] = (mfxU16)MFX_TARGETUSAGE_2; + tu[MSDK_STRING("3")] = (mfxU16)MFX_TARGETUSAGE_3; + tu[MSDK_STRING("4")] = (mfxU16)MFX_TARGETUSAGE_4; + tu[MSDK_STRING("5")] = (mfxU16)MFX_TARGETUSAGE_5; + tu[MSDK_STRING("6")] = (mfxU16)MFX_TARGETUSAGE_6; + tu[MSDK_STRING("7")] = (mfxU16)MFX_TARGETUSAGE_7; + + if (tu.find(strInput) == tu.end()) + return 0; + else + return tu[strInput]; +} + +const msdk_char* TargetUsageToStr(mfxU16 tu) +{ + switch(tu) + { + case MFX_TARGETUSAGE_BALANCED: + return MSDK_STRING("balanced"); + case MFX_TARGETUSAGE_BEST_QUALITY: + return MSDK_STRING("quality"); + case MFX_TARGETUSAGE_BEST_SPEED: + return MSDK_STRING("speed"); + case MFX_TARGETUSAGE_UNKNOWN: + return MSDK_STRING("unknown"); + default: + return MSDK_STRING("unsupported"); + } +} + +const msdk_char* ColorFormatToStr(mfxU32 format) +{ + switch(format) + { + case MFX_FOURCC_NV12: + return MSDK_STRING("NV12"); + case MFX_FOURCC_YV12: + return MSDK_STRING("YV12"); + case MFX_FOURCC_I420: + return MSDK_STRING("YUV420"); + case MFX_FOURCC_RGB4: + return MSDK_STRING("RGB4"); + case MFX_FOURCC_YUY2: + return MSDK_STRING("YUY2"); + case MFX_FOURCC_UYVY: + return MSDK_STRING("UYVY"); + case MFX_FOURCC_P010: + return MSDK_STRING("P010"); + default: + return MSDK_STRING("unsupported"); + } +} + +mfxU32 GCD(mfxU32 a, mfxU32 b) +{ + if (0 == a) + return b; + else if (0 == b) + return a; + + mfxU32 a1, b1; + + if (a >= b) + { + a1 = a; + b1 = b; + } + else + { + a1 = b; + b1 = a; + } + + // a1 >= b1; + mfxU32 r = a1 % b1; + + while (0 != r) + { + a1 = b1; + b1 = r; + r = a1 % b1; + } + + return b1; +} + +mfxStatus DARtoPAR(mfxU32 darw, mfxU32 darh, mfxU32 w, mfxU32 h, mfxU16 *pparw, mfxU16 *pparh) +{ + MSDK_CHECK_POINTER(pparw, MFX_ERR_NULL_PTR); + MSDK_CHECK_POINTER(pparh, MFX_ERR_NULL_PTR); + MSDK_CHECK_ERROR(darw, 0, MFX_ERR_UNDEFINED_BEHAVIOR); + MSDK_CHECK_ERROR(darh, 0, MFX_ERR_UNDEFINED_BEHAVIOR); + MSDK_CHECK_ERROR(w, 0, MFX_ERR_UNDEFINED_BEHAVIOR); + MSDK_CHECK_ERROR(h, 0, MFX_ERR_UNDEFINED_BEHAVIOR); + + mfxU16 reduced_w = 0, reduced_h = 0; + mfxU32 gcd = GCD(w, h); + + // divide by greatest common divisor to fit into mfxU16 + reduced_w = (mfxU16) (w / gcd); + reduced_h = (mfxU16) (h / gcd); + + // for mpeg2 we need to set exact values for par (standard supports only dar 4:3, 16:9, 221:100 or par 1:1) + if (darw * 3 == darh * 4) + { + *pparw = 4 * reduced_h; + *pparh = 3 * reduced_w; + } + else if (darw * 9 == darh * 16) + { + *pparw = 16 * reduced_h; + *pparh = 9 * reduced_w; + } + else if (darw * 100 == darh * 221) + { + *pparw = 221 * reduced_h; + *pparh = 100 * reduced_w; + } + else if (darw * reduced_h == darh * reduced_w) + { + *pparw = 1; + *pparh = 1; + } + else + { + *pparw = (mfxU16)((mfxF64)(darw * reduced_h) / (darh * reduced_w) * 1000); + *pparh = 1000; + } + + return MFX_ERR_NONE; +} + +std::basic_string FormMVCFileName(const msdk_char *strFileNamePattern, const mfxU32 numView) +{ + if (NULL == strFileNamePattern) + return MSDK_STRING(""); + + std::basic_string fileName, mvcFileName, fileExt; + fileName = strFileNamePattern; + + msdk_char postfixBuffer[4]; + msdk_itoa_decimal(numView, postfixBuffer); + mvcFileName = fileName; + mvcFileName.append(MSDK_STRING("_")); + mvcFileName.append(postfixBuffer); + mvcFileName.append(MSDK_STRING(".yuv")); + + return mvcFileName; +} + +// function for getting a pointer to a specific external buffer from the array +mfxExtBuffer* GetExtBuffer(mfxExtBuffer** ebuffers, mfxU32 nbuffers, mfxU32 BufferId) +{ + if (!ebuffers) return 0; + for(mfxU32 i=0; iBufferId == BufferId) { + return ebuffers[i]; + } + } + return 0; +} + +mfxStatus MJPEG_AVI_ParsePicStruct(mfxBitstream *bitstream) +{ + // check input for consistency + MSDK_CHECK_POINTER(bitstream->Data, MFX_ERR_MORE_DATA); + if (bitstream->DataLength <= 0) + return MFX_ERR_MORE_DATA; + + // define JPEG markers + const mfxU8 APP0_marker [] = { 0xFF, 0xE0 }; + const mfxU8 SOI_marker [] = { 0xFF, 0xD8 }; + const mfxU8 AVI1 [] = { 'A', 'V', 'I', '1' }; + + // size of length field in header + const mfxU8 len_size = 2; + // size of picstruct field in header + const mfxU8 picstruct_size = 1; + + mfxU32 length = bitstream->DataLength; + const mfxU8 *ptr = reinterpret_cast(bitstream->Data); + + //search for SOI marker + while ((length >= sizeof(SOI_marker)) && memcmp(ptr, SOI_marker, sizeof(SOI_marker))) + { + skip(ptr, length, (mfxU32)1); + } + + // skip SOI + if (!skip(ptr, length, (mfxU32)sizeof(SOI_marker)) || length < sizeof(APP0_marker)) + return MFX_ERR_MORE_DATA; + + // if there is no APP0 marker return + if (memcmp(ptr, APP0_marker, sizeof(APP0_marker))) + { + bitstream->PicStruct = MFX_PICSTRUCT_UNKNOWN; + return MFX_ERR_NONE; + } + + // skip APP0 & length value + if (!skip(ptr, length, (mfxU32)sizeof(APP0_marker) + len_size) || length < sizeof(AVI1)) + return MFX_ERR_MORE_DATA; + + if (memcmp(ptr, AVI1, sizeof(AVI1))) + { + bitstream->PicStruct = MFX_PICSTRUCT_UNKNOWN; + return MFX_ERR_NONE; + } + + // skip 'AVI1' + if (!skip(ptr, length, (mfxU32)sizeof(AVI1)) || length < picstruct_size) + return MFX_ERR_MORE_DATA; + + // get PicStruct + switch (*ptr) + { + case 0: + bitstream->PicStruct = MFX_PICSTRUCT_PROGRESSIVE; + break; + case 1: + bitstream->PicStruct = MFX_PICSTRUCT_FIELD_TFF; + break; + case 2: + bitstream->PicStruct = MFX_PICSTRUCT_FIELD_BFF; + break; + default: + bitstream->PicStruct = MFX_PICSTRUCT_UNKNOWN; + } + + return MFX_ERR_NONE; +} + +mfxVersion getMinimalRequiredVersion(const APIChangeFeatures &features) +{ + mfxVersion version = {{1, 1}}; + + if (features.MVCDecode || features.MVCEncode || features.LowLatency || features.JpegDecode) + { + version.Minor = 3; + } + + if (features.ViewOutput) + { + version.Minor = 4; + } + + if (features.JpegEncode || features.IntraRefresh) + { + version.Minor = 6; + } + + if (features.LookAheadBRC) + { + version.Minor = 7; + } + + if (features.AudioDecode) { + version.Minor = 8; + } + + if (features.SupportCodecPluginAPI) { + version.Minor = 8; + } + + return version; +} + +bool CheckVersion(mfxVersion* version, msdkAPIFeature feature) +{ + if (!version->Major || (version->Major > 1)) { + return false; + } + + switch (feature) { + case MSDK_FEATURE_NONE: + return true; + case MSDK_FEATURE_MVC: + if ((version->Major == 1) && (version->Minor >= 3)) { + return true; + } + break; + case MSDK_FEATURE_JPEG_DECODE: + if ((version->Major == 1) && (version->Minor >= 3)) { + return true; + } + break; + case MSDK_FEATURE_LOW_LATENCY: + if ((version->Major == 1) && (version->Minor >= 3)) { + return true; + } + break; + case MSDK_FEATURE_MVC_VIEWOUTPUT: + if ((version->Major == 1) && (version->Minor >= 4)) { + return true; + } + break; + case MSDK_FEATURE_JPEG_ENCODE: + if ((version->Major == 1) && (version->Minor >= 6)) { + return true; + } + break; + case MSDK_FEATURE_LOOK_AHEAD: + if ((version->Major == 1) && (version->Minor >= 7)) { + return true; + } + break; + case MSDK_FEATURE_PLUGIN_API: + if ((version->Major == 1) && (version->Minor >= 8)) { + return true; + } + break; + default: + return false; + } + return false; +} + +void ConfigureAspectRatioConversion(mfxInfoVPP* pVppInfo) +{ + if (!pVppInfo) return; + + if (pVppInfo->In.AspectRatioW && + pVppInfo->In.AspectRatioH && + pVppInfo->In.CropW && + pVppInfo->In.CropH && + pVppInfo->Out.AspectRatioW && + pVppInfo->Out.AspectRatioH && + pVppInfo->Out.CropW && + pVppInfo->Out.CropH) + { + mfxF64 dFrameAR = ((mfxF64)pVppInfo->In.AspectRatioW * pVppInfo->In.CropW) / + (mfxF64)pVppInfo->In.AspectRatioH / + (mfxF64)pVppInfo->In.CropH; + + mfxF64 dPixelAR = pVppInfo->Out.AspectRatioW / (mfxF64)pVppInfo->Out.AspectRatioH; + + mfxU16 dProportionalH = (mfxU16)(pVppInfo->Out.CropW * dPixelAR / dFrameAR + 1) & -2; //round to closest odd (values are always positive) + + if (dProportionalH < pVppInfo->Out.CropH) + { + pVppInfo->Out.CropY = (mfxU16)((pVppInfo->Out.CropH - dProportionalH) / 2. + 1) & -2; + pVppInfo->Out.CropH = pVppInfo->Out.CropH - 2 * pVppInfo->Out.CropY; + } + else if (dProportionalH > pVppInfo->Out.CropH) + { + mfxU16 dProportionalW = (mfxU16)(pVppInfo->Out.CropH * dFrameAR / dPixelAR + 1) & -2; + + pVppInfo->Out.CropX = (mfxU16)((pVppInfo->Out.CropW - dProportionalW) / 2 + 1) & -2; + pVppInfo->Out.CropW = pVppInfo->Out.CropW - 2 * pVppInfo->Out.CropX; + } + } +} + +void SEICalcSizeType(std::vector& data, mfxU16 type, mfxU32 size) +{ + mfxU32 B = type; + + while (B > 255) + { + data.push_back(255); + B -= 255; + } + data.push_back(mfxU8(B)); + + B = size; + + while (B > 255) + { + data.push_back(255); + B -= 255; + } + data.push_back(mfxU8(B)); +} + +mfxU8 Char2Hex(msdk_char ch) +{ + msdk_char value = ch; + if(value >= MSDK_CHAR('0') && value <= MSDK_CHAR('9')) + { + value -= MSDK_CHAR('0'); + } + else if (value >= MSDK_CHAR('a') && value <= MSDK_CHAR('f')) + { + value = value - MSDK_CHAR('a') + 10; + } + else if (value >= MSDK_CHAR('A') && value <= MSDK_CHAR('F')) + { + value = value - MSDK_CHAR('A') + 10; + } + else + { + value = 0; + } + return (mfxU8)value; +} + +namespace { + int g_trace_level = MSDK_TRACE_LEVEL_INFO; +} + +int msdk_trace_get_level() { + return g_trace_level; +} + +void msdk_trace_set_level(int newLevel) { + g_trace_level = newLevel; +} + +bool msdk_trace_is_printable(int level) { + return g_trace_level >= level; +} + +msdk_ostream & operator <<(msdk_ostream & os, MsdkTraceLevel tl) { + switch (tl) + { + case MSDK_TRACE_LEVEL_CRITICAL : + os< mfxStatus +msdk_opt_read(const msdk_char* string, mfxU8& value) +{ + msdk_char* stopCharacter; + value = (mfxU8)msdk_strtol(string, &stopCharacter, 10); + + return (msdk_strlen(stopCharacter) == 0)? MFX_ERR_NONE: MFX_ERR_UNKNOWN; +} + +template<> mfxStatus +msdk_opt_read(const msdk_char* string, mfxU16& value) +{ + msdk_char* stopCharacter; + value = (mfxU16)msdk_strtol(string, &stopCharacter, 10); + + return (msdk_strlen(stopCharacter) == 0)? MFX_ERR_NONE: MFX_ERR_UNKNOWN; +} + +template<> mfxStatus +msdk_opt_read(const msdk_char* string, mfxU32& value) +{ + msdk_char* stopCharacter; + value = (mfxU32)msdk_strtol(string, &stopCharacter, 10); + + return (msdk_strlen(stopCharacter) == 0)? MFX_ERR_NONE: MFX_ERR_UNKNOWN; +} + +template<> mfxStatus +msdk_opt_read(const msdk_char* string, mfxF32& value) +{ + msdk_char* stopCharacter; + value = (mfxF32)msdk_strtod(string, &stopCharacter); + return (msdk_strlen(stopCharacter) == 0)? MFX_ERR_NONE: MFX_ERR_UNKNOWN; +} +template<> mfxStatus +msdk_opt_read(const msdk_char* string, mfxF64& value) +{ + msdk_char* stopCharacter; + value = (mfxF64)msdk_strtod(string, &stopCharacter); + + return (msdk_strlen(stopCharacter) == 0)? MFX_ERR_NONE: MFX_ERR_UNKNOWN; +} + +mfxStatus msdk_opt_read(const msdk_char* string, mfxU8& value); +mfxStatus msdk_opt_read(const msdk_char* string, mfxU16& value); +mfxStatus msdk_opt_read(const msdk_char* string, mfxU32& value); +mfxStatus msdk_opt_read(const msdk_char* string, mfxF64& value); +mfxStatus msdk_opt_read(const msdk_char* string, mfxF32& value); + +template<> mfxStatus +msdk_opt_read(const msdk_char* string, mfxI16& value) +{ + msdk_char* stopCharacter; + value = (mfxI16)msdk_strtol(string, &stopCharacter, 10); + + return (msdk_strlen(stopCharacter) == 0)? MFX_ERR_NONE: MFX_ERR_UNKNOWN; +} + +template<> mfxStatus +msdk_opt_read(const msdk_char* string, mfxI32& value) +{ + msdk_char* stopCharacter; + value = (mfxI32)msdk_strtol(string, &stopCharacter, 10); + + return (msdk_strlen(stopCharacter) == 0)? MFX_ERR_NONE: MFX_ERR_UNKNOWN; +} + +mfxStatus msdk_opt_read(const msdk_char* string, mfxI16& value); +mfxStatus msdk_opt_read(const msdk_char* string, mfxI32& value); + +template<> mfxStatus +msdk_opt_read(const msdk_char* string, mfxPriority& value) +{ + mfxU32 priority = 0; + mfxStatus sts = msdk_opt_read<>(string, priority); + + if (MFX_ERR_NONE == sts) value = (mfxPriority)priority; + return sts; +} + +mfxStatus msdk_opt_read(msdk_char* string, mfxPriority& value); + +bool IsDecodeCodecSupported(mfxU32 codecFormat) +{ + switch(codecFormat) + { + case MFX_CODEC_MPEG2: + case MFX_CODEC_AVC: + case MFX_CODEC_HEVC: + case MFX_CODEC_VC1: + case CODEC_MVC: + case MFX_CODEC_JPEG: + case MFX_CODEC_VP8: + case MFX_CODEC_VP9: + break; + default: + return false; + } + return true; +} + +bool IsEncodeCodecSupported(mfxU32 codecFormat) +{ + switch(codecFormat) + { + case MFX_CODEC_AVC: + case MFX_CODEC_HEVC: + case MFX_CODEC_MPEG2: + case CODEC_MVC: + case MFX_CODEC_VP8: + case MFX_CODEC_JPEG: + break; + default: + return false; + } + return true; +} + +bool IsPluginCodecSupported(mfxU32 codecFormat) +{ + switch(codecFormat) + { + case MFX_CODEC_HEVC: + case MFX_CODEC_AVC: + case MFX_CODEC_MPEG2: + case MFX_CODEC_VC1: + case MFX_CODEC_VP8: + case MFX_CODEC_VP9: + break; + default: + return false; + } + return true; +} + +mfxStatus StrFormatToCodecFormatFourCC(msdk_char* strInput, mfxU32 &codecFormat) +{ + mfxStatus sts = MFX_ERR_NONE; + codecFormat = 0; + + if (strInput == NULL) + sts = MFX_ERR_NULL_PTR; + + if (sts == MFX_ERR_NONE) + { + if (0 == msdk_strcmp(strInput, MSDK_STRING("mpeg2"))) + { + codecFormat = MFX_CODEC_MPEG2; + } + else if (0 == msdk_strcmp(strInput, MSDK_STRING("h264"))) + { + codecFormat = MFX_CODEC_AVC; + } + else if (0 == msdk_strcmp(strInput, MSDK_STRING("h265"))) + { + codecFormat = MFX_CODEC_HEVC; + } + else if (0 == msdk_strcmp(strInput, MSDK_STRING("vc1"))) + { + codecFormat = MFX_CODEC_VC1; + } + else if (0 == msdk_strcmp(strInput, MSDK_STRING("mvc"))) + { + codecFormat = CODEC_MVC; + } + else if (0 == msdk_strcmp(strInput, MSDK_STRING("jpeg"))) + { + codecFormat = MFX_CODEC_JPEG; + } + else if (0 == msdk_strcmp(strInput, MSDK_STRING("vp8"))) + { + codecFormat = MFX_CODEC_VP8; + } + else if (0 == msdk_strcmp(strInput, MSDK_STRING("vp9"))) + { + codecFormat = MFX_CODEC_VP9; + } + else if ((0 == msdk_strcmp(strInput, MSDK_STRING("raw")))) + { + codecFormat = MFX_CODEC_DUMP; + } + else if ((0 == msdk_strcmp(strInput, MSDK_STRING("rgb4_frame")))) + { + codecFormat = MFX_CODEC_RGB4; + } + else + sts = MFX_ERR_UNSUPPORTED; + } + + return sts; +} + +msdk_string StatusToString(mfxStatus sts) +{ + switch(sts) + { + case MFX_ERR_NONE: + return msdk_string(MSDK_STRING("MFX_ERR_NONE")); + case MFX_ERR_UNKNOWN: + return msdk_string(MSDK_STRING("MFX_ERR_UNKNOWN")); + case MFX_ERR_NULL_PTR: + return msdk_string(MSDK_STRING("MFX_ERR_NULL_PTR")); + case MFX_ERR_UNSUPPORTED: + return msdk_string(MSDK_STRING("MFX_ERR_UNSUPPORTED")); + case MFX_ERR_MEMORY_ALLOC: + return msdk_string(MSDK_STRING("MFX_ERR_MEMORY_ALLOC")); + case MFX_ERR_NOT_ENOUGH_BUFFER: + return msdk_string(MSDK_STRING("MFX_ERR_NOT_ENOUGH_BUFFER")); + case MFX_ERR_INVALID_HANDLE: + return msdk_string(MSDK_STRING("MFX_ERR_INVALID_HANDLE")); + case MFX_ERR_LOCK_MEMORY: + return msdk_string(MSDK_STRING("MFX_ERR_LOCK_MEMORY")); + case MFX_ERR_NOT_INITIALIZED: + return msdk_string(MSDK_STRING("MFX_ERR_NOT_INITIALIZED")); + case MFX_ERR_NOT_FOUND: + return msdk_string(MSDK_STRING("MFX_ERR_NOT_FOUND")); + case MFX_ERR_MORE_DATA: + return msdk_string(MSDK_STRING("MFX_ERR_MORE_DATA")); + case MFX_ERR_MORE_SURFACE: + return msdk_string(MSDK_STRING("MFX_ERR_MORE_SURFACE")); + case MFX_ERR_ABORTED: + return msdk_string(MSDK_STRING("MFX_ERR_ABORTED")); + case MFX_ERR_DEVICE_LOST: + return msdk_string(MSDK_STRING("MFX_ERR_DEVICE_LOST")); + case MFX_ERR_INCOMPATIBLE_VIDEO_PARAM: + return msdk_string(MSDK_STRING("MFX_ERR_INCOMPATIBLE_VIDEO_PARAM")); + case MFX_ERR_INVALID_VIDEO_PARAM: + return msdk_string(MSDK_STRING("MFX_ERR_INVALID_VIDEO_PARAM")); + case MFX_ERR_UNDEFINED_BEHAVIOR: + return msdk_string(MSDK_STRING("MFX_ERR_UNDEFINED_BEHAVIOR")); + case MFX_ERR_DEVICE_FAILED: + return msdk_string(MSDK_STRING("MFX_ERR_DEVICE_FAILED")); + case MFX_ERR_MORE_BITSTREAM: + return msdk_string(MSDK_STRING("MFX_ERR_MORE_BITSTREAM")); + case MFX_ERR_INCOMPATIBLE_AUDIO_PARAM: + return msdk_string(MSDK_STRING("MFX_ERR_INCOMPATIBLE_AUDIO_PARAM")); + case MFX_ERR_INVALID_AUDIO_PARAM: + return msdk_string(MSDK_STRING("MFX_ERR_INVALID_AUDIO_PARAM")); + case MFX_ERR_GPU_HANG: + return msdk_string(MSDK_STRING("MFX_ERR_GPU_HANG")); + case MFX_ERR_REALLOC_SURFACE: + return msdk_string(MSDK_STRING("MFX_ERR_REALLOC_SURFACE")); + case MFX_WRN_IN_EXECUTION: + return msdk_string(MSDK_STRING("MFX_WRN_IN_EXECUTION")); + case MFX_WRN_DEVICE_BUSY: + return msdk_string(MSDK_STRING("MFX_WRN_DEVICE_BUSY")); + case MFX_WRN_VIDEO_PARAM_CHANGED: + return msdk_string(MSDK_STRING("MFX_WRN_VIDEO_PARAM_CHANGED")); + case MFX_WRN_PARTIAL_ACCELERATION: + return msdk_string(MSDK_STRING("MFX_WRN_PARTIAL_ACCELERATION")); + case MFX_WRN_INCOMPATIBLE_VIDEO_PARAM: + return msdk_string(MSDK_STRING("MFX_WRN_INCOMPATIBLE_VIDEO_PARAM")); + case MFX_WRN_VALUE_NOT_CHANGED: + return msdk_string(MSDK_STRING("MFX_WRN_VALUE_NOT_CHANGED")); + case MFX_WRN_OUT_OF_RANGE: + return msdk_string(MSDK_STRING("MFX_WRN_OUT_OF_RANGE")); + case MFX_WRN_FILTER_SKIPPED: + return msdk_string(MSDK_STRING("MFX_WRN_FILTER_SKIPPED")); + case MFX_WRN_INCOMPATIBLE_AUDIO_PARAM: + return msdk_string(MSDK_STRING("MFX_WRN_INCOMPATIBLE_AUDIO_PARAM")); + case MFX_TASK_WORKING: + return msdk_string(MSDK_STRING("MFX_TASK_WORKING")); + case MFX_TASK_BUSY: + return msdk_string(MSDK_STRING("MFX_TASK_BUSY")); + case MFX_ERR_MORE_DATA_SUBMIT_TASK: + return msdk_string(MSDK_STRING("MFX_ERR_MORE_DATA_SUBMIT_TASK")); + default: + return msdk_string(MSDK_STRING("[Unknown status]")); + } +} + +mfxI32 getMonitorType(msdk_char* str) +{ + struct { + const msdk_char* str; + mfxI32 mfx_type; + } table[] = { +#define __DECLARE(type) { MSDK_STRING(#type), MFX_MONITOR_ ## type } + __DECLARE(Unknown), + __DECLARE(VGA), + __DECLARE(DVII), + __DECLARE(DVID), + __DECLARE(DVIA), + __DECLARE(Composite), + __DECLARE(SVIDEO), + __DECLARE(LVDS), + __DECLARE(Component), + __DECLARE(9PinDIN), + __DECLARE(HDMIA), + __DECLARE(HDMIB), + __DECLARE(eDP), + __DECLARE(TV), + __DECLARE(DisplayPort), +#if defined(DRM_MODE_CONNECTOR_VIRTUAL) // from libdrm 2.4.59 + __DECLARE(VIRTUAL), +#endif +#if defined(DRM_MODE_CONNECTOR_DSI) // from libdrm 2.4.59 + __DECLARE(DSI) +#endif +#undef __DECLARE + }; + for (unsigned int i=0; i < sizeof(table)/sizeof(table[0]); ++i) { + if (0 == msdk_strcmp(str, table[i].str)) { + return table[i].mfx_type; + } + } + return MFX_MONITOR_MAXNUMBER; +} + +CH264FrameReader::CH264FrameReader() +: CSmplBitstreamReader() +, m_processedBS(0) +, m_isEndOfStream(false) +, m_frame(0) +, m_plainBuffer(0) +, m_plainBufferSize(0) +{ +} + +CH264FrameReader::~CH264FrameReader() +{ +} + +void CH264FrameReader::Close() +{ + WipeMfxBitstream(m_originalBS.get()); + CSmplBitstreamReader::Close(); + + if (NULL != m_plainBuffer) + { + free(m_plainBuffer); + m_plainBuffer = NULL; + m_plainBufferSize = 0; + } +} + +mfxStatus CH264FrameReader::Init(const msdk_char *strFileName) +{ + mfxStatus sts = MFX_ERR_NONE; + + sts = CSmplBitstreamReader::Init(strFileName); + if (sts != MFX_ERR_NONE) + return sts; + + m_isEndOfStream = false; + m_processedBS = NULL; + + m_originalBS.reset(new mfxBitstream()); + sts = InitMfxBitstream(m_originalBS.get(), 1024 * 1024); + if (sts != MFX_ERR_NONE) + return sts; + + m_pNALSplitter.reset(new ProtectedLibrary::AVC_Spl()); + + m_frame = 0; + m_plainBuffer = 0; + m_plainBufferSize = 0; + + return sts; +} + +mfxStatus CH264FrameReader::ReadNextFrame(mfxBitstream *pBS) +{ + mfxStatus sts = MFX_ERR_NONE; + pBS->DataFlag = MFX_BITSTREAM_COMPLETE_FRAME; + //read bit stream from source + while (!m_originalBS->DataLength) + { + sts = CSmplBitstreamReader::ReadNextFrame(m_originalBS.get()); + if (sts != MFX_ERR_NONE && sts != MFX_ERR_MORE_DATA) + return sts; + if (sts == MFX_ERR_MORE_DATA) + { + m_isEndOfStream = true; + break; + } + } + + do + { + sts = PrepareNextFrame(m_isEndOfStream ? NULL : m_originalBS.get(), &m_processedBS); + + if (sts == MFX_ERR_MORE_DATA) + { + if (m_isEndOfStream) + { + break; + } + + sts = CSmplBitstreamReader::ReadNextFrame(m_originalBS.get()); + if (sts == MFX_ERR_MORE_DATA) + m_isEndOfStream = true; + continue; + } + else if (MFX_ERR_NONE != sts) + return sts; + + } while (MFX_ERR_NONE != sts); + + // get output stream + if (NULL != m_processedBS) + { + mfxStatus copySts = CopyBitstream2( + pBS, + m_processedBS); + if (copySts < MFX_ERR_NONE) + return copySts; + m_processedBS = NULL; + } + + return sts; +} + +mfxStatus CH264FrameReader::PrepareNextFrame(mfxBitstream *in, mfxBitstream **out) +{ + mfxStatus sts = MFX_ERR_NONE; + + if (NULL == out) + return MFX_ERR_NULL_PTR; + + *out = NULL; + + // get frame if it is not ready yet + if (NULL == m_frame) + { + sts = m_pNALSplitter->GetFrame(in, &m_frame); + if (sts != MFX_ERR_NONE) + return sts; + } + + if (m_plainBufferSize < m_frame->DataLength) + { + if (NULL != m_plainBuffer) + { + free(m_plainBuffer); + m_plainBuffer = NULL; + m_plainBufferSize = 0; + } + m_plainBuffer = (mfxU8*)malloc(m_frame->DataLength); + if (NULL == m_plainBuffer) + return MFX_ERR_MEMORY_ALLOC; + m_plainBufferSize = m_frame->DataLength; + } + + MSDK_MEMCPY_BUF(m_plainBuffer, 0, m_plainBufferSize, m_frame->Data, m_frame->DataLength); + + memset(&m_outBS, 0, sizeof(mfxBitstream)); + m_outBS.Data = m_plainBuffer; + m_outBS.DataOffset = 0; + m_outBS.DataLength = m_frame->DataLength; + m_outBS.MaxLength = m_frame->DataLength; + m_outBS.DataFlag = MFX_BITSTREAM_COMPLETE_FRAME; + m_outBS.TimeStamp = m_frame->TimeStamp; + + m_pNALSplitter->ResetCurrentState(); + m_frame = NULL; + + *out = &m_outBS; + + return sts; +} + + +// 1 ms provides better result in range [0..5] ms +#define DEVICE_WAIT_TIME 1 + +// This function either performs synchronization using provided syncpoint, or just waits for predefined time if syncpoint is already 0 (this usually happens if syncpoint was already processed) +void WaitForDeviceToBecomeFree(MFXVideoSession& session, mfxSyncPoint& syncPoint,mfxStatus& currentStatus) +{ + // Wait 1ms will be probably enough to device release + if (syncPoint) { + mfxStatus stsSync = session.SyncOperation(syncPoint, DEVICE_WAIT_TIME); + if (MFX_ERR_NONE == stsSync) { + // retire completed sync point (otherwise we may start active polling) + syncPoint = NULL; + } else if (stsSync < 0) { + currentStatus = stsSync; + } + } else { + MSDK_SLEEP(DEVICE_WAIT_TIME); + } +} + +mfxU16 FourCCToChroma(mfxU32 fourCC) +{ + switch(fourCC) + { + case MFX_FOURCC_NV12: + case MFX_FOURCC_P010: + return MFX_CHROMAFORMAT_YUV420; + case MFX_FOURCC_NV16: + case MFX_FOURCC_P210: + case MFX_FOURCC_YUY2: + return MFX_CHROMAFORMAT_YUV422; + case MFX_FOURCC_RGB4: + return MFX_CHROMAFORMAT_YUV444; + } + + return MFX_CHROMAFORMAT_YUV420; +} diff --git a/vshampor/deshuffler/sample_common/src/sysmem_allocator.cpp b/vshampor/deshuffler/sample_common/src/sysmem_allocator.cpp new file mode 100644 index 0000000..ae95ff9 --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/sysmem_allocator.cpp @@ -0,0 +1,418 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "sysmem_allocator.h" +#include "sample_utils.h" + +#define MSDK_ALIGN32(X) (((mfxU32)((X)+31)) & (~ (mfxU32)31)) +#define ID_BUFFER MFX_MAKEFOURCC('B','U','F','F') +#define ID_FRAME MFX_MAKEFOURCC('F','R','M','E') + +#pragma warning(disable : 4100) + +SysMemFrameAllocator::SysMemFrameAllocator() +: m_pBufferAllocator(0), m_bOwnBufferAllocator(false) +{ +} + +SysMemFrameAllocator::~SysMemFrameAllocator() +{ + Close(); +} + +mfxStatus SysMemFrameAllocator::Init(mfxAllocatorParams *pParams) +{ + // check if any params passed from application + if (pParams) + { + SysMemAllocatorParams *pSysMemParams = 0; + pSysMemParams = dynamic_cast(pParams); + if (!pSysMemParams) + return MFX_ERR_NOT_INITIALIZED; + + m_pBufferAllocator = pSysMemParams->pBufferAllocator; + m_bOwnBufferAllocator = false; + } + + // if buffer allocator wasn't passed from application create own + if (!m_pBufferAllocator) + { + m_pBufferAllocator = new SysMemBufferAllocator; + if (!m_pBufferAllocator) + return MFX_ERR_MEMORY_ALLOC; + + m_bOwnBufferAllocator = true; + } + + return MFX_ERR_NONE; +} + +mfxStatus SysMemFrameAllocator::Close() +{ + mfxStatus sts = BaseFrameAllocator::Close(); + + if (m_bOwnBufferAllocator) + { + delete m_pBufferAllocator; + m_pBufferAllocator = 0; + } + return sts; +} + +mfxStatus SysMemFrameAllocator::LockFrame(mfxMemId mid, mfxFrameData *ptr) +{ + if (!m_pBufferAllocator) + return MFX_ERR_NOT_INITIALIZED; + + if (!ptr) + return MFX_ERR_NULL_PTR; + + // If allocator uses pointers instead of mids, no further action is required + if (!mid && ptr->Y) + return MFX_ERR_NONE; + + sFrame *fs = 0; + mfxStatus sts = m_pBufferAllocator->Lock(m_pBufferAllocator->pthis, mid,(mfxU8 **)&fs); + + if (MFX_ERR_NONE != sts) + return sts; + + if (ID_FRAME != fs->id) + { + m_pBufferAllocator->Unlock(m_pBufferAllocator->pthis, mid); + return MFX_ERR_INVALID_HANDLE; + } + + mfxU16 Width2 = (mfxU16)MSDK_ALIGN32(fs->info.Width); + mfxU16 Height2 = (mfxU16)MSDK_ALIGN32(fs->info.Height); + ptr->B = ptr->Y = (mfxU8 *)fs + MSDK_ALIGN32(sizeof(sFrame)); + + switch (fs->info.FourCC) + { + case MFX_FOURCC_NV12: + ptr->U = ptr->Y + Width2 * Height2; + ptr->V = ptr->U + 1; + ptr->Pitch = Width2; + break; + case MFX_FOURCC_NV16: + ptr->U = ptr->Y + Width2 * Height2; + ptr->V = ptr->U + 1; + ptr->Pitch = Width2; + break; + case MFX_FOURCC_YV12: + ptr->V = ptr->Y + Width2 * Height2; + ptr->U = ptr->V + (Width2 >> 1) * (Height2 >> 1); + ptr->Pitch = Width2; + break; + case MFX_FOURCC_UYVY: + ptr->U = ptr->Y; + ptr->Y = ptr->U + 1; + ptr->V = ptr->U + 2; + ptr->Pitch = 2 * Width2; + break; + case MFX_FOURCC_YUY2: + ptr->U = ptr->Y + 1; + ptr->V = ptr->Y + 3; + ptr->Pitch = 2 * Width2; + break; +#if (MFX_VERSION >= MFX_VERSION_NEXT) + case MFX_FOURCC_RGB565: + ptr->G = ptr->B; + ptr->R = ptr->B; + ptr->Pitch = 2 * Width2; + break; +#endif + case MFX_FOURCC_RGB3: + ptr->G = ptr->B + 1; + ptr->R = ptr->B + 2; + ptr->Pitch = 3 * Width2; + break; + case MFX_FOURCC_RGBP: + ptr->G = ptr->B + Width2 * Height2; + ptr->R = ptr->B + Width2 * Height2 * 2; + ptr->Pitch = Width2; + break; + case MFX_FOURCC_RGB4: + case MFX_FOURCC_A2RGB10: + ptr->G = ptr->B + 1; + ptr->R = ptr->B + 2; + ptr->A = ptr->B + 3; + ptr->Pitch = 4 * Width2; + break; + case MFX_FOURCC_R16: + ptr->Y16 = (mfxU16 *)ptr->B; + ptr->Pitch = 2 * Width2; + break; + case MFX_FOURCC_P010: + ptr->U = ptr->Y + Width2 * Height2 * 2; + ptr->V = ptr->U + 2; + ptr->Pitch = Width2 * 2; + break; + case MFX_FOURCC_P210: + ptr->U = ptr->Y + Width2 * Height2 * 2; + ptr->V = ptr->U + 2; + ptr->Pitch = Width2 * 2; + break; + case MFX_FOURCC_AYUV: + ptr->V = ptr->B; + ptr->U = ptr->V + 1; + ptr->Y = ptr->V + 2; + ptr->A = ptr->V + 3; + ptr->Pitch = 4 * Width2; + break; + + default: + return MFX_ERR_UNSUPPORTED; + } + + return MFX_ERR_NONE; +} + +mfxStatus SysMemFrameAllocator::UnlockFrame(mfxMemId mid, mfxFrameData *ptr) +{ + if (!m_pBufferAllocator) + return MFX_ERR_NOT_INITIALIZED; + + // If allocator uses pointers instead of mids, no further action is required + if (!mid && ptr->Y) + return MFX_ERR_NONE; + + mfxStatus sts = m_pBufferAllocator->Unlock(m_pBufferAllocator->pthis, mid); + + if (MFX_ERR_NONE != sts) + return sts; + + if (NULL != ptr) + { + ptr->Pitch = 0; + ptr->Y = 0; + ptr->U = 0; + ptr->V = 0; + ptr->A = 0; + } + + return MFX_ERR_NONE; +} + +mfxStatus SysMemFrameAllocator::GetFrameHDL(mfxMemId mid, mfxHDL *handle) +{ + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus SysMemFrameAllocator::CheckRequestType(mfxFrameAllocRequest *request) +{ + mfxStatus sts = BaseFrameAllocator::CheckRequestType(request); + if (MFX_ERR_NONE != sts) + return sts; + + if ((request->Type & MFX_MEMTYPE_SYSTEM_MEMORY) != 0) + return MFX_ERR_NONE; + else + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus SysMemFrameAllocator::AllocImpl(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) +{ + if (!m_pBufferAllocator) + return MFX_ERR_NOT_INITIALIZED; + + mfxU32 numAllocated = 0; + + mfxU32 Width2 = MSDK_ALIGN32(request->Info.Width); + mfxU32 Height2 = MSDK_ALIGN32(request->Info.Height); + mfxU32 nbytes; + + switch (request->Info.FourCC) + { + case MFX_FOURCC_YV12: + case MFX_FOURCC_NV12: + nbytes = Width2*Height2 + (Width2>>1)*(Height2>>1) + (Width2>>1)*(Height2>>1); + break; + case MFX_FOURCC_NV16: + nbytes = Width2*Height2 + (Width2>>1)*(Height2) + (Width2>>1)*(Height2); + break; +#if (MFX_VERSION >= MFX_VERSION_NEXT) + case MFX_FOURCC_RGB565: + nbytes = 2*Width2*Height2; + break; +#endif + case MFX_FOURCC_RGBP: + case MFX_FOURCC_RGB3: + nbytes = Width2*Height2 + Width2*Height2 + Width2*Height2; + break; + case MFX_FOURCC_RGB4: + case MFX_FOURCC_AYUV: + nbytes = Width2*Height2 + Width2*Height2 + Width2*Height2 + Width2*Height2; + break; + case MFX_FOURCC_UYVY: + case MFX_FOURCC_YUY2: + nbytes = Width2*Height2 + (Width2>>1)*(Height2) + (Width2>>1)*(Height2); + break; + case MFX_FOURCC_R16: + nbytes = 2*Width2*Height2; + break; + case MFX_FOURCC_P010: + nbytes = Width2*Height2 + (Width2>>1)*(Height2>>1) + (Width2>>1)*(Height2>>1); + nbytes *= 2; + break; + case MFX_FOURCC_A2RGB10: + nbytes = Width2*Height2*4; // 4 bytes per pixel + break; + case MFX_FOURCC_P210: + nbytes = Width2*Height2 + (Width2>>1)*(Height2) + (Width2>>1)*(Height2); + nbytes *= 2; // 16bits + break; + + + default: + return MFX_ERR_UNSUPPORTED; + } + + safe_array mids(new mfxMemId[request->NumFrameSuggested]); + if (!mids.get()) + return MFX_ERR_MEMORY_ALLOC; + + // allocate frames + for (numAllocated = 0; numAllocated < request->NumFrameSuggested; numAllocated ++) + { + mfxStatus sts = m_pBufferAllocator->Alloc(m_pBufferAllocator->pthis, + nbytes + MSDK_ALIGN32(sizeof(sFrame)), request->Type, &(mids.get()[numAllocated])); + + if (MFX_ERR_NONE != sts) + break; + + sFrame *fs; + sts = m_pBufferAllocator->Lock(m_pBufferAllocator->pthis, mids.get()[numAllocated], (mfxU8 **)&fs); + + if (MFX_ERR_NONE != sts) + break; + + fs->id = ID_FRAME; + fs->info = request->Info; + m_pBufferAllocator->Unlock(m_pBufferAllocator->pthis, mids.get()[numAllocated]); + } + + // check the number of allocated frames + if (numAllocated < request->NumFrameSuggested) + { + return MFX_ERR_MEMORY_ALLOC; + } + + response->NumFrameActual = (mfxU16) numAllocated; + response->mids = mids.release(); + + return MFX_ERR_NONE; +} + +mfxStatus SysMemFrameAllocator::ReleaseResponse(mfxFrameAllocResponse *response) +{ + if (!response) + return MFX_ERR_NULL_PTR; + + if (!m_pBufferAllocator) + return MFX_ERR_NOT_INITIALIZED; + + mfxStatus sts = MFX_ERR_NONE; + + if (response->mids) + { + for (mfxU32 i = 0; i < response->NumFrameActual; i++) + { + if (response->mids[i]) + { + sts = m_pBufferAllocator->Free(m_pBufferAllocator->pthis, response->mids[i]); + if (MFX_ERR_NONE != sts) + return sts; + } + } + } + + delete [] response->mids; + response->mids = 0; + + return sts; +} + +SysMemBufferAllocator::SysMemBufferAllocator() +{ + +} + +SysMemBufferAllocator::~SysMemBufferAllocator() +{ + +} + +mfxStatus SysMemBufferAllocator::AllocBuffer(mfxU32 nbytes, mfxU16 type, mfxMemId *mid) +{ + if (!mid) + return MFX_ERR_NULL_PTR; + + if (0 == (type & MFX_MEMTYPE_SYSTEM_MEMORY)) + return MFX_ERR_UNSUPPORTED; + + mfxU32 header_size = MSDK_ALIGN32(sizeof(sBuffer)); + mfxU8 *buffer_ptr = (mfxU8 *)calloc(header_size + nbytes + 32, 1); + + if (!buffer_ptr) + return MFX_ERR_MEMORY_ALLOC; + + sBuffer *bs = (sBuffer *)buffer_ptr; + bs->id = ID_BUFFER; + bs->type = type; + bs->nbytes = nbytes; + *mid = (mfxHDL) bs; + return MFX_ERR_NONE; +} + +mfxStatus SysMemBufferAllocator::LockBuffer(mfxMemId mid, mfxU8 **ptr) +{ + if (!ptr) + return MFX_ERR_NULL_PTR; + + sBuffer *bs = (sBuffer *)mid; + + if (!bs) + return MFX_ERR_INVALID_HANDLE; + if (ID_BUFFER != bs->id) + return MFX_ERR_INVALID_HANDLE; + + *ptr = (mfxU8*)((size_t)((mfxU8 *)bs+MSDK_ALIGN32(sizeof(sBuffer))+31)&(~((size_t)31))); + return MFX_ERR_NONE; +} + +mfxStatus SysMemBufferAllocator::UnlockBuffer(mfxMemId mid) +{ + sBuffer *bs = (sBuffer *)mid; + + if (!bs || ID_BUFFER != bs->id) + return MFX_ERR_INVALID_HANDLE; + + return MFX_ERR_NONE; +} + +mfxStatus SysMemBufferAllocator::FreeBuffer(mfxMemId mid) +{ + sBuffer *bs = (sBuffer *)mid; + if (!bs || ID_BUFFER != bs->id) + return MFX_ERR_INVALID_HANDLE; + + free(bs); + return MFX_ERR_NONE; +} diff --git a/vshampor/deshuffler/sample_common/src/v4l2_util.cpp b/vshampor/deshuffler/sample_common/src/v4l2_util.cpp new file mode 100644 index 0000000..104f456 --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/v4l2_util.cpp @@ -0,0 +1,357 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#if defined (ENABLE_V4L2_SUPPORT) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "v4l2_util.h" + +/* Global Declaration */ +Buffer *buffers, *CurBuffers; +bool CtrlFlag = false; +int m_q[5], m_first = 0, m_last = 0, m_numInQ = 0; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t empty = PTHREAD_MUTEX_INITIALIZER; + +v4l2Device::v4l2Device( const char *devname, + uint32_t width, + uint32_t height, + uint32_t num_buffers, + enum AtomISPMode MipiMode, + enum V4L2PixelFormat v4l2Format): + m_devname(devname), + m_height(height), + m_width(width), + m_num_buffers(num_buffers), + m_MipiPort(0), + m_MipiMode(MipiMode), + m_v4l2Format(v4l2Format), + m_fd(-1) +{ +} + +v4l2Device::~v4l2Device() +{ + if (m_fd > -1) + { + BYE_ON(close(m_fd) < 0, "V4L2 device close failed: %s\n", ERRSTR); + } +} + +int v4l2Device::blockIOCTL(int handle, int request, void *args) +{ + int ioctlStatus; + do + { + ioctlStatus = ioctl(handle, request, args); + } while (-1 == ioctlStatus && EINTR == errno); + return ioctlStatus; +} + +int v4l2Device::GetAtomISPModes(enum AtomISPMode mode) +{ + switch(mode) + { + case VIDEO: return _ISP_MODE_VIDEO; + case PREVIEW: return _ISP_MODE_PREVIEW; + case CONTINUOUS: return _ISP_MODE_CONTINUOUS; + case STILL: return _ISP_MODE_STILL; + case NONE: + + default: + return _ISP_MODE_NONE; + } +} + +int v4l2Device::ConvertToMFXFourCC(enum V4L2PixelFormat v4l2Format) +{ + switch (v4l2Format) + { + case UYVY: return MFX_FOURCC_UYVY; + case YUY2: return MFX_FOURCC_YUY2; + case NO_FORMAT: + + default: + assert( !"Unsupported mfx fourcc"); + return 0; + } +} + +int v4l2Device::ConvertToV4L2FourCC() +{ + switch (m_v4l2Format) + { + case UYVY: return V4L2_PIX_FMT_UYVY; + case YUY2: return V4L2_PIX_FMT_YUYV; + case NO_FORMAT: + + default: + assert( !"Unsupported v4l2 fourcc"); + return 0; + } +} + +void v4l2Device::Init( const char *devname, + uint32_t width, + uint32_t height, + uint32_t num_buffers, + enum V4L2PixelFormat v4l2Format, + enum AtomISPMode MipiMode, + int MipiPort) +{ + + (devname != NULL)? m_devname = devname : m_devname; + (m_width != width )? m_width = width : m_width; + (m_height != height)? m_height = height : m_height; + (m_num_buffers != num_buffers)? m_num_buffers = num_buffers : m_num_buffers; + (m_v4l2Format != v4l2Format )? m_v4l2Format = v4l2Format : m_v4l2Format; + (m_MipiMode != MipiMode )? m_MipiMode = MipiMode : m_MipiMode; + (m_MipiPort != MipiPort )? m_MipiPort = MipiPort : m_MipiPort; + + memset(&m_format, 0, sizeof m_format); + m_format.width = m_width; + m_format.height = m_height; + m_format.pixelformat = ConvertToV4L2FourCC(); + + V4L2Init(); +} + +void v4l2Device::V4L2Init() +{ + int ret; + struct v4l2_format fmt; + struct v4l2_capability caps; + struct v4l2_streamparm parm; + struct v4l2_requestbuffers rqbufs; + CLEAR(parm); + + m_fd = open(m_devname, O_RDWR); + BYE_ON(m_fd < 0, "failed to open %s: %s\n", m_devname, ERRSTR); + CLEAR(caps); + + /* Specifically for setting up mipi configuration. DMABUFF is + * also enable by default here. + */ + if (m_MipiPort > -1 && m_MipiMode != NONE) { + parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm.parm.capture.capturemode = GetAtomISPModes(m_MipiMode); + + ret = blockIOCTL(m_fd, VIDIOC_S_INPUT, &m_MipiPort); + BYE_ON(ret < 0, "VIDIOC_S_INPUT failed: %s\n", ERRSTR); + + ret = blockIOCTL(m_fd, VIDIOC_S_PARM, &parm); + BYE_ON(ret < 0, "VIDIOC_S_PARAM failed: %s\n", ERRSTR); + } + + ret = blockIOCTL(m_fd, VIDIOC_QUERYCAP, &caps); + msdk_printf( "Driver Caps:\n" + " Driver: \"%s\"\n" + " Card: \"%s\"\n" + " Bus: \"%s\"\n" + " Version: %d.%d\n" + " Capabilities: %08x\n", + caps.driver, + caps.card, + caps.bus_info, + (caps.version>>16)&&0xff, + (caps.version>>24)&&0xff, + caps.capabilities); + + BYE_ON(ret, "VIDIOC_QUERYCAP failed: %s\n", ERRSTR); + BYE_ON(~caps.capabilities & V4L2_CAP_VIDEO_CAPTURE, + "video: singleplanar capture is not supported\n"); + + CLEAR(fmt); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = blockIOCTL(m_fd, VIDIOC_G_FMT, &fmt); + + BYE_ON(ret < 0, "VIDIOC_G_FMT failed: %s\n", ERRSTR); + + msdk_printf("G_FMT(start): width = %u, height = %u, 4cc = %.4s, BPP = %u sizeimage = %d field = %d\n", + fmt.fmt.pix.width, fmt.fmt.pix.height, + (char*)&fmt.fmt.pix.pixelformat, + fmt.fmt.pix.bytesperline, + fmt.fmt.pix.sizeimage, + fmt.fmt.pix.field); + + fmt.fmt.pix = m_format; + + msdk_printf("G_FMT(pre): width = %u, height = %u, 4cc = %.4s, BPP = %u sizeimage = %d field = %d\n", + fmt.fmt.pix.width, fmt.fmt.pix.height, + (char*)&fmt.fmt.pix.pixelformat, + fmt.fmt.pix.bytesperline, + fmt.fmt.pix.sizeimage, + fmt.fmt.pix.field); + + ret = blockIOCTL(m_fd, VIDIOC_S_FMT, &fmt); + BYE_ON(ret < 0, "VIDIOC_S_FMT failed: %s\n", ERRSTR); + + ret = blockIOCTL(m_fd, VIDIOC_G_FMT, &fmt); + BYE_ON(ret < 0, "VIDIOC_G_FMT failed: %s\n", ERRSTR); + msdk_printf("G_FMT(final): width = %u, height = %u, 4cc = %.4s, BPP = %u\n", + fmt.fmt.pix.width, fmt.fmt.pix.height, + (char*)&fmt.fmt.pix.pixelformat, + fmt.fmt.pix.bytesperline); + + CLEAR(rqbufs); + rqbufs.count = m_num_buffers; + rqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rqbufs.memory = V4L2_MEMORY_DMABUF; + + ret = blockIOCTL(m_fd, VIDIOC_REQBUFS, &rqbufs); + BYE_ON(ret < 0, "VIDIOC_REQBUFS failed: %s\n", ERRSTR); + BYE_ON(rqbufs.count < m_num_buffers, "video node allocated only " + "%u of %u buffers\n", rqbufs.count, m_num_buffers); + + m_format = fmt.fmt.pix; +} + +void v4l2Device::V4L2Alloc() +{ + buffers = (Buffer *)malloc(sizeof(Buffer) * (int) m_num_buffers); +} + +void v4l2Device::V4L2QueueBuffer(Buffer *buffer) +{ + struct v4l2_buffer buf; + int ret; + + memset(&buf, 0, sizeof buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_DMABUF; + buf.index = buffer->index; + buf.m.fd = buffer->fd; + + ret = blockIOCTL(m_fd, VIDIOC_QBUF, &buf); + BYE_ON(ret < 0, "VIDIOC_QBUF for buffer %d failed: %s (fd %u) (i %u)\n", + buf.index, ERRSTR, buffer->fd, buffer->index); +} + +Buffer *v4l2Device::V4L2DeQueueBuffer(Buffer *buffer) +{ + struct v4l2_buffer buf; + int ret; + + memset(&buf, 0, sizeof buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_DMABUF; + + ret = blockIOCTL(m_fd, VIDIOC_DQBUF, &buf); + BYE_ON(ret, "VIDIOC_DQBUF failed: %s\n", ERRSTR); + + return &buffer[buf.index]; +} + +void v4l2Device::V4L2StartCapture() +{ + int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int ret = 0; + + ret = blockIOCTL(m_fd, VIDIOC_STREAMON, &type); + BYE_ON(ret < 0, "STREAMON failed: %s\n", ERRSTR); +} + +void v4l2Device::V4L2StopCapture() +{ + int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int ret = 0; + + ret = blockIOCTL(m_fd, VIDIOC_STREAMOFF, &type); + BYE_ON(ret < 0, "STREAMOFF failed: %s\n", ERRSTR); +} + +void v4l2Device::PutOnQ(int x) +{ + pthread_mutex_lock(&mutex); + m_q[m_first] = x; + m_first = (m_first+1) % 5; + m_numInQ++; + pthread_mutex_unlock(&mutex); + pthread_mutex_unlock(&empty); +} + +int v4l2Device::GetOffQ() +{ + int thing; + + /* wait if the queue is empty. */ + while (m_numInQ == 0) + pthread_mutex_lock(&empty); + + pthread_mutex_lock(&mutex); + thing = m_q[m_last]; + m_last = (m_last+1) % 5; + m_numInQ--; + pthread_mutex_unlock(&mutex); + + return thing; +} + +int v4l2Device::GetV4L2TerminationSignal() +{ + return (CtrlFlag && m_numInQ == 0)? 1 : 0; +} + +static void CtrlCTerminationHandler(int s) { CtrlFlag = true; } + +void *PollingThread(void *data) +{ + + v4l2Device *v4l2 = (v4l2Device *)data; + + struct sigaction sigIntHandler; + sigIntHandler.sa_handler = CtrlCTerminationHandler; + sigemptyset(&sigIntHandler.sa_mask); + sigIntHandler.sa_flags = 0; + sigaction(SIGINT, &sigIntHandler, NULL); + + struct pollfd fd; + fd.fd = v4l2->GetV4L2DisplayID(); + fd.events = POLLIN; + + while(1) + { + if (poll(&fd, 1, 5000) > 0) + { + if (fd.revents & POLLIN) + { + CurBuffers = v4l2->V4L2DeQueueBuffer(buffers); + v4l2->PutOnQ(CurBuffers->index); + + if (CtrlFlag) + break; + + if (CurBuffers) + v4l2->V4L2QueueBuffer(&buffers[CurBuffers->index]); + } + } + } +} + +#endif // ifdef ENABLE_V4L2_SUPPORT diff --git a/vshampor/deshuffler/sample_common/src/vaapi_allocator.cpp b/vshampor/deshuffler/sample_common/src/vaapi_allocator.cpp new file mode 100644 index 0000000..4f0091c --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vaapi_allocator.cpp @@ -0,0 +1,582 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#if defined(LIBVA_SUPPORT) + +#include +#include + +#include "vaapi_allocator.h" +#include "vaapi_utils.h" + +enum { + MFX_FOURCC_VP8_NV12 = MFX_MAKEFOURCC('V','P','8','N'), + MFX_FOURCC_VP8_MBDATA = MFX_MAKEFOURCC('V','P','8','M'), + MFX_FOURCC_VP8_SEGMAP = MFX_MAKEFOURCC('V','P','8','S'), +}; + +// TODO: remove this internal definition once it appears in VA API +#define VA_FOURCC_R5G6B5 MFX_MAKEFOURCC('R','G','1','6') + +unsigned int ConvertMfxFourccToVAFormat(mfxU32 fourcc) +{ + switch (fourcc) + { + case MFX_FOURCC_NV12: + return VA_FOURCC_NV12; + case MFX_FOURCC_YUY2: + return VA_FOURCC_YUY2; + case MFX_FOURCC_UYVY: + return VA_FOURCC_UYVY; + case MFX_FOURCC_YV12: + return VA_FOURCC_YV12; +#if (MFX_VERSION >= MFX_VERSION_NEXT) + case MFX_FOURCC_RGB565: + return VA_FOURCC_R5G6B5; +#endif + case MFX_FOURCC_RGB4: + return VA_FOURCC_ARGB; + case MFX_FOURCC_RGBP: + return VA_FOURCC_RGBP; + case MFX_FOURCC_P8: + return VA_FOURCC_P208; + case MFX_FOURCC_P010: + return VA_FOURCC_P010; + case MFX_FOURCC_A2RGB10: + return VA_FOURCC_ARGB; // rt format will be VA_RT_FORMAT_RGB32_10BPP + + default: + assert(!"unsupported fourcc"); + return 0; + } +} + +unsigned int ConvertVP8FourccToMfxFourcc(mfxU32 fourcc) +{ + switch (fourcc) + { + case MFX_FOURCC_VP8_NV12: + case MFX_FOURCC_VP8_MBDATA: + return MFX_FOURCC_NV12; + case MFX_FOURCC_VP8_SEGMAP: + return MFX_FOURCC_P8; + + default: + return fourcc; + } +} + +vaapiFrameAllocator::vaapiFrameAllocator() + : m_dpy(0) + , m_libva(new MfxLoader::VA_Proxy) + , m_export_mode(vaapiAllocatorParams::DONOT_EXPORT) + , m_exporter(NULL) +{ +} + +vaapiFrameAllocator::~vaapiFrameAllocator() +{ + Close(); + delete m_libva; +} + +mfxStatus vaapiFrameAllocator::Init(mfxAllocatorParams *pParams) +{ + vaapiAllocatorParams* p_vaapiParams = dynamic_cast(pParams); + + if ((NULL == p_vaapiParams) || (NULL == p_vaapiParams->m_dpy)) + return MFX_ERR_NOT_INITIALIZED; + + if ((p_vaapiParams->m_export_mode != vaapiAllocatorParams::DONOT_EXPORT) && + !(p_vaapiParams->m_export_mode & vaapiAllocatorParams::FLINK) && + !(p_vaapiParams->m_export_mode & vaapiAllocatorParams::PRIME) && + !(p_vaapiParams->m_export_mode & vaapiAllocatorParams::CUSTOM)) + return MFX_ERR_UNSUPPORTED; + if ((p_vaapiParams->m_export_mode & vaapiAllocatorParams::CUSTOM) && + !p_vaapiParams->m_exporter) + return MFX_ERR_UNSUPPORTED; + + m_dpy = p_vaapiParams->m_dpy; + m_export_mode = p_vaapiParams->m_export_mode; + m_exporter = p_vaapiParams->m_exporter; +#if defined(LIBVA_WAYLAND_SUPPORT) || defined (ENABLE_V4L2_SUPPORT)|| defined (X11_DRI3_SUPPORT) + // TODO this should be done on application level via allocator parameters!! + m_export_mode = vaapiAllocatorParams::PRIME; +#endif + return MFX_ERR_NONE; +} + +mfxStatus vaapiFrameAllocator::CheckRequestType(mfxFrameAllocRequest *request) +{ + mfxStatus sts = BaseFrameAllocator::CheckRequestType(request); + if (MFX_ERR_NONE != sts) + return sts; + + if ((request->Type & (MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET | MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET)) != 0) + return MFX_ERR_NONE; + else + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus vaapiFrameAllocator::Close() +{ + return BaseFrameAllocator::Close(); +} + +mfxStatus vaapiFrameAllocator::AllocImpl(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) +{ + mfxStatus mfx_res = MFX_ERR_NONE; + VAStatus va_res = VA_STATUS_SUCCESS; + unsigned int va_fourcc = 0; + VASurfaceID* surfaces = NULL; + VASurfaceAttrib attrib; + vaapiMemId *vaapi_mids = NULL, *vaapi_mid = NULL; + mfxMemId* mids = NULL; + mfxU32 fourcc = request->Info.FourCC; + mfxU16 surfaces_num = request->NumFrameSuggested, numAllocated = 0, i = 0; + bool bCreateSrfSucceeded = false; + + memset(response, 0, sizeof(mfxFrameAllocResponse)); + + // VP8 hybrid driver has weird requirements for allocation of surfaces/buffers for VP8 encoding + // to comply with them additional logic is required to support regular and VP8 hybrid allocation pathes + mfxU32 mfx_fourcc = ConvertVP8FourccToMfxFourcc(fourcc); + va_fourcc = ConvertMfxFourccToVAFormat(mfx_fourcc); + if (!va_fourcc || ((VA_FOURCC_NV12 != va_fourcc) && + (VA_FOURCC_YV12 != va_fourcc) && + (VA_FOURCC_YUY2 != va_fourcc) && + (VA_FOURCC_UYVY != va_fourcc) && +#if (MFX_VERSION >= MFX_VERSION_NEXT) + (VA_FOURCC_R5G6B5 != va_fourcc) && +#endif + (VA_FOURCC_ARGB != va_fourcc) && + (VA_FOURCC_RGBP != va_fourcc) && + (VA_FOURCC_P208 != va_fourcc) && + (VA_FOURCC_P010 != va_fourcc))) + { + msdk_printf(MSDK_STRING("VAAPI Allocator: invalid fourcc is provided (%#X), exitting\n"),va_fourcc); + return MFX_ERR_MEMORY_ALLOC; + } + if (!surfaces_num) + { + return MFX_ERR_MEMORY_ALLOC; + } + + if (MFX_ERR_NONE == mfx_res) + { + surfaces = (VASurfaceID*)calloc(surfaces_num, sizeof(VASurfaceID)); + vaapi_mids = (vaapiMemId*)calloc(surfaces_num, sizeof(vaapiMemId)); + mids = (mfxMemId*)calloc(surfaces_num, sizeof(mfxMemId)); + if ((NULL == surfaces) || (NULL == vaapi_mids) || (NULL == mids)) mfx_res = MFX_ERR_MEMORY_ALLOC; + } + if (MFX_ERR_NONE == mfx_res) + { + if( VA_FOURCC_P208 != va_fourcc ) + { + unsigned int format; + + attrib.type = VASurfaceAttribPixelFormat; + attrib.flags = VA_SURFACE_ATTRIB_SETTABLE; + attrib.value.type = VAGenericValueTypeInteger; + attrib.value.value.i = va_fourcc; + format = va_fourcc; + + if (fourcc == MFX_FOURCC_VP8_NV12) + { + // special configuration for NV12 surf allocation for VP8 hybrid encoder is required + attrib.type = (VASurfaceAttribType)VASurfaceAttribUsageHint; + attrib.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER; + } + else if (fourcc == MFX_FOURCC_VP8_MBDATA) + { + // special configuration for MB data surf allocation for VP8 hybrid encoder is required + attrib.value.value.i = VA_FOURCC_P208; + format = VA_FOURCC_P208; + } + else if (va_fourcc == VA_FOURCC_NV12) + { + format = VA_RT_FORMAT_YUV420; + } + else if ((va_fourcc == VA_FOURCC_UYVY) || (va_fourcc == VA_FOURCC_YUY2)) + { + format = VA_RT_FORMAT_YUV422; + } + else if (fourcc == MFX_FOURCC_A2RGB10) + { + format = VA_RT_FORMAT_RGB32_10BPP; + } + else if (fourcc == MFX_FOURCC_RGBP) + { + format = VA_RT_FORMAT_RGBP; + } + + va_res = m_libva->vaCreateSurfaces(m_dpy, + format, + request->Info.Width, request->Info.Height, + surfaces, + surfaces_num, + &attrib, 1); + + mfx_res = va_to_mfx_status(va_res); + bCreateSrfSucceeded = (MFX_ERR_NONE == mfx_res); + } + else + { + VAContextID context_id = request->AllocId; + int codedbuf_size; + + int width32 = 32 * ((request->Info.Width + 31) >> 5); + int height32 = 32 * ((request->Info.Height + 31) >> 5); + + VABufferType codedbuf_type; + if (fourcc == MFX_FOURCC_VP8_SEGMAP) + { + codedbuf_size = request->Info.Width * request->Info.Height; + codedbuf_type = (VABufferType)VAEncMacroblockMapBufferType; + } + else + { + codedbuf_size = static_cast((width32 * height32) * 400LL / (16 * 16)); + codedbuf_type = VAEncCodedBufferType; + } + + for (numAllocated = 0; numAllocated < surfaces_num; numAllocated++) + { + VABufferID coded_buf; + + va_res = m_libva->vaCreateBuffer(m_dpy, + context_id, + codedbuf_type, + codedbuf_size, + 1, + NULL, + &coded_buf); + mfx_res = va_to_mfx_status(va_res); + if (MFX_ERR_NONE != mfx_res) break; + surfaces[numAllocated] = coded_buf; + } + } + } + + if ((MFX_ERR_NONE == mfx_res) && + (request->Type & MFX_MEMTYPE_EXPORT_FRAME)) + { + if (m_export_mode == vaapiAllocatorParams::DONOT_EXPORT) { + mfx_res = MFX_ERR_UNKNOWN; + } + for (i=0; i < surfaces_num; ++i) + { + if (m_export_mode & vaapiAllocatorParams::NATIVE_EXPORT_MASK) { + vaapi_mids[i].m_buffer_info.mem_type = (m_export_mode & vaapiAllocatorParams::PRIME)? + VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME: VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM; + va_res = m_libva->vaDeriveImage(m_dpy, surfaces[i], &(vaapi_mids[i].m_image)); + mfx_res = va_to_mfx_status(va_res); + + if (MFX_ERR_NONE != mfx_res) break; + + va_res = m_libva->vaAcquireBufferHandle(m_dpy, vaapi_mids[i].m_image.buf, &(vaapi_mids[i].m_buffer_info)); + + mfx_res = va_to_mfx_status(va_res); + + if (MFX_ERR_NONE != mfx_res) { + m_libva->vaDestroyImage(m_dpy, vaapi_mids[i].m_image.image_id); + break; + } + } + if (m_exporter) { + vaapi_mids[i].m_custom = m_exporter->acquire(&vaapi_mids[i]); + if (!vaapi_mids[i].m_custom) { + mfx_res = MFX_ERR_UNKNOWN; + break; + } + } + } + } + if (MFX_ERR_NONE == mfx_res) + { + for (i = 0; i < surfaces_num; ++i) + { + vaapi_mid = &(vaapi_mids[i]); + vaapi_mid->m_fourcc = fourcc; + vaapi_mid->m_surface = &(surfaces[i]); + mids[i] = vaapi_mid; + } + } + if (MFX_ERR_NONE == mfx_res) + { + response->mids = mids; + response->NumFrameActual = surfaces_num; + } + else // i.e. MFX_ERR_NONE != mfx_res + { + response->mids = NULL; + response->NumFrameActual = 0; + if (VA_FOURCC_P208 != va_fourcc + || fourcc == MFX_FOURCC_VP8_MBDATA ) + { + if (bCreateSrfSucceeded) + m_libva->vaDestroySurfaces(m_dpy, surfaces, surfaces_num); + } + else + { + for (i = 0; i < numAllocated; i++) + m_libva->vaDestroyBuffer(m_dpy, surfaces[i]); + } + if (mids) + { + free(mids); + mids = NULL; + } + if (vaapi_mids) { free(vaapi_mids); vaapi_mids = NULL; } + if (surfaces) { free(surfaces); surfaces = NULL; } + } + return mfx_res; +} + +mfxStatus vaapiFrameAllocator::ReleaseResponse(mfxFrameAllocResponse *response) +{ + vaapiMemId *vaapi_mids = NULL; + VASurfaceID* surfaces = NULL; + mfxU32 i = 0; + bool isBitstreamMemory=false; + + if (!response) return MFX_ERR_NULL_PTR; + + if (response->mids) + { + vaapi_mids = (vaapiMemId*)(response->mids[0]); + mfxU32 mfx_fourcc = ConvertVP8FourccToMfxFourcc(vaapi_mids->m_fourcc); + isBitstreamMemory = (MFX_FOURCC_P8 == mfx_fourcc)?true:false; + surfaces = vaapi_mids->m_surface; + for (i = 0; i < response->NumFrameActual; ++i) + { + if (MFX_FOURCC_P8 == vaapi_mids[i].m_fourcc) m_libva->vaDestroyBuffer(m_dpy, surfaces[i]); + else if (vaapi_mids[i].m_sys_buffer) free(vaapi_mids[i].m_sys_buffer); + if (m_export_mode != vaapiAllocatorParams::DONOT_EXPORT) { + if (m_exporter && vaapi_mids[i].m_custom) { + m_exporter->release(&vaapi_mids[i], vaapi_mids[i].m_custom); + } + if (m_export_mode & vaapiAllocatorParams::NATIVE_EXPORT_MASK) { + m_libva->vaReleaseBufferHandle(m_dpy, vaapi_mids[i].m_image.buf); + m_libva->vaDestroyImage(m_dpy, vaapi_mids[i].m_image.image_id); + } + } + } + free(vaapi_mids); + free(response->mids); + response->mids = NULL; + + if (!isBitstreamMemory) m_libva->vaDestroySurfaces(m_dpy, surfaces, response->NumFrameActual); + free(surfaces); + } + response->NumFrameActual = 0; + return MFX_ERR_NONE; +} + +mfxStatus vaapiFrameAllocator::LockFrame(mfxMemId mid, mfxFrameData *ptr) +{ + mfxStatus mfx_res = MFX_ERR_NONE; + VAStatus va_res = VA_STATUS_SUCCESS; + vaapiMemId* vaapi_mid = (vaapiMemId*)mid; + mfxU8* pBuffer = 0; + VASurfaceAttrib attrib; + + if (!vaapi_mid || !(vaapi_mid->m_surface)) return MFX_ERR_INVALID_HANDLE; + + mfxU32 mfx_fourcc = ConvertVP8FourccToMfxFourcc(vaapi_mid->m_fourcc); + + if (MFX_FOURCC_P8 == mfx_fourcc) // bitstream processing + { + VACodedBufferSegment *coded_buffer_segment; + if (vaapi_mid->m_fourcc == MFX_FOURCC_VP8_SEGMAP) + va_res = m_libva->vaMapBuffer(m_dpy, *(vaapi_mid->m_surface), (void **)(&pBuffer)); + else + va_res = m_libva->vaMapBuffer(m_dpy, *(vaapi_mid->m_surface), (void **)(&coded_buffer_segment)); + mfx_res = va_to_mfx_status(va_res); + if (MFX_ERR_NONE == mfx_res) + { + if (vaapi_mid->m_fourcc == MFX_FOURCC_VP8_SEGMAP) + ptr->Y = pBuffer; + else + ptr->Y = (mfxU8*)coded_buffer_segment->buf; + + } + } + else // Image processing + { + va_res = m_libva->vaDeriveImage(m_dpy, *(vaapi_mid->m_surface), &(vaapi_mid->m_image)); + mfx_res = va_to_mfx_status(va_res); + + if (MFX_ERR_NONE == mfx_res) + { + va_res = m_libva->vaMapBuffer(m_dpy, vaapi_mid->m_image.buf, (void **) &pBuffer); + mfx_res = va_to_mfx_status(va_res); + } + if (MFX_ERR_NONE == mfx_res) + { + switch (vaapi_mid->m_image.format.fourcc) + { + case VA_FOURCC_NV12: + if (mfx_fourcc == MFX_FOURCC_NV12) + { + ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; + ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0]; + ptr->U = pBuffer + vaapi_mid->m_image.offsets[1]; + ptr->V = ptr->U + 1; + } + else mfx_res = MFX_ERR_LOCK_MEMORY; + break; + case VA_FOURCC_YV12: + if (mfx_fourcc == MFX_FOURCC_YV12) + { + ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; + ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0]; + ptr->V = pBuffer + vaapi_mid->m_image.offsets[1]; + ptr->U = pBuffer + vaapi_mid->m_image.offsets[2]; + } + else mfx_res = MFX_ERR_LOCK_MEMORY; + break; + case VA_FOURCC_YUY2: + if (mfx_fourcc == MFX_FOURCC_YUY2) + { + ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; + ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0]; + ptr->U = ptr->Y + 1; + ptr->V = ptr->Y + 3; + } + else mfx_res = MFX_ERR_LOCK_MEMORY; + break; + case VA_FOURCC_UYVY: + if (mfx_fourcc == MFX_FOURCC_UYVY) + { + ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; + ptr->U = pBuffer + vaapi_mid->m_image.offsets[0]; + ptr->Y = ptr->U + 1; + ptr->V = ptr->U + 2; + } + else mfx_res = MFX_ERR_LOCK_MEMORY; + break; +#if (MFX_VERSION >= MFX_VERSION_NEXT) + case VA_FOURCC_R5G6B5: + if (mfx_fourcc == MFX_FOURCC_RGB565) + { + ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; + ptr->B = pBuffer + vaapi_mid->m_image.offsets[0]; + ptr->G = ptr->B; + ptr->R = ptr->B; + } + else mfx_res = MFX_ERR_LOCK_MEMORY; + break; +#endif + case VA_FOURCC_ARGB: + if (mfx_fourcc == MFX_FOURCC_RGB4) + { + ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; + ptr->B = pBuffer + vaapi_mid->m_image.offsets[0]; + ptr->G = ptr->B + 1; + ptr->R = ptr->B + 2; + ptr->A = ptr->B + 3; + } + else if (mfx_fourcc == MFX_FOURCC_A2RGB10) + { + ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; + ptr->B = pBuffer + vaapi_mid->m_image.offsets[0]; + ptr->G = ptr->B; + ptr->R = ptr->B; + ptr->A = ptr->B; + } + else mfx_res = MFX_ERR_LOCK_MEMORY; + break; + case VA_FOURCC_RGBP: + if (mfx_fourcc == MFX_FOURCC_RGBP) + { + ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; + ptr->B = pBuffer + vaapi_mid->m_image.offsets[0]; + ptr->G = pBuffer + vaapi_mid->m_image.offsets[1]; + ptr->R = pBuffer + vaapi_mid->m_image.offsets[2]; + } + else mfx_res = MFX_ERR_LOCK_MEMORY; + break; + case VA_FOURCC_P208: + if (mfx_fourcc == MFX_FOURCC_NV12) + { + ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; + ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0]; + } + else mfx_res = MFX_ERR_LOCK_MEMORY; + break; + case VA_FOURCC_P010: + if (mfx_fourcc == MFX_FOURCC_P010) + { + ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; + ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0]; + ptr->U = pBuffer + vaapi_mid->m_image.offsets[1]; + ptr->V = ptr->U + 2; + } + else mfx_res = MFX_ERR_LOCK_MEMORY; + break; + default: + mfx_res = MFX_ERR_LOCK_MEMORY; + break; + } + } + } + return mfx_res; +} + +mfxStatus vaapiFrameAllocator::UnlockFrame(mfxMemId mid, mfxFrameData *ptr) +{ + vaapiMemId* vaapi_mid = (vaapiMemId*)mid; + + if (!vaapi_mid || !(vaapi_mid->m_surface)) return MFX_ERR_INVALID_HANDLE; + + mfxU32 mfx_fourcc = ConvertVP8FourccToMfxFourcc(vaapi_mid->m_fourcc); + + if (MFX_FOURCC_P8 == mfx_fourcc) // bitstream processing + { + m_libva->vaUnmapBuffer(m_dpy, *(vaapi_mid->m_surface)); + } + else // Image processing + { + m_libva->vaUnmapBuffer(m_dpy, vaapi_mid->m_image.buf); + m_libva->vaDestroyImage(m_dpy, vaapi_mid->m_image.image_id); + + if (NULL != ptr) + { + ptr->Pitch = 0; + ptr->Y = NULL; + ptr->U = NULL; + ptr->V = NULL; + ptr->A = NULL; + } + } + return MFX_ERR_NONE; +} + +mfxStatus vaapiFrameAllocator::GetFrameHDL(mfxMemId mid, mfxHDL *handle) +{ + vaapiMemId* vaapi_mid = (vaapiMemId*)mid; + + if (!handle || !vaapi_mid || !(vaapi_mid->m_surface)) return MFX_ERR_INVALID_HANDLE; + + *handle = vaapi_mid->m_surface; //VASurfaceID* <-> mfxHDL + return MFX_ERR_NONE; +} + +#endif // #if defined(LIBVA_SUPPORT) diff --git a/vshampor/deshuffler/sample_common/src/vaapi_device.cpp b/vshampor/deshuffler/sample_common/src/vaapi_device.cpp new file mode 100644 index 0000000..0a0c50a --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vaapi_device.cpp @@ -0,0 +1,531 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#if defined(LIBVA_DRM_SUPPORT) || defined(LIBVA_X11_SUPPORT) || defined(LIBVA_ANDROID_SUPPORT) + +#include "vaapi_device.h" + +#if defined(LIBVA_WAYLAND_SUPPORT) +#include "class_wayland.h" +#endif + +#if defined(LIBVA_X11_SUPPORT) + +#include +#include + +#include "vaapi_allocator.h" +#if defined(X11_DRI3_SUPPORT) +#include + +#define ALIGN(x, y) (((x) + (y) - 1) & -(y)) +#define PAGE_ALIGN(x) ALIGN(x, 4096) +#endif // X11_DRI3_SUPPORT + +#define VAAPI_GET_X_DISPLAY(_display) (Display*)(_display) +#define VAAPI_GET_X_WINDOW(_window) (Window*)(_window) + +CVAAPIDeviceX11::~CVAAPIDeviceX11(void) +{ + Close(); +} + +mfxStatus CVAAPIDeviceX11::Init(mfxHDL hWindow, mfxU16 nViews, mfxU32 nAdapterNum) +{ + mfxStatus mfx_res = MFX_ERR_NONE; + Window* window = NULL; + + if (nViews) + { + if (MFX_ERR_NONE == mfx_res) + { + m_window = window = (Window*)malloc(sizeof(Window)); + if (!m_window) mfx_res = MFX_ERR_MEMORY_ALLOC; + } + if (MFX_ERR_NONE == mfx_res) + { + Display* display = VAAPI_GET_X_DISPLAY(m_X11LibVA.GetXDisplay()); + MfxLoader::XLib_Proxy & x11lib = m_X11LibVA.GetX11(); + mfxU32 screen_number = DefaultScreen(display); + + *window = x11lib.XCreateSimpleWindow( + display, + RootWindow(display, screen_number), + m_bRenderWin ? m_nRenderWinX : 0, + m_bRenderWin ? m_nRenderWinY : 0, + 100, + 100, + 0, + 0, + BlackPixel(display, screen_number)); + + if (!(*window)) mfx_res = MFX_ERR_UNKNOWN; + else + { + x11lib.XMapWindow(display, *window); + x11lib.XSync(display, False); + } + } + } +#if defined(X11_DRI3_SUPPORT) + MfxLoader::DrmIntel_Proxy & drmintellib = m_X11LibVA.GetDrmIntelX11(); + MfxLoader::XLib_Proxy & x11lib = m_X11LibVA.GetX11(); + MfxLoader::X11_Xcb_Proxy & x11xcblib = m_X11LibVA.GetX11XcbX11(); + + m_dpy = x11lib.XOpenDisplay(NULL); + if (m_dpy == NULL){ + msdk_printf(MSDK_STRING("Failed to open display\n")); + return MFX_ERR_NOT_INITIALIZED; + } + m_xcbconn = x11xcblib.XGetXCBConnection(m_dpy); + + m_dri_fd = open("/dev/dri/card0", O_RDWR); + if (m_dri_fd < 0) { + msdk_printf(MSDK_STRING("Failed to open dri device\n")); + return MFX_ERR_NOT_INITIALIZED; + } + + m_bufmgr = drmintellib.drm_intel_bufmgr_gem_init(m_dri_fd, 4096); + if (!m_bufmgr){ + msdk_printf(MSDK_STRING("Failed to get buffer manager\n")); + return MFX_ERR_NOT_INITIALIZED; + } + +#endif + + return mfx_res; +} + +void CVAAPIDeviceX11::Close(void) +{ + if (m_window) + { + Display* display = VAAPI_GET_X_DISPLAY(m_X11LibVA.GetXDisplay()); + Window* window = VAAPI_GET_X_WINDOW(m_window); + + MfxLoader::XLib_Proxy & x11lib = m_X11LibVA.GetX11(); + x11lib.XDestroyWindow(display, *window); + + free(m_window); + m_window = NULL; + } +#if defined(X11_DRI3_SUPPORT) + if (m_dri_fd) + { + close(m_dri_fd); + } + if (m_dpy) + { + MfxLoader::XLib_Proxy & x11lib = m_X11LibVA.GetX11(); + x11lib.XCloseDisplay(m_dpy); + } +#endif +} + +mfxStatus CVAAPIDeviceX11::Reset(void) +{ + return MFX_ERR_NONE; +} + +mfxStatus CVAAPIDeviceX11::GetHandle(mfxHandleType type, mfxHDL *pHdl) +{ + if ((MFX_HANDLE_VA_DISPLAY == type) && (NULL != pHdl)) + { + *pHdl = m_X11LibVA.GetVADisplay(); + + return MFX_ERR_NONE; + } + + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus CVAAPIDeviceX11::SetHandle(mfxHandleType type, mfxHDL hdl) +{ + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus CVAAPIDeviceX11::RenderFrame(mfxFrameSurface1 * pSurface, mfxFrameAllocator * /*pmfxAlloc*/) +{ + VAStatus va_res = VA_STATUS_SUCCESS; + mfxStatus mfx_res = MFX_ERR_NONE; + vaapiMemId * memId = NULL; + +#if !defined(X11_DRI3_SUPPORT) + VASurfaceID surface; + Display* display = VAAPI_GET_X_DISPLAY(m_X11LibVA.GetXDisplay()); + Window* window = VAAPI_GET_X_WINDOW(m_window); + + if(!window || !(*window)) mfx_res = MFX_ERR_NOT_INITIALIZED; + // should MFX_ERR_NONE be returned below considering situation as EOS? + if ((MFX_ERR_NONE == mfx_res) && NULL == pSurface) mfx_res = MFX_ERR_NULL_PTR; + if (MFX_ERR_NONE == mfx_res) + { + memId = (vaapiMemId*)(pSurface->Data.MemId); + if (!memId || !memId->m_surface) mfx_res = MFX_ERR_NULL_PTR; + } + if (MFX_ERR_NONE == mfx_res) + { + VADisplay dpy = m_X11LibVA.GetVADisplay(); + VADisplay rnddpy = m_X11LibVA.GetVADisplay(true); + VASurfaceID rndsrf; + void* ctx; + + surface = *memId->m_surface; + + va_res = m_X11LibVA.AcquireVASurface(&ctx, dpy, surface, rnddpy, &rndsrf); + mfx_res = va_to_mfx_status(va_res); + if (MFX_ERR_NONE != mfx_res) return mfx_res; + + MfxLoader::XLib_Proxy & x11lib = m_X11LibVA.GetX11(); + x11lib.XResizeWindow(display, *window, pSurface->Info.CropW, pSurface->Info.CropH); + + + MfxLoader::VA_X11Proxy & vax11lib = m_X11LibVA.GetVAX11(); + va_res = vax11lib.vaPutSurface(rnddpy, + rndsrf, + *window, + pSurface->Info.CropX, + pSurface->Info.CropY, + pSurface->Info.CropX + pSurface->Info.CropW, + pSurface->Info.CropY + pSurface->Info.CropH, + pSurface->Info.CropX, + pSurface->Info.CropY, + pSurface->Info.CropX + pSurface->Info.CropW, + pSurface->Info.CropY + pSurface->Info.CropH, + NULL, + 0, + VA_FRAME_PICTURE); + + mfx_res = va_to_mfx_status(va_res); + x11lib.XSync(display, False); + + m_X11LibVA.ReleaseVASurface(ctx, dpy, surface, rnddpy, rndsrf); + + } + return mfx_res; +#else //\/ X11_DRI3_SUPPORT + Window* window = VAAPI_GET_X_WINDOW(m_window); + Window root; + drm_intel_bo *bo = NULL; + unsigned int border, depth, stride, size, + width, height; + int fd = 0, bpp = 0, x, y; + + MfxLoader::Xcb_Proxy & xcblib = m_X11LibVA.GetXcbX11(); + MfxLoader::XLib_Proxy & x11lib = m_X11LibVA.GetX11(); + MfxLoader::DrmIntel_Proxy & drmintellib = m_X11LibVA.GetDrmIntelX11(); + MfxLoader::Xcbpresent_Proxy & xcbpresentlib = m_X11LibVA.GetXcbpresentX11(); + MfxLoader::XCB_Dri3_Proxy & dri3lib= m_X11LibVA.GetXCBDri3X11(); + + if(!window || !(*window)) mfx_res = MFX_ERR_NOT_INITIALIZED; + // should MFX_ERR_NONE be returned below considering situation as EOS? + if ((MFX_ERR_NONE == mfx_res) && NULL == pSurface) mfx_res = MFX_ERR_NULL_PTR; + if (MFX_ERR_NONE == mfx_res) + { + memId = (vaapiMemId*)(pSurface->Data.MemId); + if (!memId || !memId->m_surface) mfx_res = MFX_ERR_NULL_PTR; + } + + if(memId && memId->m_buffer_info.mem_type != VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME){ + msdk_printf(MSDK_STRING("Memory type invalid!\n")); + return MFX_ERR_UNSUPPORTED; + } + + if (MFX_ERR_NONE == mfx_res) + { + x11lib.XResizeWindow(m_dpy, *window, pSurface->Info.CropW, pSurface->Info.CropH); + x11lib.XGetGeometry(m_dpy, *window, &root, &x, &y, &width, &height, &border, &depth); + + switch (depth) { + case 8: bpp = 8; break; + case 15: case 16: bpp = 16; break; + case 24: case 32: bpp = 32; break; + default: msdk_printf(MSDK_STRING("Invalid depth\n")); + } + + width = pSurface->Info.CropX + pSurface->Info.CropW; + height = pSurface->Info.CropY + pSurface->Info.CropH; + + stride = width * bpp/8; + size = PAGE_ALIGN(stride * height); + + bo = drmintellib.drm_intel_bo_gem_create_from_prime(m_bufmgr, memId->m_buffer_info.handle, size); + if (!bo) { + msdk_printf(MSDK_STRING("Failed to create buffer object\n")); + return MFX_ERR_INVALID_VIDEO_PARAM; + } + + drmintellib.drm_intel_bo_gem_export_to_prime(bo, &fd); + if (!fd){ + msdk_printf(MSDK_STRING("Invalid fd\n")); + return MFX_ERR_NOT_INITIALIZED; + } + + xcb_pixmap_t pixmap = xcblib.xcb_generate_id(m_xcbconn); + dri3lib.xcb_dri3_pixmap_from_buffer(m_xcbconn, pixmap, root, size, width, height, stride, depth, bpp, fd); + + xcbpresentlib.xcb_present_pixmap(m_xcbconn, + *window, pixmap, + 0, + 0, + 0, + 0, + 0, + None, + None, + None, + XCB_PRESENT_OPTION_NONE, + 0, + 0, + 0, + 0, NULL); + + xcblib.xcb_free_pixmap(m_xcbconn, pixmap); + xcblib.xcb_flush(m_xcbconn); + } + + return mfx_res; + +#endif // X11_DRI3_SUPPORT +} +#endif + +#if defined(LIBVA_WAYLAND_SUPPORT) +#include "wayland-drm-client-protocol.h" + +CVAAPIDeviceWayland::~CVAAPIDeviceWayland(void) +{ + Close(); +} + +mfxStatus CVAAPIDeviceWayland::Init(mfxHDL hWindow, mfxU16 nViews, mfxU32 nAdapterNum) +{ + mfxStatus mfx_res = MFX_ERR_NONE; + + if(nViews) + { + m_Wayland = (Wayland*)m_WaylandClient.WaylandCreate(); + if(!m_Wayland->InitDisplay()) { + return MFX_ERR_DEVICE_FAILED; + } + + if(NULL == m_Wayland->GetDisplay()) + { + mfx_res = MFX_ERR_UNKNOWN; + return mfx_res; + } + if(-1 == m_Wayland->DisplayRoundtrip()) + { + mfx_res = MFX_ERR_UNKNOWN; + return mfx_res; + } + if(!m_Wayland->CreateSurface()) + { + mfx_res = MFX_ERR_UNKNOWN; + return mfx_res; + } + } + return mfx_res; +} + +mfxStatus CVAAPIDeviceWayland::RenderFrame(mfxFrameSurface1 * pSurface, mfxFrameAllocator * /*pmfxAlloc*/) +{ + uint32_t drm_format = 0; + int offsets[3], pitches[3]; + mfxStatus mfx_res = MFX_ERR_NONE; + vaapiMemId * memId = NULL; + struct wl_buffer *m_wl_buffer = NULL; + if(NULL==pSurface) { + mfx_res = MFX_ERR_UNKNOWN; + return mfx_res; + } + m_Wayland->Sync(); + memId = (vaapiMemId*)(pSurface->Data.MemId); + + if (pSurface->Info.FourCC == MFX_FOURCC_NV12) + { + drm_format = WL_DRM_FORMAT_NV12; + } else if(pSurface->Info.FourCC == MFX_FOURCC_RGB4) + { + drm_format = WL_DRM_FORMAT_ARGB8888; + + if (m_isMondelloInputEnabled) + { + drm_format = WL_DRM_FORMAT_XBGR8888; + } + } + + offsets[0] = memId->m_image.offsets[0]; + offsets[1] = memId->m_image.offsets[1]; + offsets[2] = memId->m_image.offsets[2]; + pitches[0] = memId->m_image.pitches[0]; + pitches[1] = memId->m_image.pitches[1]; + pitches[2] = memId->m_image.pitches[2]; + m_wl_buffer = m_Wayland->CreatePrimeBuffer(memId->m_buffer_info.handle + , pSurface->Info.CropW + , pSurface->Info.CropH + , drm_format + , offsets + , pitches); + if(NULL == m_wl_buffer) + { + msdk_printf("\nCan't wrap flink to wl_buffer\n"); + mfx_res = MFX_ERR_UNKNOWN; + return mfx_res; + } + + m_Wayland->RenderBuffer(m_wl_buffer, pSurface->Info.CropW, pSurface->Info.CropH); + + return mfx_res; +} + +void CVAAPIDeviceWayland::Close(void) +{ + m_Wayland->FreeSurface(); +} + +CHWDevice* CreateVAAPIDevice(void) +{ + return new CVAAPIDeviceWayland(); +} + +#endif // LIBVA_WAYLAND_SUPPORT + +#if defined(LIBVA_DRM_SUPPORT) + +CVAAPIDeviceDRM::CVAAPIDeviceDRM(int type) + : m_DRMLibVA(type) + , m_rndr(NULL) +{ +} + +CVAAPIDeviceDRM::~CVAAPIDeviceDRM(void) +{ + MSDK_SAFE_DELETE(m_rndr); +} + +mfxStatus CVAAPIDeviceDRM::Init(mfxHDL hWindow, mfxU16 nViews, mfxU32 nAdapterNum) +{ + if (0 == nViews) { + return MFX_ERR_NONE; + } + if (1 == nViews) { + if (m_DRMLibVA.getBackendType() == MFX_LIBVA_DRM_RENDERNODE) { + return MFX_ERR_NONE; + } + mfxI32 * monitorType = (mfxI32*)hWindow; + if (!monitorType) return MFX_ERR_INVALID_VIDEO_PARAM; + try { + m_rndr = new drmRenderer(m_DRMLibVA.getFD(), *monitorType); + } catch(...) { + msdk_printf(MSDK_STRING("vaapi_device: failed to initialize drmrender\n")); + return MFX_ERR_UNKNOWN; + } + return MFX_ERR_NONE; + } + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus CVAAPIDeviceDRM::RenderFrame(mfxFrameSurface1 * pSurface, mfxFrameAllocator * pmfxAlloc) +{ + return (m_rndr)? m_rndr->render(pSurface): MFX_ERR_NONE; +} + +#endif + +#if defined(LIBVA_DRM_SUPPORT) || defined(LIBVA_X11_SUPPORT) || defined (LIBVA_WAYLAND_SUPPORT) + +CHWDevice* CreateVAAPIDevice(int type) +{ + CHWDevice * device = NULL; + + switch (type) + { + case MFX_LIBVA_DRM_RENDERNODE: + case MFX_LIBVA_DRM_MODESET: +#if defined(LIBVA_DRM_SUPPORT) + try + { + device = new CVAAPIDeviceDRM(type); + } + catch (std::exception&) + { + device = NULL; + } +#endif + break; + + case MFX_LIBVA_X11: +#if defined(LIBVA_X11_SUPPORT) + try + { + device = new CVAAPIDeviceX11; + } + catch (std::exception&) + { + device = NULL; + } +#endif + break; + case MFX_LIBVA_WAYLAND: +#if defined(LIBVA_WAYLAND_SUPPORT) + device = new CVAAPIDeviceWayland; +#endif + break; + case MFX_LIBVA_AUTO: +#if defined(LIBVA_X11_SUPPORT) + try + { + device = new CVAAPIDeviceX11; + } + catch (std::exception&) + { + device = NULL; + } +#endif +#if defined(LIBVA_DRM_SUPPORT) + if (!device) + { + try + { + device = new CVAAPIDeviceDRM(type); + } + catch (std::exception&) + { + device = NULL; + } + } +#endif + break; + } // switch(type) + + return device; +} + +#elif defined(LIBVA_ANDROID_SUPPORT) + +static AndroidLibVA g_LibVA; +CHWDevice* CreateVAAPIDevice(int type) +{ + return new CVAAPIDeviceAndroid(&g_LibVA); +} + +#endif + +#endif //#if defined(LIBVA_DRM_SUPPORT) || defined(LIBVA_X11_SUPPORT) || defined(LIBVA_ANDROID_SUPPORT) diff --git a/vshampor/deshuffler/sample_common/src/vaapi_utils.cpp b/vshampor/deshuffler/sample_common/src/vaapi_utils.cpp new file mode 100644 index 0000000..4c50d19 --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vaapi_utils.cpp @@ -0,0 +1,462 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifdef LIBVA_SUPPORT + +#include "vaapi_utils.h" +#include +#include + +//#if defined(LIBVA_DRM_SUPPORT) +#include "vaapi_utils_drm.h" +//#elif defined(LIBVA_X11_SUPPORT) +#include "vaapi_utils_x11.h" +//#endif + +#if defined(LIBVA_WAYLAND_SUPPORT) +#include "class_wayland.h" +#endif + +namespace MfxLoader +{ + +SimpleLoader::SimpleLoader(const char * name) +{ + dlerror(); + so_handle = dlopen(name, RTLD_GLOBAL | RTLD_NOW); + if (NULL == so_handle) + { + std::cerr << dlerror() << std::endl; + throw std::runtime_error("Can't load library"); + } +} + +void * SimpleLoader::GetFunction(const char * name) +{ + void * fn_ptr = dlsym(so_handle, name); + if (!fn_ptr) + throw std::runtime_error("Can't find function"); + return fn_ptr; +} + +SimpleLoader::~SimpleLoader() +{ + dlclose(so_handle); +} + +#define SIMPLE_LOADER_STRINGIFY1( x) #x +#define SIMPLE_LOADER_STRINGIFY(x) SIMPLE_LOADER_STRINGIFY1(x) +#define SIMPLE_LOADER_DECORATOR1(fun,suffix) fun ## _ ## suffix +#define SIMPLE_LOADER_DECORATOR(fun,suffix) SIMPLE_LOADER_DECORATOR1(fun,suffix) + + +// Following macro applied on vaInitialize will give: vaInitialize((vaInitialize_type)lib.GetFunction("vaInitialize")) +#define SIMPLE_LOADER_FUNCTION(name) name( (SIMPLE_LOADER_DECORATOR(name, type)) lib.GetFunction(SIMPLE_LOADER_STRINGIFY(name)) ) + + +#if defined(LIBVA_SUPPORT) +VA_Proxy::VA_Proxy() +#ifdef ANDROID + : lib("libva-android.so") +#else + : lib("libva.so.2") +#endif + , SIMPLE_LOADER_FUNCTION(vaInitialize) + , SIMPLE_LOADER_FUNCTION(vaTerminate) + , SIMPLE_LOADER_FUNCTION(vaCreateSurfaces) + , SIMPLE_LOADER_FUNCTION(vaDestroySurfaces) + , SIMPLE_LOADER_FUNCTION(vaCreateBuffer) + , SIMPLE_LOADER_FUNCTION(vaDestroyBuffer) + , SIMPLE_LOADER_FUNCTION(vaMapBuffer) + , SIMPLE_LOADER_FUNCTION(vaUnmapBuffer) + , SIMPLE_LOADER_FUNCTION(vaSyncSurface) + , SIMPLE_LOADER_FUNCTION(vaDeriveImage) + , SIMPLE_LOADER_FUNCTION(vaDestroyImage) + , SIMPLE_LOADER_FUNCTION(vaGetLibFunc) + , SIMPLE_LOADER_FUNCTION(vaAcquireBufferHandle) + , SIMPLE_LOADER_FUNCTION(vaReleaseBufferHandle) + , SIMPLE_LOADER_FUNCTION(vaMaxNumEntrypoints) + , SIMPLE_LOADER_FUNCTION(vaQueryConfigEntrypoints) + , SIMPLE_LOADER_FUNCTION(vaGetConfigAttributes) + , SIMPLE_LOADER_FUNCTION(vaCreateConfig) + , SIMPLE_LOADER_FUNCTION(vaCreateContext) + , SIMPLE_LOADER_FUNCTION(vaDestroyConfig) + , SIMPLE_LOADER_FUNCTION(vaDestroyContext) +{ +} + +VA_Proxy::~VA_Proxy() +{} + +#endif + +#if defined(LIBVA_DRM_SUPPORT) +DRM_Proxy::DRM_Proxy() + : lib("libdrm.so.2") + , SIMPLE_LOADER_FUNCTION(drmIoctl) + , SIMPLE_LOADER_FUNCTION(drmModeAddFB) + , SIMPLE_LOADER_FUNCTION(drmModeFreeConnector) + , SIMPLE_LOADER_FUNCTION(drmModeFreeCrtc) + , SIMPLE_LOADER_FUNCTION(drmModeFreeEncoder) + , SIMPLE_LOADER_FUNCTION(drmModeFreePlane) + , SIMPLE_LOADER_FUNCTION(drmModeFreePlaneResources) + , SIMPLE_LOADER_FUNCTION(drmModeFreeResources) + , SIMPLE_LOADER_FUNCTION(drmModeGetConnector) + , SIMPLE_LOADER_FUNCTION(drmModeGetCrtc) + , SIMPLE_LOADER_FUNCTION(drmModeGetEncoder) + , SIMPLE_LOADER_FUNCTION(drmModeGetPlane) + , SIMPLE_LOADER_FUNCTION(drmModeGetPlaneResources) + , SIMPLE_LOADER_FUNCTION(drmModeGetResources) + , SIMPLE_LOADER_FUNCTION(drmModeRmFB) + , SIMPLE_LOADER_FUNCTION(drmModeSetCrtc) + , SIMPLE_LOADER_FUNCTION(drmSetMaster) + , SIMPLE_LOADER_FUNCTION(drmDropMaster) + , SIMPLE_LOADER_FUNCTION(drmModeSetPlane) +{ +} + +DrmIntel_Proxy::~DrmIntel_Proxy() +{} + +DrmIntel_Proxy::DrmIntel_Proxy() + : lib("libdrm_intel.so.1") + , SIMPLE_LOADER_FUNCTION(drm_intel_bo_gem_create_from_prime) + , SIMPLE_LOADER_FUNCTION(drm_intel_bo_unreference) + , SIMPLE_LOADER_FUNCTION(drm_intel_bufmgr_gem_init) + , SIMPLE_LOADER_FUNCTION(drm_intel_bufmgr_destroy) +#if defined(X11_DRI3_SUPPORT) + , SIMPLE_LOADER_FUNCTION(drm_intel_bo_gem_export_to_prime) +#endif +{ +} + +DRM_Proxy::~DRM_Proxy() +{} + +VA_DRMProxy::VA_DRMProxy() + : lib("libva-drm.so.2") + , SIMPLE_LOADER_FUNCTION(vaGetDisplayDRM) +{ +} + +VA_DRMProxy::~VA_DRMProxy() +{} + +#if defined(X11_DRI3_SUPPORT) +XCB_Dri3_Proxy::XCB_Dri3_Proxy() + : lib("libxcb-dri3.so.0") + , SIMPLE_LOADER_FUNCTION(xcb_dri3_pixmap_from_buffer) +{ +} + +XCB_Dri3_Proxy::~XCB_Dri3_Proxy() +{} + +Xcb_Proxy::Xcb_Proxy() + : lib("libxcb.so.1") + , SIMPLE_LOADER_FUNCTION(xcb_generate_id) + , SIMPLE_LOADER_FUNCTION(xcb_free_pixmap) + , SIMPLE_LOADER_FUNCTION(xcb_flush) +{ +} + +Xcb_Proxy::~Xcb_Proxy() +{} + +X11_Xcb_Proxy::X11_Xcb_Proxy() + : lib("libX11-xcb.so.1") + , SIMPLE_LOADER_FUNCTION(XGetXCBConnection) +{ +} + +X11_Xcb_Proxy::~X11_Xcb_Proxy() +{} + +Xcbpresent_Proxy::Xcbpresent_Proxy() + : lib("libxcb-present.so.0") + , SIMPLE_LOADER_FUNCTION(xcb_present_pixmap) +{ +} + +Xcbpresent_Proxy::~Xcbpresent_Proxy() +{} +#endif // X11_DRI3_SUPPORT +#endif + +#if defined(LIBVA_WAYLAND_SUPPORT) + +VA_WaylandClientProxy::VA_WaylandClientProxy() + : lib("libmfx_wayland.so") + , SIMPLE_LOADER_FUNCTION(WaylandCreate) +{ +} + +VA_WaylandClientProxy::~VA_WaylandClientProxy() +{} + +#endif // LIBVA_WAYLAND_SUPPORT + +#if defined(LIBVA_X11_SUPPORT) +VA_X11Proxy::VA_X11Proxy() + : lib("libva-x11.so.2") + , SIMPLE_LOADER_FUNCTION(vaGetDisplay) + , SIMPLE_LOADER_FUNCTION(vaPutSurface) +{ +} + +VA_X11Proxy::~VA_X11Proxy() +{} + +XLib_Proxy::XLib_Proxy() + : lib("libX11.so.6") + , SIMPLE_LOADER_FUNCTION(XOpenDisplay) + , SIMPLE_LOADER_FUNCTION(XCloseDisplay) + , SIMPLE_LOADER_FUNCTION(XCreateSimpleWindow) + , SIMPLE_LOADER_FUNCTION(XMapWindow) + , SIMPLE_LOADER_FUNCTION(XSync) + , SIMPLE_LOADER_FUNCTION(XDestroyWindow) + , SIMPLE_LOADER_FUNCTION(XResizeWindow) +#if defined(X11_DRI3_SUPPORT) + , SIMPLE_LOADER_FUNCTION(XGetGeometry) +#endif // X11_DRI3_SUPPORT +{} + +XLib_Proxy::~XLib_Proxy() +{} + + +#endif + +#undef SIMPLE_LOADER_FUNCTION + +} // MfxLoader + +mfxStatus va_to_mfx_status(VAStatus va_res) +{ + mfxStatus mfxRes = MFX_ERR_NONE; + + switch (va_res) + { + case VA_STATUS_SUCCESS: + mfxRes = MFX_ERR_NONE; + break; + case VA_STATUS_ERROR_ALLOCATION_FAILED: + mfxRes = MFX_ERR_MEMORY_ALLOC; + break; + case VA_STATUS_ERROR_ATTR_NOT_SUPPORTED: + case VA_STATUS_ERROR_UNSUPPORTED_PROFILE: + case VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT: + case VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT: + case VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE: + case VA_STATUS_ERROR_FLAG_NOT_SUPPORTED: + case VA_STATUS_ERROR_RESOLUTION_NOT_SUPPORTED: + mfxRes = MFX_ERR_UNSUPPORTED; + break; + case VA_STATUS_ERROR_INVALID_DISPLAY: + case VA_STATUS_ERROR_INVALID_CONFIG: + case VA_STATUS_ERROR_INVALID_CONTEXT: + case VA_STATUS_ERROR_INVALID_SURFACE: + case VA_STATUS_ERROR_INVALID_BUFFER: + case VA_STATUS_ERROR_INVALID_IMAGE: + case VA_STATUS_ERROR_INVALID_SUBPICTURE: + mfxRes = MFX_ERR_NOT_INITIALIZED; + break; + case VA_STATUS_ERROR_INVALID_PARAMETER: + mfxRes = MFX_ERR_INVALID_VIDEO_PARAM; + default: + mfxRes = MFX_ERR_UNKNOWN; + break; + } + return mfxRes; +} + +#if defined(LIBVA_DRM_SUPPORT) || defined(LIBVA_X11_SUPPORT) +CLibVA* CreateLibVA(int type) +{ + CLibVA * libva = NULL; + switch (type) + { + case MFX_LIBVA_DRM: +#if defined(LIBVA_DRM_SUPPORT) + try + { + libva = new DRMLibVA(type); + } + catch (std::exception&) + { + libva = 0; + } +#endif + break; + + case MFX_LIBVA_X11: +#if defined(LIBVA_X11_SUPPORT) + try + { + libva = new X11LibVA; + } + catch (std::exception&) + { + libva = NULL; + } +#endif + break; + + case MFX_LIBVA_AUTO: +#if defined(LIBVA_X11_SUPPORT) + try + { + libva = new X11LibVA; + } + catch (std::exception&) + { + libva = NULL; + } +#endif +#if defined(LIBVA_DRM_SUPPORT) + if (!libva) + { + try + { + libva = new DRMLibVA(type); + } + catch (std::exception&) + { + libva = NULL; + } + } +#endif + break; + } // switch(type) + + return libva; +} +#endif // #if defined(LIBVA_DRM_SUPPORT) || defined(LIBVA_X11_SUPPORT) + +#if defined(LIBVA_X11_SUPPORT) + +struct AcquireCtx +{ + int fd; + VAImage image; +}; + +VAStatus CLibVA::AcquireVASurface( + void** pctx, + VADisplay dpy1, + VASurfaceID srf1, + VADisplay dpy2, + VASurfaceID* srf2) +{ + if (!pctx || !srf2) return VA_STATUS_ERROR_OPERATION_FAILED; + + if (dpy1 == dpy2) { + *srf2 = srf1; + return VA_STATUS_SUCCESS; + } + + AcquireCtx* ctx; + unsigned long handle=0; + VAStatus va_res; + VASurfaceAttrib attribs[2]; + VASurfaceAttribExternalBuffers extsrf; + VABufferInfo bufferInfo; + uint32_t memtype = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; + + MSDK_ZERO_MEMORY(attribs); + MSDK_ZERO_MEMORY(extsrf); + MSDK_ZERO_MEMORY(bufferInfo); + extsrf.num_buffers = 1; + extsrf.buffers = &handle; + + attribs[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType; + attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; + attribs[0].value.type = VAGenericValueTypeInteger; + attribs[0].value.value.i = memtype; + + attribs[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor; + attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; + attribs[1].value.type = VAGenericValueTypePointer; + attribs[1].value.value.p = &extsrf; + + ctx = (AcquireCtx*)calloc(1, sizeof(AcquireCtx)); + if (!ctx) return VA_STATUS_ERROR_OPERATION_FAILED; + + va_res = m_libva.vaDeriveImage(dpy1, srf1, &ctx->image); + if (VA_STATUS_SUCCESS != va_res) { + free(ctx); + return va_res; + } + + va_res = m_libva.vaAcquireBufferHandle(dpy1, ctx->image.buf, &bufferInfo); + if (VA_STATUS_SUCCESS != va_res) { + m_libva.vaDestroyImage(dpy1, ctx->image.image_id); + free(ctx); + return va_res; + } + + extsrf.width = ctx->image.width; + extsrf.height = ctx->image.height; + extsrf.num_planes = ctx->image.num_planes; + extsrf.pixel_format = ctx->image.format.fourcc; + for (int i=0; i < 3; ++i) { + extsrf.pitches[i] = ctx->image.pitches[i]; + extsrf.offsets[i] = ctx->image.offsets[i]; + } + extsrf.data_size = ctx->image.data_size; + extsrf.flags = memtype; + extsrf.buffers[0] = bufferInfo.handle; + + va_res = m_libva.vaCreateSurfaces(dpy2, + VA_RT_FORMAT_YUV420, + extsrf.width, extsrf.height, + srf2, 1, attribs, 2); + if (VA_STATUS_SUCCESS != va_res) { + m_libva.vaDestroyImage(dpy1, ctx->image.image_id); + free(ctx); + return va_res; + } + + *pctx = ctx; + + return VA_STATUS_SUCCESS; +} + +void CLibVA::ReleaseVASurface( + void* actx, + VADisplay dpy1, + VASurfaceID /*srf1*/, + VADisplay dpy2, + VASurfaceID srf2) +{ + if (dpy1 != dpy2) { + AcquireCtx* ctx = (AcquireCtx*)actx; + if (ctx) { + m_libva.vaDestroySurfaces(dpy2, &srf2, 1); + close(ctx->fd); + m_libva.vaReleaseBufferHandle(dpy1, ctx->image.buf); + m_libva.vaDestroyImage(dpy1, ctx->image.image_id); + free(ctx); + } + } +} + +#endif //LIBVA_X11_SUPPORT + +#endif // #ifdef LIBVA_SUPPORT diff --git a/vshampor/deshuffler/sample_common/src/vaapi_utils_android.cpp b/vshampor/deshuffler/sample_common/src/vaapi_utils_android.cpp new file mode 100644 index 0000000..5ca85de --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vaapi_utils_android.cpp @@ -0,0 +1,84 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#ifdef LIBVA_ANDROID_SUPPORT +#ifdef ANDROID + +#include "vaapi_utils_android.h" + +CLibVA* CreateLibVA(int) +{ + return new AndroidLibVA; +} + +/*------------------------------------------------------------------------------*/ + +typedef unsigned int vaapiAndroidDisplay; + +#define VAAPI_ANDROID_DEFAULT_DISPLAY 0x18c34078 + +AndroidLibVA::AndroidLibVA(void) + : CLibVA(MFX_LIBVA_AUTO) + , m_display(NULL) +{ + VAStatus va_res = VA_STATUS_SUCCESS; + mfxStatus sts = MFX_ERR_NONE; + int major_version = 0, minor_version = 0; + vaapiAndroidDisplay* display = NULL; + + m_display = display = (vaapiAndroidDisplay*)malloc(sizeof(vaapiAndroidDisplay)); + if (NULL == m_display) sts = MFX_ERR_NOT_INITIALIZED; + else *display = VAAPI_ANDROID_DEFAULT_DISPLAY; + + if (MFX_ERR_NONE == sts) + { + m_va_dpy = vaGetDisplay(m_display); + if (!m_va_dpy) + { + free(m_display); + sts = MFX_ERR_NULL_PTR; + } + } + if (MFX_ERR_NONE == sts) + { + va_res = vaInitialize(m_va_dpy, &major_version, &minor_version); + sts = va_to_mfx_status(va_res); + if (MFX_ERR_NONE != sts) + { + free(display); + m_display = NULL; + } + } + if (MFX_ERR_NONE != sts) throw std::bad_alloc(); +} + +AndroidLibVA::~AndroidLibVA(void) +{ + if (m_va_dpy) + { + vaTerminate(m_va_dpy); + } + if (m_display) + { + free(m_display); + } +} + +#endif // #ifdef ANDROID +#endif // #ifdef LIBVA_ANDROID_SUPPORT diff --git a/vshampor/deshuffler/sample_common/src/vaapi_utils_drm.cpp b/vshampor/deshuffler/sample_common/src/vaapi_utils_drm.cpp new file mode 100644 index 0000000..f25abac --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vaapi_utils_drm.cpp @@ -0,0 +1,608 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#if defined(LIBVA_DRM_SUPPORT) || defined(LIBVA_WAYLAND_SUPPORT) + +#include "vaapi_utils_drm.h" +#include "vaapi_allocator.h" +#include + +#include +#include + +#include +#include + +#define MFX_PCI_DIR "/sys/bus/pci/devices" +#define MFX_DRI_DIR "/dev/dri/" +#define MFX_PCI_DISPLAY_CONTROLLER_CLASS 0x03 + +struct mfx_disp_adapters +{ + mfxU32 vendor_id; + mfxU32 device_id; +}; + +static int mfx_dir_filter(const struct dirent* dir_ent) +{ + if (!dir_ent) return 0; + if (!strcmp(dir_ent->d_name, ".")) return 0; + if (!strcmp(dir_ent->d_name, "..")) return 0; + return 1; +} + +typedef int (*fsort)(const struct dirent**, const struct dirent**); + +static mfxU32 mfx_init_adapters(struct mfx_disp_adapters** p_adapters) +{ + mfxU32 adapters_num = 0; + int i = 0; + struct mfx_disp_adapters* adapters = NULL; + struct dirent** dir_entries = NULL; + int entries_num = scandir(MFX_PCI_DIR, &dir_entries, mfx_dir_filter, (fsort)alphasort); + + char file_name[300] = {}; + char str[16] = {0}; + FILE* file = NULL; + + for (i = 0; i < entries_num; ++i) + { + long int class_id = 0, vendor_id = 0, device_id = 0; + + if (!dir_entries[i]) + continue; + + // obtaining device class id + snprintf(file_name, sizeof(file_name)/sizeof(file_name[0]), "%s/%s/%s", MFX_PCI_DIR, dir_entries[i]->d_name, "class"); + file = fopen(file_name, "r"); + if (file) + { + if (fgets(str, sizeof(str), file)) + { + class_id = strtol(str, NULL, 16); + } + fclose(file); + + if (MFX_PCI_DISPLAY_CONTROLLER_CLASS == (class_id >> 16)) + { + // obtaining device vendor id + snprintf(file_name, sizeof(file_name)/sizeof(file_name[0]), "%s/%s/%s", MFX_PCI_DIR, dir_entries[i]->d_name, "vendor"); + file = fopen(file_name, "r"); + if (file) + { + if (fgets(str, sizeof(str), file)) + { + vendor_id = strtol(str, NULL, 16); + } + fclose(file); + } + // obtaining device id + snprintf(file_name, sizeof(file_name)/sizeof(file_name[0]), "%s/%s/%s", MFX_PCI_DIR, dir_entries[i]->d_name, "device"); + file = fopen(file_name, "r"); + if (file) + { + if (fgets(str, sizeof(str), file)) + { + device_id = strtol(str, NULL, 16); + } + fclose(file); + } + // adding valid adapter to the list + if (vendor_id && device_id) + { + struct mfx_disp_adapters* tmp_adapters = NULL; + + tmp_adapters = (mfx_disp_adapters*)realloc(adapters, + (adapters_num+1)*sizeof(struct mfx_disp_adapters)); + + if (tmp_adapters) + { + adapters = tmp_adapters; + adapters[adapters_num].vendor_id = vendor_id; + adapters[adapters_num].device_id = device_id; + + ++adapters_num; + } + } + } + } + free(dir_entries[i]); + } + if (entries_num) free(dir_entries); + if (p_adapters) *p_adapters = adapters; + + return adapters_num; +} + +DRMLibVA::DRMLibVA(int type) + : CLibVA(type) + , m_fd(-1) +{ + const mfxU32 IntelVendorID = 0x8086; + //the first Intel adapter is only required now, the second - in the future + const mfxU32 numberOfRequiredIntelAdapter = 1; + const char nodesNames[][8] = {"renderD", "card"}; + + VAStatus va_res = VA_STATUS_SUCCESS; + mfxStatus sts = MFX_ERR_NONE; + int major_version = 0, minor_version = 0; + + mfx_disp_adapters* adapters = NULL; + int adapters_num = mfx_init_adapters(&adapters); + + // Search for the required display adapter + int i = 0, nFoundAdapters = 0; + int nodesNumbers[] = {0,0}; + while ((i < adapters_num) && (nFoundAdapters != numberOfRequiredIntelAdapter)) + { + if (adapters[i].vendor_id == IntelVendorID) + { + nFoundAdapters++; + nodesNumbers[0] = i+128; //for render nodes + nodesNumbers[1] = i; //for card + } + i++; + } + if (adapters_num) free(adapters); + // If Intel adapter with specified number wasn't found, throws exception + if (nFoundAdapters != numberOfRequiredIntelAdapter) + throw std::range_error("The Intel adapter with a specified number wasn't found"); + + // Initialization of paths to the device nodes + char** adapterPaths = new char* [2]; + for (int i=0; i<2; i++) + { + if ((i == 0) && (type == MFX_LIBVA_DRM_MODESET)) { + adapterPaths[i] = NULL; + continue; + } + adapterPaths[i] = new char[sizeof(MFX_DRI_DIR) + sizeof(nodesNames[i]) + 3]; + sprintf(adapterPaths[i], "%s%s%d", MFX_DRI_DIR, nodesNames[i], nodesNumbers[i]); + } + + // Loading display. At first trying to open render nodes, then card. + for (int i=0; i<2; i++) + { + if (!adapterPaths[i]) { + sts = MFX_ERR_UNSUPPORTED; + continue; + } + sts = MFX_ERR_NONE; + m_fd = open(adapterPaths[i], O_RDWR); + + if (m_fd < 0) sts = MFX_ERR_NOT_INITIALIZED; + if (MFX_ERR_NONE == sts) + { + m_va_dpy = m_vadrmlib.vaGetDisplayDRM(m_fd); + + if (!m_va_dpy) + { + close(m_fd); + sts = MFX_ERR_NULL_PTR; + } + } + + if (MFX_ERR_NONE == sts) + { + va_res = m_libva.vaInitialize(m_va_dpy, &major_version, &minor_version); + sts = va_to_mfx_status(va_res); + if (MFX_ERR_NONE != sts) + { + close(m_fd); + m_fd = -1; + } + } + + if (MFX_ERR_NONE == sts) break; + } + + for (int i=0; i<2; i++) + { + delete [] adapterPaths[i]; + } + delete [] adapterPaths; + + if (MFX_ERR_NONE != sts) + { + if (m_va_dpy) + { + m_libva.vaTerminate(m_va_dpy); + m_va_dpy=0; + } + if (m_fd >= 0) + { + close(m_fd); + m_fd=-1; + } + + throw std::invalid_argument("Loading of VA display was failed"); + } +} + +DRMLibVA::~DRMLibVA(void) +{ + if (m_va_dpy) + { + m_libva.vaTerminate(m_va_dpy); + } + if (m_fd >= 0) + { + close(m_fd); + } +} + +struct drmMonitorsTable { + mfxI32 mfx_type; + uint32_t drm_type; + const msdk_char * type_name; +}; + +drmMonitorsTable g_drmMonitorsTable[] = { +#define __DECLARE(type) { MFX_MONITOR_ ## type, DRM_MODE_CONNECTOR_ ## type, MSDK_STRING(#type) } + __DECLARE(Unknown), + __DECLARE(VGA), + __DECLARE(DVII), + __DECLARE(DVID), + __DECLARE(DVIA), + __DECLARE(Composite), + __DECLARE(SVIDEO), + __DECLARE(LVDS), + __DECLARE(Component), + __DECLARE(9PinDIN), + __DECLARE(HDMIA), + __DECLARE(HDMIB), + __DECLARE(eDP), + __DECLARE(TV), + __DECLARE(DisplayPort), +#if defined(DRM_MODE_CONNECTOR_VIRTUAL) // from libdrm 2.4.59 + __DECLARE(VIRTUAL), +#endif +#if defined(DRM_MODE_CONNECTOR_DSI) // from libdrm 2.4.59 + __DECLARE(DSI) +#endif +#undef __DECLARE +}; + +uint32_t drmRenderer::getConnectorType(mfxI32 monitor_type) +{ + for (size_t i=0; i < sizeof(g_drmMonitorsTable)/sizeof(g_drmMonitorsTable[0]); ++i) { + if (g_drmMonitorsTable[i].mfx_type == monitor_type) { + return g_drmMonitorsTable[i].drm_type; + } + } + return DRM_MODE_CONNECTOR_Unknown; +} + +const msdk_char* drmRenderer::getConnectorName(uint32_t connector_type) +{ + for (size_t i=0; i < sizeof(g_drmMonitorsTable)/sizeof(g_drmMonitorsTable[0]); ++i) { + if (g_drmMonitorsTable[i].drm_type == connector_type) { + return g_drmMonitorsTable[i].type_name; + } + } + return MSDK_STRING("Unknown"); +} + +drmRenderer::drmRenderer(int fd, mfxI32 monitorType) + : m_fd(fd) + , m_bufmgr(NULL) + , m_overlay_wrn(true) + , m_pCurrentRenderTargetSurface(NULL) +{ + bool res = false; + uint32_t connectorType = getConnectorType(monitorType); + uint32_t i; + + if (monitorType == MFX_MONITOR_AUTO) { + connectorType = DRM_MODE_CONNECTOR_Unknown; + } else if (connectorType == DRM_MODE_CONNECTOR_Unknown) { + throw std::invalid_argument("Unsupported monitor type"); + } + drmModeRes *resource = m_drmlib.drmModeGetResources(m_fd); + if (resource) { + if (getConnector(resource, connectorType) && + getPlane()) { + res = true; + } + m_drmlib.drmModeFreeResources(resource); + } + if (!res) { + throw std::invalid_argument("Failed to allocate renderer"); + } + msdk_printf(MSDK_STRING("drmrender: connected via %s to %dx%d@%d capable display\n"), + getConnectorName(m_connector_type), m_mode.hdisplay, m_mode.vdisplay, m_mode.vrefresh); +} + +drmRenderer::~drmRenderer() +{ + m_drmlib.drmModeFreeCrtc(m_crtc); + if (m_bufmgr) + { + m_drmintellib.drm_intel_bufmgr_destroy(m_bufmgr); + m_bufmgr = NULL; + } +} + +bool drmRenderer::getConnector(drmModeRes *resource, uint32_t connector_type) +{ + bool found = false; + drmModeConnectorPtr connector = NULL; + + for (int i = 0; i < resource->count_connectors; ++i) { + connector = m_drmlib.drmModeGetConnector(m_fd, resource->connectors[i]); + if (connector) { + if ((connector->connector_type == connector_type) || + (connector_type == DRM_MODE_CONNECTOR_Unknown)) { + if (connector->connection == DRM_MODE_CONNECTED) { + msdk_printf(MSDK_STRING("drmrender: trying connection: %s\n"), getConnectorName(connector->connector_type)); + m_connector_type = connector->connector_type; + m_connectorID = connector->connector_id; + found = setupConnection(resource, connector); + if (found) msdk_printf(MSDK_STRING("drmrender: succeeded...\n")); + else msdk_printf(MSDK_STRING("drmrender: failed...\n")); + } else if ((connector_type != DRM_MODE_CONNECTOR_Unknown)) { + msdk_printf(MSDK_STRING("drmrender: error: requested monitor not connected\n")); + } + } + m_drmlib.drmModeFreeConnector(connector); + if (found) return true; + } + } + msdk_printf(MSDK_STRING("drmrender: error: requested monitor not available\n")); + return false; +} + +bool drmRenderer::setupConnection(drmModeRes *resource, drmModeConnector* connector) +{ + bool ret = false; + drmModeEncoderPtr encoder; + + if (!connector->count_modes) { + msdk_printf(MSDK_STRING("drmrender: error: no valid modes for %s connector\n"), + getConnectorName(connector->connector_type)); + return false; + } + // we will use the first available mode - that's always mode with the highest resolution + m_mode = connector->modes[0]; + + // trying encoder+crtc which are currently attached to connector + m_encoderID = connector->encoder_id; + encoder = m_drmlib.drmModeGetEncoder(m_fd, m_encoderID); + if (encoder) { + m_crtcID = encoder->crtc_id; + for (int j = 0; j < resource->count_crtcs; ++j) + { + if (m_crtcID == resource->crtcs[j]) + { + m_crtcIndex = j; + break; + } + } + ret = true; + msdk_printf(MSDK_STRING("drmrender: selected crtc already attached to connector\n")); + m_drmlib.drmModeFreeEncoder(encoder); + } + + // if previous attempt to get crtc failed, let performs global search + // searching matching encoder+crtc globally + if (!ret) { + for (int i = 0; i < connector->count_encoders; ++i) { + encoder = m_drmlib.drmModeGetEncoder(m_fd, connector->encoders[i]); + if (encoder) { + for (int j = 0; j < resource->count_crtcs; ++j) { + // check whether this CRTC works with the encoder + if ( !((encoder->possible_crtcs & (1 << j)) && + (encoder->crtc_id == resource->crtcs[j])) ) + continue; + + m_encoderID = connector->encoders[i]; + m_crtcIndex = j; + m_crtcID = resource->crtcs[j]; + ret = true; + msdk_printf(MSDK_STRING("drmrender: found crtc with global search\n")); + break; + } + m_drmlib.drmModeFreeEncoder(encoder); + if (ret) + break; + } + } + } + if (ret) { + m_crtc = m_drmlib.drmModeGetCrtc(m_fd, m_crtcID); + if (!m_crtc) + ret = false; + } else { + msdk_printf(MSDK_STRING("drmrender: failed to select crtc\n")); + } + return ret; +} + +bool drmRenderer::getPlane() +{ + drmModePlaneResPtr planes = m_drmlib.drmModeGetPlaneResources(m_fd); + if (!planes) { + return false; + } + for (uint32_t i = 0; i < planes->count_planes; ++i) { + drmModePlanePtr plane = m_drmlib.drmModeGetPlane(m_fd, planes->planes[i]); + if (plane) { + if (plane->possible_crtcs & (1 << m_crtcIndex)) { + for (uint32_t j = 0; j < plane->count_formats; ++j) { + if (plane->formats[j] == DRM_FORMAT_XRGB8888) { + m_planeID = plane->plane_id; + m_drmlib.drmModeFreePlane(plane); + m_drmlib.drmModeFreePlaneResources(planes); + return true; + } + } + } + m_drmlib.drmModeFreePlane(plane); + } + } + m_drmlib.drmModeFreePlaneResources(planes); + return false; +} + +bool drmRenderer::setMaster() +{ + int wait_count = 0; + do { + if (!m_drmlib.drmSetMaster(m_fd)) return true; + usleep(100); + ++wait_count; + } while(wait_count < 30000); + msdk_printf(MSDK_STRING("drmrender: error: failed to get drm mastership during 3 seconds - aborting\n")); + return false; +} + +void drmRenderer::dropMaster() +{ + m_drmlib.drmDropMaster(m_fd); +} + +bool drmRenderer::restore() +{ + if (!setMaster()) return false; + + int ret = m_drmlib.drmModeSetCrtc(m_fd, m_crtcID, m_crtc->buffer_id, m_crtc->x, m_crtc->y, &m_connectorID, 1, &m_mode); + if (ret) { + msdk_printf(MSDK_STRING("drmrender: failed to restore original mode\n")); + return false; + } + dropMaster(); + return true; +} + +void* drmRenderer::acquire(mfxMemId mid) +{ + vaapiMemId* vmid = (vaapiMemId*)mid; + uint32_t fbhandle=0; + + if (vmid->m_buffer_info.mem_type == VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME) { + if (!m_bufmgr) { + m_bufmgr = m_drmintellib.drm_intel_bufmgr_gem_init(m_fd, 4096); + if (!m_bufmgr) return NULL; + } + + drm_intel_bo* bo = m_drmintellib.drm_intel_bo_gem_create_from_prime( + m_bufmgr, (int)vmid->m_buffer_info.handle, vmid->m_buffer_info.mem_size); + if (!bo) return NULL; + + int ret = m_drmlib.drmModeAddFB(m_fd, + vmid->m_image.width, vmid->m_image.height, + 24, 32, vmid->m_image.pitches[0], + bo->handle, &fbhandle); + if (ret) { + return NULL; + } + m_drmintellib.drm_intel_bo_unreference(bo); + } else if (vmid->m_buffer_info.mem_type == VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM) { + struct drm_gem_open flink_open; + struct drm_gem_close flink_close; + + MSDK_ZERO_MEMORY(flink_open); + flink_open.name = vmid->m_buffer_info.handle; + int ret = m_drmlib.drmIoctl(m_fd, DRM_IOCTL_GEM_OPEN, &flink_open); + if (ret) return NULL; + + ret = m_drmlib.drmModeAddFB(m_fd, + vmid->m_image.width, vmid->m_image.height, + 24, 32, vmid->m_image.pitches[0], + flink_open.handle, &fbhandle); + if (ret) return NULL; + + MSDK_ZERO_MEMORY(flink_close); + flink_close.handle = flink_open.handle; + ret = m_drmlib.drmIoctl(m_fd, DRM_IOCTL_GEM_CLOSE, &flink_close); + if (ret) return NULL; + } else { + return NULL; + } + try { + uint32_t* hdl = new uint32_t; + *hdl = fbhandle; + return hdl; + } catch(...) { + return NULL; + } +} + +void drmRenderer::release(mfxMemId mid, void * mem) +{ + uint32_t* hdl = (uint32_t*)mem; + if (!hdl) return; + if (!restore()) { + msdk_printf(MSDK_STRING("drmrender: warning: failure to restore original mode may lead to application segfault!\n")); + } + m_drmlib.drmModeRmFB(m_fd, *hdl); + delete(hdl); +} + +mfxStatus drmRenderer::render(mfxFrameSurface1 * pSurface) +{ + int ret; + vaapiMemId * memid; + uint32_t fbhandle; + + if (!pSurface || !pSurface->Data.MemId) return MFX_ERR_INVALID_HANDLE; + memid = (vaapiMemId*)(pSurface->Data.MemId); + if (!memid->m_custom) return MFX_ERR_INVALID_HANDLE; + fbhandle = *(uint32_t*)memid->m_custom; + + // rendering on the screen + if (!setMaster()) { + return MFX_ERR_UNKNOWN; + } + if ((m_mode.hdisplay == memid->m_image.width) && + (m_mode.vdisplay == memid->m_image.height)) { + // surface in the framebuffer exactly matches crtc scanout port, so we + // can scanout from this framebuffer for the whole crtc + ret = m_drmlib.drmModeSetCrtc(m_fd, m_crtcID, fbhandle, 0, 0, &m_connectorID, 1, &m_mode); + if (ret) { + return MFX_ERR_UNKNOWN; + } + } else { + if (m_overlay_wrn) { + m_overlay_wrn = false; + msdk_printf(MSDK_STRING("drmrender: warning: rendering via OVERLAY plane\n")); + } + // surface in the framebuffer exactly does NOT match crtc scanout port, + // and we can only use overlay technique with possible resize (depending on the driver)) + ret = m_drmlib.drmModeSetPlane(m_fd, m_planeID, m_crtcID, fbhandle, 0, + 0, 0, m_crtc->width, m_crtc->height, + pSurface->Info.CropX << 16, pSurface->Info.CropY << 16, pSurface->Info.CropW << 16, pSurface->Info.CropH << 16); + if (ret) { + return MFX_ERR_UNKNOWN; + } + } + dropMaster(); + + /* Unlock previous Render Target Surface (if exists) */ + if (NULL != m_pCurrentRenderTargetSurface) + msdk_atomic_dec16((volatile mfxU16*)&m_pCurrentRenderTargetSurface->Data.Locked); + + /* new Render target */ + m_pCurrentRenderTargetSurface = pSurface; + /* And lock it */ + msdk_atomic_inc16((volatile mfxU16*)&m_pCurrentRenderTargetSurface->Data.Locked); + return MFX_ERR_NONE; +} + +#endif // #if defined(LIBVA_DRM_SUPPORT) diff --git a/vshampor/deshuffler/sample_common/src/vaapi_utils_x11.cpp b/vshampor/deshuffler/sample_common/src/vaapi_utils_x11.cpp new file mode 100644 index 0000000..0b321bf --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vaapi_utils_x11.cpp @@ -0,0 +1,198 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#if defined(LIBVA_X11_SUPPORT) + +#include "sample_defs.h" +#include "vaapi_utils_x11.h" + +#include +#if defined(X11_DRI3_SUPPORT) +#include +#endif + +#define VAAPI_X_DEFAULT_DISPLAY ":0.0" + +const char* PROCESSING_DRIVER_NAME="iHD"; +const char* RENDERING_DRIVER_NAME="iHD"; + +X11LibVA::X11LibVA(void) + : CLibVA(MFX_LIBVA_X11) + , m_display(0) + , m_contextID(VA_INVALID_ID) +{ + VAStatus va_res = VA_STATUS_SUCCESS; + mfxStatus sts = MFX_ERR_NONE; + int major_version = 0, minor_version = 0; + char* currentDisplay = getenv("DISPLAY"); + +#if !defined(X11_DRI3_SUPPORT) + try + { + if (currentDisplay) + m_display = m_x11lib.XOpenDisplay(currentDisplay); + else + m_display = m_x11lib.XOpenDisplay(VAAPI_X_DEFAULT_DISPLAY); + + if (NULL == m_display) sts = MFX_ERR_NOT_INITIALIZED; + if (MFX_ERR_NONE == sts) + { + m_va_dpy = m_vax11lib.vaGetDisplay(m_display); + m_va_dpy_render = m_vax11lib.vaGetDisplay(m_display); + + if (!m_va_dpy || !m_va_dpy_render) + { + m_x11lib.XCloseDisplay(m_display); + sts = MFX_ERR_NULL_PTR; + } + } + if (MFX_ERR_NONE == sts) + { + if (!setenv("LIBVA_DRIVER_NAME", PROCESSING_DRIVER_NAME, 1)) { + va_res = m_libva.vaInitialize(m_va_dpy, &major_version, &minor_version); + sts = va_to_mfx_status(va_res); + if (MFX_ERR_NONE != sts) { + m_x11lib.XCloseDisplay(m_display); + } + unsetenv("LIBVA_DRIVER_NAME"); + } else { + sts = MFX_ERR_NOT_INITIALIZED; + } + } + if (MFX_ERR_NONE == sts) + { + if (!msdk_strcmp(PROCESSING_DRIVER_NAME, RENDERING_DRIVER_NAME)) { + // same driver + VAStatus va_res = VA_STATUS_SUCCESS; + VAConfigID vpp_config_id = VA_INVALID_ID; + VAConfigAttrib cfgAttrib; + + m_va_dpy_render = m_va_dpy; + + cfgAttrib.type = VAConfigAttribRTFormat; + m_libva.vaGetConfigAttributes(m_va_dpy_render, + VAProfileNone, + VAEntrypointVideoProc, + &cfgAttrib, + 1); + + va_res = m_libva.vaCreateConfig(m_va_dpy_render, + VAProfileNone, + VAEntrypointVideoProc, + &cfgAttrib, + 1, + &vpp_config_id); + + /* Create a context for VPP pipe */ + va_res = m_libva.vaCreateContext(m_va_dpy_render, + vpp_config_id, + 0, + 0, + VA_PROGRESSIVE, + 0, + 0, + &m_contextID); + } else { + m_va_dpy_render = m_vax11lib.vaGetDisplay(m_display); + if (!m_va_dpy_render) + { + m_libva.vaTerminate(m_va_dpy); + m_x11lib.XCloseDisplay(m_display); + sts = MFX_ERR_NULL_PTR; + } + if (!setenv("LIBVA_DRIVER_NAME", RENDERING_DRIVER_NAME, 1)) { + va_res = m_libva.vaInitialize(m_va_dpy_render, &major_version, &minor_version); + sts = va_to_mfx_status(va_res); + if (MFX_ERR_NONE != sts) + { + m_libva.vaTerminate(m_va_dpy); + m_x11lib.XCloseDisplay(m_display); + } + unsetenv("LIBVA_DRIVER_NAME"); + } + } + } + } +#else + try + { + if (currentDisplay) + m_display = m_x11lib.XOpenDisplay(currentDisplay); + else + m_display = m_x11lib.XOpenDisplay(VAAPI_X_DEFAULT_DISPLAY); + + if (NULL == m_display) sts = MFX_ERR_NOT_INITIALIZED; + if (MFX_ERR_NONE == sts) + { + m_va_dpy = m_vax11lib.vaGetDisplay(m_display); + + if (!m_va_dpy) + { + m_x11lib.XCloseDisplay(m_display); + sts = MFX_ERR_NULL_PTR; + } + } + if (MFX_ERR_NONE == sts) + { + va_res = m_libva.vaInitialize(m_va_dpy, &major_version, &minor_version); + sts = va_to_mfx_status(va_res); + if (MFX_ERR_NONE != sts) + { + m_x11lib.XCloseDisplay(m_display); + } + } + } +#endif // X11_DRI3_SUPPORT + catch(std::exception& ) + { + sts = MFX_ERR_NOT_INITIALIZED; + } + + if (MFX_ERR_NONE != sts) throw std::bad_alloc(); +} + +X11LibVA::~X11LibVA(void) +{ + if (!msdk_strcmp(PROCESSING_DRIVER_NAME, RENDERING_DRIVER_NAME)) + { + //release context + if (m_contextID != VA_INVALID_ID) + { + m_libva.vaDestroyContext(m_va_dpy_render, m_contextID); + m_contextID = VA_INVALID_ID; + } + } + +#if !defined(X11_DRI3_SUPPORT) + if (m_va_dpy_render && (m_va_dpy_render != m_va_dpy)) + { + m_libva.vaTerminate(m_va_dpy_render); + } +#endif + if (m_va_dpy) + { + m_libva.vaTerminate(m_va_dpy); + } + if (m_display) + { + m_x11lib.XCloseDisplay(m_display); + } +} + +#endif // #if defined(LIBVA_X11_SUPPORT) diff --git a/vshampor/deshuffler/sample_common/src/vm/atomic.cpp b/vshampor/deshuffler/sample_common/src/vm/atomic.cpp new file mode 100644 index 0000000..bfcc7bd --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vm/atomic.cpp @@ -0,0 +1,19 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + diff --git a/vshampor/deshuffler/sample_common/src/vm/atomic_linux.cpp b/vshampor/deshuffler/sample_common/src/vm/atomic_linux.cpp new file mode 100644 index 0000000..052c934 --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vm/atomic_linux.cpp @@ -0,0 +1,62 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + + +#include "vm/atomic_defs.h" + +static mfxU16 msdk_atomic_add16(volatile mfxU16 *mem, mfxU16 val) +{ + asm volatile ("lock; xaddw %0,%1" + : "=r" (val), "=m" (*mem) + : "0" (val), "m" (*mem) + : "memory", "cc"); + return val; +} + +static mfxU32 msdk_atomic_add32(volatile mfxU32 *mem, mfxU32 val) +{ + asm volatile ("lock; xaddl %0,%1" + : "=r" (val), "=m" (*mem) + : "0" (val), "m" (*mem) + : "memory", "cc"); + return val; +} + +mfxU16 msdk_atomic_inc16(volatile mfxU16 *pVariable) +{ + return msdk_atomic_add16(pVariable, 1) + 1; +} + +/* Thread-safe 16-bit variable decrementing */ +mfxU16 msdk_atomic_dec16(volatile mfxU16 *pVariable) +{ + return msdk_atomic_add16(pVariable, (mfxU16)-1) + 1; +} + +mfxU32 msdk_atomic_inc32(volatile mfxU32 *pVariable) +{ + return msdk_atomic_add32(pVariable, 1) + 1; +} + +/* Thread-safe 16-bit variable decrementing */ +mfxU32 msdk_atomic_dec32(volatile mfxU32 *pVariable) +{ + return msdk_atomic_add32(pVariable, (mfxU32)-1) + 1; +} + diff --git a/vshampor/deshuffler/sample_common/src/vm/shared_object.cpp b/vshampor/deshuffler/sample_common/src/vm/shared_object.cpp new file mode 100644 index 0000000..9f1a77b --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vm/shared_object.cpp @@ -0,0 +1,21 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + diff --git a/vshampor/deshuffler/sample_common/src/vm/shared_object_linux.cpp b/vshampor/deshuffler/sample_common/src/vm/shared_object_linux.cpp new file mode 100644 index 0000000..326af34 --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vm/shared_object_linux.cpp @@ -0,0 +1,41 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + + +#include "vm/so_defs.h" +#include + +msdk_so_handle msdk_so_load(const msdk_char *file_name) +{ + if (!file_name) return NULL; + return (msdk_so_handle) dlopen(file_name, RTLD_LAZY); +} + +msdk_func_pointer msdk_so_get_addr(msdk_so_handle handle, const char *func_name) +{ + if (!handle) return NULL; + return (msdk_func_pointer)dlsym(handle, func_name); +} + +void msdk_so_free(msdk_so_handle handle) +{ + if (!handle) return; + dlclose(handle); +} + diff --git a/vshampor/deshuffler/sample_common/src/vm/thread.cpp b/vshampor/deshuffler/sample_common/src/vm/thread.cpp new file mode 100644 index 0000000..34c737a --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vm/thread.cpp @@ -0,0 +1,59 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include // std::bad_alloc + +#include "vm/thread_defs.h" + +AutomaticMutex::AutomaticMutex(MSDKMutex& mutex): + m_rMutex(mutex), + m_bLocked(false) +{ + if (MFX_ERR_NONE != Lock()) throw std::bad_alloc(); +}; +AutomaticMutex::~AutomaticMutex(void) +{ + Unlock(); +} + +mfxStatus AutomaticMutex::Lock(void) +{ + mfxStatus sts = MFX_ERR_NONE; + if (!m_bLocked) + { + if (!m_rMutex.Try()) + { + // add time measurement here to estimate how long you sleep on mutex... + sts = m_rMutex.Lock(); + } + m_bLocked = true; + } + return sts; +} + +mfxStatus AutomaticMutex::Unlock(void) +{ + mfxStatus sts = MFX_ERR_NONE; + if (m_bLocked) + { + sts = m_rMutex.Unlock(); + m_bLocked = false; + } + return sts; +} diff --git a/vshampor/deshuffler/sample_common/src/vm/thread_linux.cpp b/vshampor/deshuffler/sample_common/src/vm/thread_linux.cpp new file mode 100644 index 0000000..18a2e0e --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vm/thread_linux.cpp @@ -0,0 +1,317 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + + +#include // std::bad_alloc +#include // setrlimit +#include +#include +#include + +#include "vm/thread_defs.h" +#include "sample_utils.h" + +MSDKMutex::MSDKMutex(void) +{ + int res = pthread_mutex_init(&m_mutex, NULL); + if (res) throw std::bad_alloc(); +} + +MSDKMutex::~MSDKMutex(void) +{ + pthread_mutex_destroy(&m_mutex); +} + +mfxStatus MSDKMutex::Lock(void) +{ + return (pthread_mutex_lock(&m_mutex))? MFX_ERR_UNKNOWN: MFX_ERR_NONE; +} + +mfxStatus MSDKMutex::Unlock(void) +{ + return (pthread_mutex_unlock(&m_mutex))? MFX_ERR_UNKNOWN: MFX_ERR_NONE; +} + +int MSDKMutex::Try(void) +{ + return (pthread_mutex_trylock(&m_mutex))? 0: 1; +} + +/* ****************************************************************************** */ + +MSDKSemaphore::MSDKSemaphore(mfxStatus &sts, mfxU32 count): + msdkSemaphoreHandle(count) +{ + sts = MFX_ERR_NONE; + int res = pthread_cond_init(&m_semaphore, NULL); + if (!res) { + res = pthread_mutex_init(&m_mutex, NULL); + if (res) { + pthread_cond_destroy(&m_semaphore); + } + } + if (res) throw std::bad_alloc(); +} + +MSDKSemaphore::~MSDKSemaphore(void) +{ + pthread_mutex_destroy(&m_mutex); + pthread_cond_destroy(&m_semaphore); +} + +mfxStatus MSDKSemaphore::Post(void) +{ + int res = pthread_mutex_lock(&m_mutex); + if (!res) { + if (0 == m_count++) res = pthread_cond_signal(&m_semaphore); + } + int sts = pthread_mutex_unlock(&m_mutex); + if (!res) res = sts; + return (res)? MFX_ERR_UNKNOWN: MFX_ERR_NONE; +} + +mfxStatus MSDKSemaphore::Wait(void) +{ + int res = pthread_mutex_lock(&m_mutex); + if (!res) { + while(!m_count) { + res = pthread_cond_wait(&m_semaphore, &m_mutex); + } + if (!res) --m_count; + int sts = pthread_mutex_unlock(&m_mutex); + if (!res) res = sts; + } + return (res)? MFX_ERR_UNKNOWN: MFX_ERR_NONE; +} + +/* ****************************************************************************** */ + +MSDKEvent::MSDKEvent(mfxStatus &sts, bool manual, bool state): + msdkEventHandle(manual, state) +{ + sts = MFX_ERR_NONE; + + int res = pthread_cond_init(&m_event, NULL); + if (!res) { + res = pthread_mutex_init(&m_mutex, NULL); + if (res) { + pthread_cond_destroy(&m_event); + } + } + if (res) throw std::bad_alloc(); +} + +MSDKEvent::~MSDKEvent(void) +{ + pthread_mutex_destroy(&m_mutex); + pthread_cond_destroy(&m_event); +} + +mfxStatus MSDKEvent::Signal(void) +{ + int res = pthread_mutex_lock(&m_mutex); + if (!res) { + if (!m_state) { + m_state = true; + if (m_manual) res = pthread_cond_broadcast(&m_event); + else res = pthread_cond_signal(&m_event); + } + int sts = pthread_mutex_unlock(&m_mutex); + if (!res) res = sts; + } + return (res)? MFX_ERR_UNKNOWN: MFX_ERR_NONE; +} + +mfxStatus MSDKEvent::Reset(void) +{ + int res = pthread_mutex_lock(&m_mutex); + if (!res) + { + if (m_state) m_state = false; + res = pthread_mutex_unlock(&m_mutex); + } + return (res)? MFX_ERR_UNKNOWN: MFX_ERR_NONE; +} + +mfxStatus MSDKEvent::Wait(void) +{ + int res = pthread_mutex_lock(&m_mutex); + if (!res) + { + while(!m_state) res = pthread_cond_wait(&m_event, &m_mutex); + if (!m_manual) m_state = false; + int sts = pthread_mutex_unlock(&m_mutex); + if (!res) res = sts; + } + return (res)? MFX_ERR_UNKNOWN: MFX_ERR_NONE; +} + +mfxStatus MSDKEvent::TimedWait(mfxU32 msec) +{ + if (MFX_INFINITE == msec) return MFX_ERR_UNSUPPORTED; + mfxStatus mfx_res = MFX_ERR_NOT_INITIALIZED; + + int res = pthread_mutex_lock(&m_mutex); + if (!res) + { + if (!m_state) + { + struct timeval tval; + struct timespec tspec; + mfxI32 res; + + gettimeofday(&tval, NULL); + msec = 1000 * msec + tval.tv_usec; + tspec.tv_sec = tval.tv_sec + msec / 1000000; + tspec.tv_nsec = (msec % 1000000) * 1000; + res = pthread_cond_timedwait(&m_event, + &m_mutex, + &tspec); + if (!res) mfx_res = MFX_ERR_NONE; + else if (ETIMEDOUT == res) mfx_res = MFX_TASK_WORKING; + else mfx_res = MFX_ERR_UNKNOWN; + } + else mfx_res = MFX_ERR_NONE; + if (!m_manual) + m_state = false; + + res = pthread_mutex_unlock(&m_mutex); + if (res) mfx_res = MFX_ERR_UNKNOWN; + } + else mfx_res = MFX_ERR_UNKNOWN; + + return mfx_res; +} + +/* ****************************************************************************** */ + +void* msdk_thread_start(void* arg) +{ + if (arg) { + MSDKThread* thread = (MSDKThread*)arg; + + if (thread->m_func) thread->m_func(thread->m_arg); + thread->m_event->Signal(); + } + return NULL; +} + +/* ****************************************************************************** */ + +MSDKThread::MSDKThread(mfxStatus &sts, msdk_thread_callback func, void* arg): + msdkThreadHandle(func, arg) +{ + m_event = new MSDKEvent(sts, false, false); + if (pthread_create(&(m_thread), NULL, msdk_thread_start, this)) { + delete(m_event); + throw std::bad_alloc(); + } +} + +MSDKThread::~MSDKThread(void) +{ + delete m_event; +} + +mfxStatus MSDKThread::Wait(void) +{ + int res = pthread_join(m_thread, NULL); + return (res)? MFX_ERR_UNKNOWN: MFX_ERR_NONE; +} + +mfxStatus MSDKThread::TimedWait(mfxU32 msec) +{ + if (MFX_INFINITE == msec) return MFX_ERR_UNSUPPORTED; + + mfxStatus mfx_res = m_event->TimedWait(msec); + + if (MFX_ERR_NONE == mfx_res) { + return (pthread_join(m_thread, NULL))? MFX_ERR_UNKNOWN: MFX_ERR_NONE; + } + return mfx_res; +} + +mfxStatus MSDKThread::GetExitCode() +{ + if (!m_event) return MFX_ERR_NOT_INITIALIZED; + + /** @todo: Need to add implementation. */ + return MFX_ERR_NONE; +} + +/* ****************************************************************************** */ + +mfxStatus msdk_setrlimit_vmem(mfxU64 size) +{ + struct rlimit limit; + + limit.rlim_cur = size; + limit.rlim_max = size; + if (setrlimit(RLIMIT_AS, &limit)) return MFX_ERR_UNKNOWN; + return MFX_ERR_NONE; +} + +mfxStatus msdk_thread_get_schedtype(const msdk_char* str, mfxI32 &type) +{ + if (!msdk_strcmp(str, MSDK_STRING("fifo"))) { + type = SCHED_FIFO; + } + else if (!msdk_strcmp(str, MSDK_STRING("rr"))) { + type = SCHED_RR; + } + else if (!msdk_strcmp(str, MSDK_STRING("other"))) { + type = SCHED_OTHER; + } + else if (!msdk_strcmp(str, MSDK_STRING("batch"))) { + type = SCHED_BATCH; + } + else if (!msdk_strcmp(str, MSDK_STRING("idle"))) { + type = SCHED_IDLE; + } +// else if (!msdk_strcmp(str, MSDK_STRING("deadline"))) { +// type = SCHED_DEADLINE; +// } + else { + return MFX_ERR_UNSUPPORTED; + } + return MFX_ERR_NONE; +} + +void msdk_thread_printf_scheduling_help() +{ + msdk_printf(MSDK_STRING("Note on the scheduling types and priorities:\n")); + msdk_printf(MSDK_STRING(" - : .. (notes)\n")); + msdk_printf(MSDK_STRING("The following scheduling types requires root privileges:\n")); + msdk_printf(MSDK_STRING(" - fifo: %d .. %d (static priority: low .. high)\n"), sched_get_priority_min(SCHED_FIFO), sched_get_priority_max(SCHED_FIFO)); + msdk_printf(MSDK_STRING(" - rr: %d .. %d (static priority: low .. high)\n"), sched_get_priority_min(SCHED_RR), sched_get_priority_max(SCHED_RR)); + msdk_printf(MSDK_STRING("The following scheduling types can be used by non-privileged users:\n")); + msdk_printf(MSDK_STRING(" - other: 0 .. 0 (static priority always 0)\n")); + msdk_printf(MSDK_STRING(" - batch: 0 .. 0 (static priority always 0)\n")); + msdk_printf(MSDK_STRING(" - idle: n/a\n")); + msdk_printf(MSDK_STRING("If you want to adjust priority for the other or batch scheduling type,\n")); + msdk_printf(MSDK_STRING("you can do that process-wise using dynamic priority - so called nice value.\n")); + msdk_printf(MSDK_STRING("Range for the nice value is: %d .. %d (high .. low)\n"), PRIO_MIN, PRIO_MAX); + msdk_printf(MSDK_STRING("Please, see 'man(1) nice' for details.\n")); +} + +mfxU32 msdk_get_current_pid() +{ + return syscall(SYS_getpid); +} + diff --git a/vshampor/deshuffler/sample_common/src/vm/thread_windows.cpp b/vshampor/deshuffler/sample_common/src/vm/thread_windows.cpp new file mode 100644 index 0000000..9f1a77b --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vm/thread_windows.cpp @@ -0,0 +1,21 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + diff --git a/vshampor/deshuffler/sample_common/src/vm/time.cpp b/vshampor/deshuffler/sample_common/src/vm/time.cpp new file mode 100644 index 0000000..9f1a77b --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vm/time.cpp @@ -0,0 +1,21 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + diff --git a/vshampor/deshuffler/sample_common/src/vm/time_linux.cpp b/vshampor/deshuffler/sample_common/src/vm/time_linux.cpp new file mode 100644 index 0000000..3b7336f --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vm/time_linux.cpp @@ -0,0 +1,45 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + + +#include "vm/time_defs.h" +#include + +#define MSDK_TIME_MHZ 1000000 + +msdk_tick msdk_time_get_tick(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return (msdk_tick)tv.tv_sec * (msdk_tick)MSDK_TIME_MHZ + (msdk_tick)tv.tv_usec; +} + +msdk_tick msdk_time_get_frequency(void) +{ + return (msdk_tick)MSDK_TIME_MHZ; +} + +mfxU64 rdtsc(void){ + unsigned int lo,hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return ((mfxU64)hi << 32) | lo; +} + + diff --git a/vshampor/deshuffler/sample_common/src/vpp_ex.cpp b/vshampor/deshuffler/sample_common/src/vpp_ex.cpp new file mode 100644 index 0000000..647bd77 --- /dev/null +++ b/vshampor/deshuffler/sample_common/src/vpp_ex.cpp @@ -0,0 +1,277 @@ +/******************************************************************************\ +Copyright (c) 2005-2018, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This sample was distributed or derived from the Intel's Media Samples package. +The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio +or https://software.intel.com/en-us/media-client-solutions-support. +\**********************************************************************************/ + +#include "mfx_samples_config.h" + +#include "sample_defs.h" + +#include "vpp_ex.h" +#include "vm/atomic_defs.h" + +static const mfxU32 MFX_TIME_STAMP_FREQUENCY = 90000; + + +MFXVideoVPPEx::MFXVideoVPPEx(mfxSession session) : +MFXVideoVPP(session) +#if !defined (USE_VPP_EX) +{}; +#else +, m_nCurrentPTS(0) +, m_nIncreaseTime(0) +, m_nInputTimeStamp(0) +, m_nArraySize(0) +{ + memset(&m_VideoParams, 0, sizeof(m_VideoParams)); +}; + +mfxStatus MFXVideoVPPEx::Close(void) +{ + for(std::vector::iterator it = m_LockedSurfacesList.begin(); it != m_LockedSurfacesList.end(); ++it) + { + try { + msdk_atomic_dec16((volatile mfxU16*)(&(*it)->Data.Locked)); + } catch (...) { // improve robustness by try/catch + } + } + + m_LockedSurfacesList.clear(); + + return MFXVideoVPP::Close(); +}; + +mfxStatus MFXVideoVPPEx::QueryIOSurf(mfxVideoParam *par, mfxFrameAllocRequest request[2]) +{ + mfxVideoParam params; + + if (NULL == par) + { + return MFX_ERR_NULL_PTR; + }; + + MSDK_MEMCPY_VAR(params, par, sizeof(mfxVideoParam)); + + params.vpp.In.FrameRateExtD = params.vpp.Out.FrameRateExtD; + params.vpp.In.FrameRateExtN = params.vpp.Out.FrameRateExtN; + + return MFXVideoVPP::QueryIOSurf(¶ms, request); +}; + +mfxStatus MFXVideoVPPEx::Query(mfxVideoParam *in, mfxVideoParam *out) +{ + mfxVideoParam params; + + if (NULL == out) + { + return MFX_ERR_NULL_PTR; + } + + if (in) + { + MSDK_MEMCPY_VAR(params, in, sizeof(mfxVideoParam)); + + params.vpp.In.FrameRateExtD = params.vpp.Out.FrameRateExtD; + params.vpp.In.FrameRateExtN = params.vpp.Out.FrameRateExtN; + } + + return MFXVideoVPP::Query((in) ? ¶ms : NULL, out); +}; + +mfxStatus MFXVideoVPPEx::Init(mfxVideoParam *par) +{ + mfxStatus sts = MFX_ERR_NONE; + + if (NULL == par) + { + return MFX_ERR_NULL_PTR; + }; + + m_nCurrentPTS = 0; + m_nArraySize = 0; + m_nInputTimeStamp = 0; + + for(std::vector::iterator it = m_LockedSurfacesList.begin(); it != m_LockedSurfacesList.end(); ++it) + { + msdk_atomic_dec16((volatile mfxU16*)(&(*it)->Data.Locked)); + } + + m_LockedSurfacesList.clear(); + + m_nIncreaseTime = (mfxU64)((mfxF64)MFX_TIME_STAMP_FREQUENCY * par->vpp.Out.FrameRateExtD / par->vpp.Out.FrameRateExtN); + + MSDK_MEMCPY_VAR(m_VideoParams, par, sizeof(mfxVideoParam)); + + m_VideoParams.vpp.In.FrameRateExtD = m_VideoParams.vpp.Out.FrameRateExtD; + m_VideoParams.vpp.In.FrameRateExtN = m_VideoParams.vpp.Out.FrameRateExtN; + + sts = MFXVideoVPP::Init(&m_VideoParams); + + m_VideoParams.vpp.In.FrameRateExtD = par->vpp.In.FrameRateExtD; + m_VideoParams.vpp.In.FrameRateExtN = par->vpp.In.FrameRateExtN; + + return sts; +} + +mfxStatus MFXVideoVPPEx::GetVideoParam(mfxVideoParam *par) +{ + mfxStatus sts = MFXVideoVPP::GetVideoParam(par); + + if (MFX_ERR_NONE == sts) + { + par->vpp.In.FrameRateExtD = m_VideoParams.vpp.In.FrameRateExtD; + par->vpp.In.FrameRateExtN = m_VideoParams.vpp.In.FrameRateExtN; + + par->vpp.Out.FrameRateExtD = m_VideoParams.vpp.Out.FrameRateExtD; + par->vpp.Out.FrameRateExtN = m_VideoParams.vpp.Out.FrameRateExtN; + }; + + return sts; +}; + +mfxStatus MFXVideoVPPEx::RunFrameVPPAsync(mfxFrameSurface1 *in, mfxFrameSurface1 *out, mfxExtVppAuxData *aux, mfxSyncPoint *syncp) +{ + mfxStatus sts = MFX_ERR_NONE; + + if (NULL == out || NULL == syncp) + { + return MFX_ERR_NULL_PTR; + }; + + if (!in) + { + if (!m_LockedSurfacesList.empty()) + { + // subtract 1 to handle minimal difference between input and expected timestamps + if (m_nCurrentPTS - 1 <= m_LockedSurfacesList[0]->Data.TimeStamp) + { + mfxU64 nPTS = m_LockedSurfacesList[0]->Data.TimeStamp; + m_LockedSurfacesList[0]->Data.TimeStamp = m_nCurrentPTS; + + sts = MFXVideoVPP::RunFrameVPPAsync(m_LockedSurfacesList[0], out, aux, syncp); + + m_LockedSurfacesList[0]->Data.TimeStamp = nPTS; + + if (MFX_WRN_DEVICE_BUSY != sts) + { + m_nCurrentPTS += m_nIncreaseTime; + } + } + else + { + for(std::vector::iterator it = m_LockedSurfacesList.begin(); it != m_LockedSurfacesList.end(); ++it) + { + msdk_atomic_dec16((volatile mfxU16*)(&(*it)->Data.Locked)); + } + + m_LockedSurfacesList.clear(); + + return MFXVideoVPP::RunFrameVPPAsync(in, out, aux, syncp); + } + } + else + { + return MFXVideoVPP::RunFrameVPPAsync(in, out, aux, syncp); + } + } + else + { + m_nArraySize = m_LockedSurfacesList.size(); + + if (!m_nArraySize) + { + m_nCurrentPTS = (mfxU64)in->Data.TimeStamp; + + msdk_atomic_inc16((volatile mfxU16*)&in->Data.Locked); + m_LockedSurfacesList.push_back(in); + + return MFX_ERR_MORE_DATA; + } + + if (1 == m_nArraySize) + { + if (in->Data.TimeStamp < m_nCurrentPTS) + { + return MFX_ERR_MORE_DATA; + } + + if (in->Data.TimeStamp > m_LockedSurfacesList[0]->Data.TimeStamp && (in->Data.TimeStamp < m_nCurrentPTS + m_nIncreaseTime/2)) + { + return MFX_ERR_MORE_DATA; + } + } + + { + mfxStatus stsRunFrame = MFX_ERR_NONE; + + m_nInputTimeStamp = m_LockedSurfacesList[0]->Data.TimeStamp; + + if (m_nCurrentPTS <= m_LockedSurfacesList[0]->Data.TimeStamp || + m_nCurrentPTS < (in->Data.TimeStamp - (mfxF64)m_nIncreaseTime/2)) + { + m_nInputTimeStamp = m_LockedSurfacesList[0]->Data.TimeStamp; + m_LockedSurfacesList[0]->Data.TimeStamp = m_nCurrentPTS; + + stsRunFrame = sts = MFXVideoVPP::RunFrameVPPAsync(m_LockedSurfacesList[0], out, aux, syncp); + + m_LockedSurfacesList[0]->Data.TimeStamp = m_nInputTimeStamp; + + if (MFX_WRN_DEVICE_BUSY != stsRunFrame) + { + m_nCurrentPTS += m_nIncreaseTime; + } + + if (MFX_ERR_NONE == stsRunFrame) + { + sts = MFX_ERR_MORE_SURFACE; + } + } + + if (MFX_WRN_DEVICE_BUSY != stsRunFrame) + { + if (1 == m_nArraySize) + { + msdk_atomic_inc16((volatile mfxU16*)&in->Data.Locked); + m_LockedSurfacesList.push_back(in); + } + + if (m_nCurrentPTS > m_LockedSurfacesList[0]->Data.TimeStamp && + m_nCurrentPTS >= (m_LockedSurfacesList[1]->Data.TimeStamp - (mfxF64)m_nIncreaseTime/2)) + { + msdk_atomic_dec16((volatile mfxU16*)&m_LockedSurfacesList[0]->Data.Locked); + m_LockedSurfacesList.erase(m_LockedSurfacesList.begin()); + + if (MFX_ERR_NONE == stsRunFrame) + { + if (stsRunFrame != sts) + { + sts = MFX_ERR_NONE; + } + else + { + sts = MFX_ERR_MORE_DATA; + } + } + } + } + } + } + + return sts; +}; + +#endif diff --git a/vshampor/deshuffler/src/yuv_reader_seek.cpp b/vshampor/deshuffler/src/yuv_reader_seek.cpp new file mode 100644 index 0000000..ef25e5c --- /dev/null +++ b/vshampor/deshuffler/src/yuv_reader_seek.cpp @@ -0,0 +1,30 @@ +/* + * yuv_reader_seek.cpp + * + * Created on: Jul 5, 2018 + * Author: root + */ + +#include "yuv_reader_seek.h" +#include + +YUVReaderSeek::YUVReaderSeek(mfxU32 frame_number) +{ + if (frame_number != 0) + { + + } + return; +} + + +virtual mfxStatus YUVReaderSeek::Init(std::string filename, mfxU32 ColorFormat) +{ + std::list input(filename); + return CSmplYUVReader::Init(input, ColorFormat); +} + +mfxStatus YUVReaderSeek::Seek(mfxU32 frame_number) +{ + return MFX_ERR_MORE_DATA; +} diff --git a/vshampor/deshuffler/unit_tests/CMakeLists.txt b/vshampor/deshuffler/unit_tests/CMakeLists.txt index 304f0dd..31809c6 100644 --- a/vshampor/deshuffler/unit_tests/CMakeLists.txt +++ b/vshampor/deshuffler/unit_tests/CMakeLists.txt @@ -5,7 +5,8 @@ include_directories(googletest/googlemock) include_directories(googletest/googlemock/include) add_executable(unit_tests - deshuffler_test.cpp) + src/deshuffler_test.cpp + src/yuv_reader_seek_test.cpp) add_dependencies(unit_tests deshuffler gtest_main) target_link_libraries(unit_tests deshuffler gtest_main) diff --git a/vshampor/deshuffler/unit_tests/src/deshuffler_test.cpp b/vshampor/deshuffler/unit_tests/src/deshuffler_test.cpp new file mode 100644 index 0000000..4491028 --- /dev/null +++ b/vshampor/deshuffler/unit_tests/src/deshuffler_test.cpp @@ -0,0 +1,9 @@ +#include "deshuffler.h" +#include "gtest/gtest.h" +TEST(DeshufflerTest, ShouldCalculatePermutation) +{ + Deshuffler deshuffler; + deshuffler.CalculatePermutation(); + FAIL(); +} + diff --git a/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_test.cpp b/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_test.cpp new file mode 100644 index 0000000..dc1b1c2 --- /dev/null +++ b/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_test.cpp @@ -0,0 +1,29 @@ +/* + * yuv_reader_seek_test.cpp + * + * Created on: Jul 5, 2018 + * Author: root + */ + +#include "yuv_reader_seek.h" +#include "gtest/gtest.h" + +#include +#include +#include + +TEST(YUVReaderSeekTest, ShouldOpenI420YUVFile) +{ + YUVReaderSeek yuv_reader_seek; + std::string stream_filename("/msdk/MEDIASDK_ROOT/foreman_352x288_300.yuv"); + struct stat buffer; + if (stat(stream_filename.c_str(), &buffer) != 0) + { + std::cout << "Stream " << stream_filename << "not found, which is required for this test case to pass\n"; + FAIL(); + } + + yuv_reader_seek.Init(stream_filename, MFX_FOURCC_I420); + yuv_reader_seek.Seek(5); + FAIL(); +} From 9903d1044bd51453f05ee5129a9a60dfdbf67767 Mon Sep 17 00:00:00 2001 From: Vasily Shamporov Date: Fri, 6 Jul 2018 15:35:16 +0300 Subject: [PATCH 6/7] [vshampor] Add I420 YUV reader with seek functionality --- vshampor/deshuffler/.gitignore | 1 + vshampor/deshuffler/CMakeLists.txt | 11 ++- vshampor/deshuffler/include/deshuffler.h | 4 +- vshampor/deshuffler/include/yuv_reader_seek.h | 15 ---- .../deshuffler/include/yuv_reader_seek_i420.h | 17 ++++ .../sample_common/lib/libsample_common.a | Bin 879306 -> 879306 bytes vshampor/deshuffler/src/yuv_reader_seek.cpp | 30 ------- .../deshuffler/src/yuv_reader_seek_i420.cpp | 29 +++++++ vshampor/deshuffler/unit_tests/CMakeLists.txt | 9 +- .../src/yuv_reader_seek_i420_test.cpp | 82 ++++++++++++++++++ .../unit_tests/src/yuv_reader_seek_test.cpp | 29 ------- 11 files changed, 141 insertions(+), 86 deletions(-) delete mode 100644 vshampor/deshuffler/include/yuv_reader_seek.h create mode 100644 vshampor/deshuffler/include/yuv_reader_seek_i420.h delete mode 100644 vshampor/deshuffler/src/yuv_reader_seek.cpp create mode 100644 vshampor/deshuffler/src/yuv_reader_seek_i420.cpp create mode 100644 vshampor/deshuffler/unit_tests/src/yuv_reader_seek_i420_test.cpp delete mode 100644 vshampor/deshuffler/unit_tests/src/yuv_reader_seek_test.cpp diff --git a/vshampor/deshuffler/.gitignore b/vshampor/deshuffler/.gitignore index 6deb4d3..b822957 100644 --- a/vshampor/deshuffler/.gitignore +++ b/vshampor/deshuffler/.gitignore @@ -14,5 +14,6 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake lib/* +sample_common/lib/* *.pc /build/ diff --git a/vshampor/deshuffler/CMakeLists.txt b/vshampor/deshuffler/CMakeLists.txt index 66c1d25..b81c0dc 100644 --- a/vshampor/deshuffler/CMakeLists.txt +++ b/vshampor/deshuffler/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.11) project (deshuffler) if (NOT CMAKE_BUILD_TYPE) message(STATUS "No build type selected, default to Debug") - set(CMAKE_BUILD_TYPE "Release") + set(CMAKE_BUILD_TYPE "Debug") endif() set (CMAKE_CXX_STANDARD 11) @@ -22,10 +22,17 @@ add_subdirectory(sample_common) add_library(deshuffler STATIC src/deshuffler.cpp src/input_params.cpp - src/permutation_data.cpp) + src/permutation_data.cpp + src/yuv_reader_seek_i420.cpp) add_dependencies(deshuffler sample_common) target_link_libraries(deshuffler sample_common) add_executable(deshuffler_cl src/main.cpp) add_dependencies(deshuffler_cl deshuffler) target_link_libraries(deshuffler_cl deshuffler) + +add_custom_command( + POST_BUILD + TARGET deshuffler_cl + COMMAND unit_tests + WORKING_DIRECTORY unit_tests/bin/) \ No newline at end of file diff --git a/vshampor/deshuffler/include/deshuffler.h b/vshampor/deshuffler/include/deshuffler.h index 7a32859..274accf 100644 --- a/vshampor/deshuffler/include/deshuffler.h +++ b/vshampor/deshuffler/include/deshuffler.h @@ -3,7 +3,7 @@ #include "permutation_data.h" #include "input_params.h" -#include "yuv_reader_seek.h" +#include "yuv_reader_seek_i420.h" #include class Deshuffler @@ -18,7 +18,7 @@ class Deshuffler private: InputParams m_params; PermutationData m_permutation_data; - YUVReaderSeek m_YUVReaderSeek; + YUVReaderSeekI420 m_YUVReader; CSmplYUVWriter m_YUVWriter; }; diff --git a/vshampor/deshuffler/include/yuv_reader_seek.h b/vshampor/deshuffler/include/yuv_reader_seek.h deleted file mode 100644 index 04732f6..0000000 --- a/vshampor/deshuffler/include/yuv_reader_seek.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef SRC_YUV_READER_SEEK_H_ -#define SRC_YUV_READER_SEEK_H_ - -#include -#include - -class YUVReaderSeek: public CSmplYUVReader -{ -public: - YUVReaderSeek(mfxU32 frame_number = 0); - virtual mfxStatus Init(std::string filename, mfxU32 ColorFormat) override; - mfxStatus Seek(mfxU32 frame_number); -}; - -#endif /* SRC_YUV_READER_SEEK_H_ */ diff --git a/vshampor/deshuffler/include/yuv_reader_seek_i420.h b/vshampor/deshuffler/include/yuv_reader_seek_i420.h new file mode 100644 index 0000000..12d3b53 --- /dev/null +++ b/vshampor/deshuffler/include/yuv_reader_seek_i420.h @@ -0,0 +1,17 @@ +#ifndef SRC_YUV_READER_SEEK_H_ +#define SRC_YUV_READER_SEEK_H_ + +#include +#include + +class YUVReaderSeekI420: public CSmplYUVReader +{ +public: + mfxStatus Init(std::string filename, mfxU32 width, mfxU32 height); + void Seek(mfxU32 frame_number); +protected: + mfxU32 m_width = 0; + mfxU32 m_height = 0; +}; + +#endif /* SRC_YUV_READER_SEEK_H_ */ diff --git a/vshampor/deshuffler/sample_common/lib/libsample_common.a b/vshampor/deshuffler/sample_common/lib/libsample_common.a index e2fc2e93e019a9f1277717b3d18c097f188a9835..3849b03c7d917b9110e43b258b7bbb3ed2bf3b32 100644 GIT binary patch delta 604 zcmX?g)%4U=(+M(c7UmWfM&=up1PWL{%<1oTut;q7E6_LsmN7J&9?;4vzJ0O@<9!{3 zg6(~Yj0K+{D$F4&q(hj#TR==R0*kck_c8%7^LG7SmgDMRz1x9?cQbBp-Nus21Q+cQ|#gfGYQDX_w_3jLd!2%R> zta4ceB)}p@rtLsK05RM4fL8W*A0UE;5RFk~953ow!GSRS{SFrK_PB2xK+FlmT-)Qm zaaYWMC^w$Iz=Bs|`kn5g79B*X2eZdKyPR8w~$9Pg9E`tcYgUXvg&o z*cCd(T_AAgh0ueBt9?;4vzJ0O@<9!{7 zhzVH9_P#{Mf={d<{f5*1TUjNxONTIhw}2=y2Wx8A?_~mF=I#2uEXUPZKuWj2+riS! zxV?27ODYpw-~%&6mj%S&Uwc@7utP<*A2`6%%(%V!7)vq}ObF<}7RK!vr&zL}LY5GH z@6NCoEPyD2idf~c3P^xOjEviX-T-2@?E$Uq?>;~Tp+1Q!<9JaI5rH_NJ?z?+!ZrmvI{JDCANR*<2hys6#+T$8N~fY7TXt`;OS)CZhDL-72-6A&^t7F*>eba zkkiGt&%eNPA59VOHMk;6sJV={kc1!s^ZqUmG+>M@ArY1TfXAH;;yBCgK*NEdXa9mH z8|og*?HgY4{Da9u!tK;Y9u97>4r2p|8Snq_nCn7Cpk}Ps=7pMJY_MHHj~D7^m=0wl P-g1bkKxtqgX&C_kMZ(O| diff --git a/vshampor/deshuffler/src/yuv_reader_seek.cpp b/vshampor/deshuffler/src/yuv_reader_seek.cpp deleted file mode 100644 index ef25e5c..0000000 --- a/vshampor/deshuffler/src/yuv_reader_seek.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * yuv_reader_seek.cpp - * - * Created on: Jul 5, 2018 - * Author: root - */ - -#include "yuv_reader_seek.h" -#include - -YUVReaderSeek::YUVReaderSeek(mfxU32 frame_number) -{ - if (frame_number != 0) - { - - } - return; -} - - -virtual mfxStatus YUVReaderSeek::Init(std::string filename, mfxU32 ColorFormat) -{ - std::list input(filename); - return CSmplYUVReader::Init(input, ColorFormat); -} - -mfxStatus YUVReaderSeek::Seek(mfxU32 frame_number) -{ - return MFX_ERR_MORE_DATA; -} diff --git a/vshampor/deshuffler/src/yuv_reader_seek_i420.cpp b/vshampor/deshuffler/src/yuv_reader_seek_i420.cpp new file mode 100644 index 0000000..6db0fb6 --- /dev/null +++ b/vshampor/deshuffler/src/yuv_reader_seek_i420.cpp @@ -0,0 +1,29 @@ +/* + * yuv_reader_seek.cpp + * + * Created on: Jul 5, 2018 + * Author: root + */ + +#include "yuv_reader_seek_i420.h" +#include + +mfxStatus YUVReaderSeekI420::Init(std::string filename, mfxU32 width, mfxU32 height) +{ + m_width = width; + m_height = height; + std::list input(1, filename); + return CSmplYUVReader::Init(input, MFX_FOURCC_I420); +} + +// After calling Seek with "frame_number", LoadNextFrame will load +// the frame with an index equal to "frame_number". +void YUVReaderSeekI420::Seek(mfxU32 frame_number) +{ + mfxU32 offset = frame_number * 3 * m_width * m_height / 2; // I420 only + for (mfxU32 i = 0; i < m_files.size(); i++) + { + fseek(m_files[i], offset, SEEK_SET); + } + return; +} diff --git a/vshampor/deshuffler/unit_tests/CMakeLists.txt b/vshampor/deshuffler/unit_tests/CMakeLists.txt index 31809c6..6b09643 100644 --- a/vshampor/deshuffler/unit_tests/CMakeLists.txt +++ b/vshampor/deshuffler/unit_tests/CMakeLists.txt @@ -6,14 +6,7 @@ include_directories(googletest/googlemock/include) add_executable(unit_tests src/deshuffler_test.cpp - src/yuv_reader_seek_test.cpp) + src/yuv_reader_seek_i420_test.cpp) add_dependencies(unit_tests deshuffler gtest_main) target_link_libraries(unit_tests deshuffler gtest_main) -add_custom_command( - POST_BUILD - TARGET unit_tests - COMMAND unit_tests - WORKING_DIRECTORY bin/ - COMMENT "Running unit tests..." -) \ No newline at end of file diff --git a/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_i420_test.cpp b/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_i420_test.cpp new file mode 100644 index 0000000..c2925cc --- /dev/null +++ b/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_i420_test.cpp @@ -0,0 +1,82 @@ +/* + * yuv_reader_seek_test.cpp + * + * Created on: Jul 5, 2018 + * Author: root + */ + +#include "yuv_reader_seek_i420.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include + +TEST(YUVReaderSeekTest, ShouldOpenI420YUVFile) +{ + YUVReaderSeekI420 yuv_reader_seek; + std::string stream_filename("/msdk/MEDIASDK_STREAMS/YUV/foreman_352x288_300.yuv"); + struct stat buffer; + if (stat(stream_filename.c_str(), &buffer) != 0) + { + std::cout << "Stream " << stream_filename << " not found, which is required for this test case to pass\n"; + FAIL(); + } + + ASSERT_EQ(MFX_ERR_NONE, yuv_reader_seek.Init(stream_filename, 352, 288)); +} + +TEST(YUVReaderSeekTest, ShouldSeekInOpenFile) +{ + YUVReaderSeekI420 yuv_reader_seek; + std::string stream_filename("/msdk/MEDIASDK_STREAMS/YUV/foreman_352x288_300.yuv"); + struct stat buffer; + if (stat(stream_filename.c_str(), &buffer) != 0) + { + std::cout << "Stream " << stream_filename << " not found, which is required for this test case to pass\n"; + FAIL(); + } + + CSmplYUVReader yuv_reader_ref; + std::list input(1, stream_filename); + ASSERT_EQ(MFX_ERR_NONE, yuv_reader_seek.Init(stream_filename, 352, 288)); + ASSERT_EQ(MFX_ERR_NONE, yuv_reader_ref.Init(input, MFX_FOURCC_I420)); + mfxU32 seek_frame_num = 5; + + mfxFrameSurface1 ref_surf = { 0 }, seek_surf = { 0 }; + ref_surf.Info.Width = 352; + ref_surf.Info.Height = 288; + ref_surf.Info.FourCC = MFX_FOURCC_NV12; + ref_surf.Data.Pitch = 352; + seek_surf.Data.Pitch = 352; + + mfxU32 luma_size = ref_surf.Info.Width * ref_surf.Info.Height; + mfxU32 chroma_size = 2 * ref_surf.Info.Width * ref_surf.Info.Height / 4; + + seek_surf.Info = ref_surf.Info; + + ref_surf.Data.Y = (mfxU8*) malloc(luma_size); + ref_surf.Data.UV = (mfxU8*) malloc(chroma_size); + + seek_surf.Data.Y = (mfxU8*) malloc(luma_size); + seek_surf.Data.UV = (mfxU8*) malloc(chroma_size); + + for (mfxU32 i = 0; i < seek_frame_num; ++i) + { + ASSERT_EQ(MFX_ERR_NONE,yuv_reader_ref.LoadNextFrame(&ref_surf)); + } + + ASSERT_EQ(MFX_ERR_NONE,yuv_reader_ref.LoadNextFrame(&ref_surf)); + yuv_reader_seek.Seek(seek_frame_num); + ASSERT_EQ(MFX_ERR_NONE, yuv_reader_seek.LoadNextFrame(&seek_surf)); + + EXPECT_TRUE(std::equal(ref_surf.Data.Y, ref_surf.Data.Y + luma_size, seek_surf.Data.Y)); + EXPECT_TRUE(std::equal(ref_surf.Data.UV, ref_surf.Data.UV + chroma_size, seek_surf.Data.UV)); + + free(ref_surf.Data.Y); + free(ref_surf.Data.UV); + free(seek_surf.Data.Y); + free(seek_surf.Data.UV); +} diff --git a/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_test.cpp b/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_test.cpp deleted file mode 100644 index dc1b1c2..0000000 --- a/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_test.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * yuv_reader_seek_test.cpp - * - * Created on: Jul 5, 2018 - * Author: root - */ - -#include "yuv_reader_seek.h" -#include "gtest/gtest.h" - -#include -#include -#include - -TEST(YUVReaderSeekTest, ShouldOpenI420YUVFile) -{ - YUVReaderSeek yuv_reader_seek; - std::string stream_filename("/msdk/MEDIASDK_ROOT/foreman_352x288_300.yuv"); - struct stat buffer; - if (stat(stream_filename.c_str(), &buffer) != 0) - { - std::cout << "Stream " << stream_filename << "not found, which is required for this test case to pass\n"; - FAIL(); - } - - yuv_reader_seek.Init(stream_filename, MFX_FOURCC_I420); - yuv_reader_seek.Seek(5); - FAIL(); -} From 458b580a8d589904f2404c142eec587b3a472cc2 Mon Sep 17 00:00:00 2001 From: Vasily Shamporov Date: Fri, 27 Jul 2018 17:09:13 +0300 Subject: [PATCH 7/7] [vshampor] Add permutation data/task framework --- vshampor/deshuffler/.gitignore | 1 + vshampor/deshuffler/CMakeLists.txt | 5 ++-- vshampor/deshuffler/include/deshuffler.h | 4 ++- vshampor/deshuffler/include/input_params.h | 12 +++++++++ .../deshuffler/include/yuv_reader_seek_i420.h | 3 ++- vshampor/deshuffler/src/deshuffler.cpp | 25 +++++++++++++++++++ vshampor/deshuffler/src/main.cpp | 1 - .../deshuffler/src/yuv_reader_seek_i420.cpp | 8 +++--- .../src/yuv_reader_seek_i420_test.cpp | 18 ++++++------- 9 files changed, 58 insertions(+), 19 deletions(-) diff --git a/vshampor/deshuffler/.gitignore b/vshampor/deshuffler/.gitignore index b822957..851e853 100644 --- a/vshampor/deshuffler/.gitignore +++ b/vshampor/deshuffler/.gitignore @@ -17,3 +17,4 @@ lib/* sample_common/lib/* *.pc /build/ +*.lib diff --git a/vshampor/deshuffler/CMakeLists.txt b/vshampor/deshuffler/CMakeLists.txt index b81c0dc..7dc20e0 100644 --- a/vshampor/deshuffler/CMakeLists.txt +++ b/vshampor/deshuffler/CMakeLists.txt @@ -31,8 +31,7 @@ add_executable(deshuffler_cl src/main.cpp) add_dependencies(deshuffler_cl deshuffler) target_link_libraries(deshuffler_cl deshuffler) -add_custom_command( - POST_BUILD - TARGET deshuffler_cl +add_custom_target(run_tests ALL + DEPENDS deshuffler COMMAND unit_tests WORKING_DIRECTORY unit_tests/bin/) \ No newline at end of file diff --git a/vshampor/deshuffler/include/deshuffler.h b/vshampor/deshuffler/include/deshuffler.h index 274accf..2321c8e 100644 --- a/vshampor/deshuffler/include/deshuffler.h +++ b/vshampor/deshuffler/include/deshuffler.h @@ -4,6 +4,7 @@ #include "permutation_data.h" #include "input_params.h" #include "yuv_reader_seek_i420.h" +#include "permut_calc_task.h" #include class Deshuffler @@ -16,9 +17,10 @@ class Deshuffler void ReconstructStream(); void OutputStream(); private: + std::vector GeneratePermutCalcTasks(); + mfxStatus CalculatePermutCalcTask(PermutCalcTask& task); InputParams m_params; PermutationData m_permutation_data; - YUVReaderSeekI420 m_YUVReader; CSmplYUVWriter m_YUVWriter; }; diff --git a/vshampor/deshuffler/include/input_params.h b/vshampor/deshuffler/include/input_params.h index b9bac64..73e4b8b 100644 --- a/vshampor/deshuffler/include/input_params.h +++ b/vshampor/deshuffler/include/input_params.h @@ -2,12 +2,24 @@ #ifndef INPUT_PARAMS_H_ #define INPUT_PARAMS_H_ +#include +#include + +struct StreamInfo +{ + std::string filename; + mfxU32 width; + mfxU32 height; + mfxU32 frame_count; +}; class InputParams { public: InputParams() = default; InputParams(int argc, char* argv[]); + mfxU32 thread_count = 8; + }; diff --git a/vshampor/deshuffler/include/yuv_reader_seek_i420.h b/vshampor/deshuffler/include/yuv_reader_seek_i420.h index 12d3b53..f337483 100644 --- a/vshampor/deshuffler/include/yuv_reader_seek_i420.h +++ b/vshampor/deshuffler/include/yuv_reader_seek_i420.h @@ -3,11 +3,12 @@ #include #include +#include class YUVReaderSeekI420: public CSmplYUVReader { public: - mfxStatus Init(std::string filename, mfxU32 width, mfxU32 height); + mfxStatus Init(const StreamInfo& stream_info); void Seek(mfxU32 frame_number); protected: mfxU32 m_width = 0; diff --git a/vshampor/deshuffler/src/deshuffler.cpp b/vshampor/deshuffler/src/deshuffler.cpp index dd2a525..bcfd983 100644 --- a/vshampor/deshuffler/src/deshuffler.cpp +++ b/vshampor/deshuffler/src/deshuffler.cpp @@ -1,7 +1,32 @@ #include "deshuffler.h" +#include "permut_calc_task.h" +#include +#include void Deshuffler::CalculatePermutation() { + std::vector tasks = GeneratePermutCalcTasks(); + std::vector threads; + for (auto task : tasks) + { + mfxStatus sts = MFX_ERR_NONE; + sts = CalculatePermutCalcTask(task); + } + +} + +// Returns MFX_ERR_NONE if all permutation data is computed and valid +// Returns MFX_ERR_MORE_DATA_SUBMIT_TASK if permutation data +// for some frames in the task could not be computed/is invalid + +mfxStatus Deshuffler::CalculatePermutCalcTask(PermutCalcTask& task) +{ + return MFX_ERR_MORE_DATA_SUBMIT_TASK; +} + +std::vector Deshuffler::GeneratePermutCalcTasks() +{ + return std::vector(); } void Deshuffler::OutputPermutation() diff --git a/vshampor/deshuffler/src/main.cpp b/vshampor/deshuffler/src/main.cpp index b4cb27a..1d5b91e 100644 --- a/vshampor/deshuffler/src/main.cpp +++ b/vshampor/deshuffler/src/main.cpp @@ -11,4 +11,3 @@ int main(int argc, char* argv[]) { deshuffler.OutputStream(); return 0; } - diff --git a/vshampor/deshuffler/src/yuv_reader_seek_i420.cpp b/vshampor/deshuffler/src/yuv_reader_seek_i420.cpp index 6db0fb6..907225b 100644 --- a/vshampor/deshuffler/src/yuv_reader_seek_i420.cpp +++ b/vshampor/deshuffler/src/yuv_reader_seek_i420.cpp @@ -8,11 +8,11 @@ #include "yuv_reader_seek_i420.h" #include -mfxStatus YUVReaderSeekI420::Init(std::string filename, mfxU32 width, mfxU32 height) +mfxStatus YUVReaderSeekI420::Init(const StreamInfo& stream_info) { - m_width = width; - m_height = height; - std::list input(1, filename); + m_width = stream_info.width; + m_height = stream_info.height; + std::list input(1, stream_info.filename); return CSmplYUVReader::Init(input, MFX_FOURCC_I420); } diff --git a/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_i420_test.cpp b/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_i420_test.cpp index c2925cc..e41176f 100644 --- a/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_i420_test.cpp +++ b/vshampor/deshuffler/unit_tests/src/yuv_reader_seek_i420_test.cpp @@ -17,31 +17,31 @@ TEST(YUVReaderSeekTest, ShouldOpenI420YUVFile) { YUVReaderSeekI420 yuv_reader_seek; - std::string stream_filename("/msdk/MEDIASDK_STREAMS/YUV/foreman_352x288_300.yuv"); + StreamInfo stream = {"/msdk/MEDIASDK_STREAMS/YUV/foreman_352x288_300.yuv", 352, 288, 300}; struct stat buffer; - if (stat(stream_filename.c_str(), &buffer) != 0) + if (stat(stream.filename.c_str(), &buffer) != 0) { - std::cout << "Stream " << stream_filename << " not found, which is required for this test case to pass\n"; + std::cout << "Stream " << stream.filename << " not found, which is required for this test case to pass\n"; FAIL(); } - ASSERT_EQ(MFX_ERR_NONE, yuv_reader_seek.Init(stream_filename, 352, 288)); + ASSERT_EQ(MFX_ERR_NONE, yuv_reader_seek.Init(stream)); } TEST(YUVReaderSeekTest, ShouldSeekInOpenFile) { YUVReaderSeekI420 yuv_reader_seek; - std::string stream_filename("/msdk/MEDIASDK_STREAMS/YUV/foreman_352x288_300.yuv"); + StreamInfo stream = {"/msdk/MEDIASDK_STREAMS/YUV/foreman_352x288_300.yuv", 352, 288, 300}; struct stat buffer; - if (stat(stream_filename.c_str(), &buffer) != 0) + if (stat(stream.filename.c_str(), &buffer) != 0) { - std::cout << "Stream " << stream_filename << " not found, which is required for this test case to pass\n"; + std::cout << "Stream " << stream.filename << " not found, which is required for this test case to pass\n"; FAIL(); } CSmplYUVReader yuv_reader_ref; - std::list input(1, stream_filename); - ASSERT_EQ(MFX_ERR_NONE, yuv_reader_seek.Init(stream_filename, 352, 288)); + std::list input(1, stream.filename); + ASSERT_EQ(MFX_ERR_NONE, yuv_reader_seek.Init(stream)); ASSERT_EQ(MFX_ERR_NONE, yuv_reader_ref.Init(input, MFX_FOURCC_I420)); mfxU32 seek_frame_num = 5;