feat: decoder options
This commit is contained in:
parent
3e74159ade
commit
cbaf4069c7
66
examples/cashu.svg
Normal file
66
examples/cashu.svg
Normal file
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
width="135"
|
||||
height="153"
|
||||
shape-rendering="crispEdges"
|
||||
id="svg29096"
|
||||
sodipodi:docname="cashu.svg"
|
||||
inkscape:version="1.1.2 (0a00cf5, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs29100" />
|
||||
<sodipodi:namedview
|
||||
id="namedview29098"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.9146341"
|
||||
inkscape:cx="-5.4840764"
|
||||
inkscape:cy="91.923567"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="838"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg29096"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<path
|
||||
id="rect2"
|
||||
d="m 18,0 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 V 8 7 6 5 4 3 2 1 0 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z m 0,9 H 17 16 15 14 13 12 11 10 9 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 V 17 16 15 14 13 12 11 10 Z M 9,18 H 8 7 6 5 4 3 2 1 0 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 H 1 2 3 4 5 6 7 8 9 V 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 Z M 0,53 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 H 8 7 6 V 60 59 58 57 H 5 4 3 V 56 55 54 53 H 2 1 Z m 9,55 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,18 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,9 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,9 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z m 81,0 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v 1 1 1 1 1 1 1 1 z"
|
||||
style="fill:#b89563;fill-opacity:1" />
|
||||
<path
|
||||
id="rect38"
|
||||
d="m 36,0 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 V 8 7 6 5 4 3 2 1 0 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z m 45,9 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 V 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 h -1 -1 -1 -1 -1 -1 -1 -1 z m 0,27 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 V 44 43 42 41 40 39 38 37 Z M 63,64 v 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,8 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,18 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z m 36,9 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,9 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z"
|
||||
style="fill:#e2d2b3;fill-opacity:1" />
|
||||
<path
|
||||
id="rect1154"
|
||||
d="m 18,9 v 1 1 1 1 1 1 1 1 1 H 17 16 15 14 13 12 11 10 9 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h 1 1 1 1 1 1 1 1 1 V 17 16 15 14 13 12 11 10 9 H 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 Z M 9,61 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 z"
|
||||
style="fill:#c5a77f;fill-opacity:1" />
|
||||
<path
|
||||
id="rect1190"
|
||||
d="m 36,9 v 1 1 1 1 1 1 1 1 1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h 1 1 1 1 1 1 1 1 1 V 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z m 9,40 v 1 1 1 1 1 1 h -1 -1 v 1 1 1 h -1 -1 -1 v 1 1 1 h -1 -1 -1 -1 v 1 1 1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 v -1 -1 -1 h -1 -1 -1 v -1 -1 -1 -1 h -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 z"
|
||||
style="fill:#dbbf98;fill-opacity:1" />
|
||||
<path
|
||||
id="rect7292"
|
||||
d="m 0,45 v 1 1 1 1 1 1 1 1 h 1 1 1 v 1 1 1 1 h 1 1 1 v 1 1 1 1 h 1 1 1 1 v 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 h 1 1 1 1 v -1 -1 -1 h 1 1 1 v -1 -1 -1 h 1 1 v -1 -1 -1 -1 -1 -1 h 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 h 1 1 1 1 v 1 1 1 1 h 1 1 1 v 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 h 1 1 1 1 v -1 -1 -1 -1 h 1 1 1 1 v -1 -1 -1 -1 h 1 1 1 1 V 52 51 50 49 48 47 46 45 H 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 Z m 6,4 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v 1 1 1 1 h -1 -1 -1 -1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 1 H 17 16 15 14 V 60 59 58 57 H 13 12 11 10 V 56 55 54 53 H 9 8 7 6 v -1 -1 -1 z m 8,8 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 z m 44,-8 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v 1 1 1 1 h -1 -1 -1 -1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 1 h -1 -1 -1 -1 v -1 -1 -1 -1 h -1 -1 -1 -1 v -1 -1 -1 -1 h -1 -1 -1 -1 v -1 -1 -1 z m 8,8 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 z"
|
||||
style="fill:#000000;fill-opacity:1" />
|
||||
<path
|
||||
id="rect8072"
|
||||
d="m 6,49 v 1 1 1 1 h 1 1 1 1 V 52 51 50 49 H 9 8 7 Z m 4,4 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m 4,0 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 z m 4,0 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m 4,4 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m -4,0 h -1 -1 -1 -1 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 z m 40,-8 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m 4,4 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m 4,0 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 z m 4,0 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m 4,4 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m -4,0 h -1 -1 -1 -1 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 z"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<path
|
||||
id="rect17144"
|
||||
d="m 99,99 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z"
|
||||
style="fill:#f7f8f3;fill-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 9.4 KiB |
@ -1,26 +1,11 @@
|
||||
use ffmpeg_rs_raw::{Decoder, Demuxer};
|
||||
use ffmpeg_sys_the_third::{av_frame_free, av_packet_free};
|
||||
use std::collections::HashMap;
|
||||
use std::env::args;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::io::{Cursor, Read};
|
||||
use std::path::PathBuf;
|
||||
|
||||
struct DropTest {
|
||||
inner: File,
|
||||
}
|
||||
|
||||
impl Read for DropTest {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DropTest {
|
||||
fn drop(&mut self) {
|
||||
println!("Dropped!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let name = args().next().unwrap_or("main".to_string());
|
||||
let path = if let Some(path) = args().skip(1).next() {
|
||||
@ -35,11 +20,27 @@ fn main() {
|
||||
|
||||
let fd = read_as_file(path.clone());
|
||||
scan_input(fd);
|
||||
|
||||
let mut opt = HashMap::new();
|
||||
opt.insert("analyzeduration".to_string(), "999".to_string());
|
||||
let svg = include_bytes!("./cashu.svg");
|
||||
let mut dx = Demuxer::new_custom_io(svg.as_slice(), Some("cashu.svg".to_string()));
|
||||
dx.set_opt(opt.clone()).unwrap();
|
||||
unsafe {
|
||||
let mut decoder = Decoder::new();
|
||||
let info = dx.probe_input().expect("probe failed");
|
||||
for chan in &info.channels {
|
||||
decoder.setup_decoder(chan, None).expect("setup failed");
|
||||
}
|
||||
loop_decoder(dx, decoder);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_as_custom_io(path: PathBuf) -> Demuxer {
|
||||
let file = File::open(path).unwrap();
|
||||
Demuxer::new_custom_io(DropTest { inner: file })
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
File::open(path).unwrap().read_to_end(&mut data).unwrap();
|
||||
let reader = Cursor::new(data);
|
||||
Demuxer::new_custom_io(reader, None)
|
||||
}
|
||||
|
||||
fn read_as_file(path_buf: PathBuf) -> Demuxer {
|
||||
@ -50,8 +51,16 @@ fn scan_input(mut demuxer: Demuxer) {
|
||||
unsafe {
|
||||
let info = demuxer.probe_input().expect("demuxer failed");
|
||||
println!("{}", info);
|
||||
decode_input(demuxer);
|
||||
}
|
||||
}
|
||||
|
||||
let mut decoder = Decoder::new();
|
||||
unsafe fn decode_input(demuxer: Demuxer) {
|
||||
let decoder = Decoder::new();
|
||||
loop_decoder(demuxer, decoder);
|
||||
}
|
||||
|
||||
unsafe fn loop_decoder(mut demuxer: Demuxer, mut decoder: Decoder) {
|
||||
loop {
|
||||
let (mut pkt, stream) = demuxer.get_packet().expect("demuxer failed");
|
||||
if pkt.is_null() {
|
||||
@ -66,4 +75,3 @@ fn scan_input(mut demuxer: Demuxer) {
|
||||
av_packet_free(&mut pkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
109
src/decode.rs
109
src/decode.rs
@ -1,3 +1,4 @@
|
||||
use crate::{options_to_dict, StreamInfoChannel};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
@ -12,12 +13,23 @@ use ffmpeg_sys_the_third::{
|
||||
};
|
||||
use libc::memcpy;
|
||||
|
||||
struct CodecContext {
|
||||
pub struct DecoderCodecContext {
|
||||
pub context: *mut AVCodecContext,
|
||||
pub codec: *const AVCodec,
|
||||
}
|
||||
|
||||
impl Drop for CodecContext {
|
||||
impl DecoderCodecContext {
|
||||
/// Set [AVCodecContext] options
|
||||
pub fn set_opt(&mut self, options: HashMap<String, String>) -> Result<(), Error> {
|
||||
crate::set_opts(self.context as *mut libc::c_void, options)
|
||||
}
|
||||
|
||||
pub fn list_opts(&self) -> Result<Vec<String>, Error> {
|
||||
crate::list_opts(self.context as *mut libc::c_void)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DecoderCodecContext {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
avcodec_free_context(&mut self.context);
|
||||
@ -27,14 +39,13 @@ impl Drop for CodecContext {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for DecoderCodecContext {}
|
||||
unsafe impl Sync for DecoderCodecContext {}
|
||||
|
||||
pub struct Decoder {
|
||||
codecs: HashMap<i32, CodecContext>,
|
||||
codecs: HashMap<i32, DecoderCodecContext>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Decoder {}
|
||||
|
||||
unsafe impl Sync for Decoder {}
|
||||
|
||||
impl Decoder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@ -42,6 +53,62 @@ impl Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set up a decoder for a given channel
|
||||
pub fn setup_decoder(
|
||||
&mut self,
|
||||
channel: &StreamInfoChannel,
|
||||
options: Option<HashMap<String, String>>,
|
||||
) -> Result<&mut DecoderCodecContext, Error> {
|
||||
unsafe { self.setup_decoder_for_stream(channel.stream, options) }
|
||||
}
|
||||
|
||||
/// Set up a decoder from an [AVStream]
|
||||
pub unsafe fn setup_decoder_for_stream(
|
||||
&mut self,
|
||||
stream: *mut AVStream,
|
||||
options: Option<HashMap<String, String>>,
|
||||
) -> Result<&mut DecoderCodecContext, Error> {
|
||||
if stream.is_null() {
|
||||
anyhow::bail!("stream is null");
|
||||
}
|
||||
|
||||
let codec_par = (*stream).codecpar;
|
||||
assert_ne!(
|
||||
codec_par,
|
||||
ptr::null_mut(),
|
||||
"Codec parameters are missing from stream"
|
||||
);
|
||||
|
||||
if let std::collections::hash_map::Entry::Vacant(e) = self.codecs.entry((*stream).index) {
|
||||
let codec = avcodec_find_decoder((*codec_par).codec_id);
|
||||
if codec.is_null() {
|
||||
anyhow::bail!(
|
||||
"Failed to find codec: {}",
|
||||
CStr::from_ptr(avcodec_get_name((*codec_par).codec_id)).to_str()?
|
||||
)
|
||||
}
|
||||
let context = avcodec_alloc_context3(codec);
|
||||
if context.is_null() {
|
||||
anyhow::bail!("Failed to alloc context")
|
||||
}
|
||||
if avcodec_parameters_to_context(context, (*stream).codecpar) != 0 {
|
||||
anyhow::bail!("Failed to copy codec parameters to context")
|
||||
}
|
||||
let mut dict = if let Some(options) = options {
|
||||
options_to_dict(options)?
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
};
|
||||
|
||||
if avcodec_open2(context, codec, &mut dict) < 0 {
|
||||
anyhow::bail!("Failed to open codec")
|
||||
}
|
||||
Ok(e.insert(DecoderCodecContext { context, codec }))
|
||||
} else {
|
||||
anyhow::bail!("Decoder already setup");
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn decode_pkt(
|
||||
&mut self,
|
||||
pkt: *mut AVPacket,
|
||||
@ -54,34 +121,6 @@ impl Decoder {
|
||||
"Passed stream reference does not match stream_index of packet"
|
||||
);
|
||||
|
||||
let codec_par = (*stream).codecpar;
|
||||
assert_ne!(
|
||||
codec_par,
|
||||
ptr::null_mut(),
|
||||
"Codec parameters are missing from stream"
|
||||
);
|
||||
|
||||
if let std::collections::hash_map::Entry::Vacant(e) = self.codecs.entry(stream_index) {
|
||||
let codec = avcodec_find_decoder((*codec_par).codec_id);
|
||||
if codec.is_null() {
|
||||
return Err(Error::msg(format!(
|
||||
"Failed to find codec: {}",
|
||||
CStr::from_ptr(avcodec_get_name((*codec_par).codec_id)).to_str()?
|
||||
)));
|
||||
}
|
||||
let context = avcodec_alloc_context3(ptr::null());
|
||||
if context.is_null() {
|
||||
return Err(Error::msg("Failed to alloc context"));
|
||||
}
|
||||
if avcodec_parameters_to_context(context, (*stream).codecpar) != 0 {
|
||||
return Err(Error::msg("Failed to copy codec parameters to context"));
|
||||
}
|
||||
if avcodec_open2(context, codec, ptr::null_mut()) < 0 {
|
||||
return Err(Error::msg("Failed to open codec"));
|
||||
}
|
||||
e.insert(CodecContext { context, codec });
|
||||
}
|
||||
|
||||
if let Some(ctx) = self.codecs.get_mut(&stream_index) {
|
||||
// subtitles don't need decoding, create a frame from the pkt data
|
||||
if (*ctx.codec).type_ == AVMediaType::AVMEDIA_TYPE_SUBTITLE {
|
||||
|
172
src/demux.rs
172
src/demux.rs
@ -1,14 +1,11 @@
|
||||
use crate::return_ffmpeg_error;
|
||||
use crate::{get_ffmpeg_error_msg, DemuxerInfo, StreamChannelType, StreamInfoChannel};
|
||||
use anyhow::Error;
|
||||
use ffmpeg_sys_the_third::*;
|
||||
use std::ffi::CStr;
|
||||
use std::{ptr, slice};
|
||||
|
||||
use crate::get_ffmpeg_error_msg;
|
||||
use crate::return_ffmpeg_error;
|
||||
use slimbox::{slimbox_unsize, SlimBox, SlimMut};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
use std::mem::transmute;
|
||||
use std::{ptr, slice};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn read_data(
|
||||
@ -27,131 +24,9 @@ unsafe extern "C" fn read_data(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct DemuxerInfo {
|
||||
pub bitrate: usize,
|
||||
pub duration: f32,
|
||||
pub channels: Vec<StreamInfoChannel>,
|
||||
}
|
||||
|
||||
unsafe impl Send for DemuxerInfo {}
|
||||
unsafe impl Sync for DemuxerInfo {}
|
||||
|
||||
impl DemuxerInfo {
|
||||
pub fn best_stream(&self, t: StreamChannelType) -> Option<&StreamInfoChannel> {
|
||||
self.channels
|
||||
.iter()
|
||||
.filter(|a| a.channel_type == t)
|
||||
.reduce(|acc, channel| {
|
||||
if channel.best_metric() > acc.best_metric() {
|
||||
channel
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn best_video(&self) -> Option<&StreamInfoChannel> {
|
||||
self.best_stream(StreamChannelType::Video)
|
||||
}
|
||||
|
||||
pub fn best_audio(&self) -> Option<&StreamInfoChannel> {
|
||||
self.best_stream(StreamChannelType::Audio)
|
||||
}
|
||||
|
||||
pub fn best_subtitle(&self) -> Option<&StreamInfoChannel> {
|
||||
self.best_stream(StreamChannelType::Subtitle)
|
||||
}
|
||||
|
||||
pub unsafe fn is_best_stream(&self, stream: *mut AVStream) -> bool {
|
||||
match (*(*stream).codecpar).codec_type {
|
||||
AVMediaType::AVMEDIA_TYPE_VIDEO => {
|
||||
(*stream).index == self.best_video().map_or(usize::MAX, |r| r.index) as libc::c_int
|
||||
}
|
||||
AVMediaType::AVMEDIA_TYPE_AUDIO => {
|
||||
(*stream).index == self.best_audio().map_or(usize::MAX, |r| r.index) as libc::c_int
|
||||
}
|
||||
AVMediaType::AVMEDIA_TYPE_SUBTITLE => {
|
||||
(*stream).index
|
||||
== self.best_subtitle().map_or(usize::MAX, |r| r.index) as libc::c_int
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DemuxerInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Demuxer Info:")?;
|
||||
for c in &self.channels {
|
||||
write!(f, "\n{}", c)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum StreamChannelType {
|
||||
Video,
|
||||
Audio,
|
||||
Subtitle,
|
||||
}
|
||||
|
||||
impl Display for StreamChannelType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
StreamChannelType::Video => "video",
|
||||
StreamChannelType::Audio => "audio",
|
||||
StreamChannelType::Subtitle => "subtitle",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct StreamInfoChannel {
|
||||
pub index: usize,
|
||||
pub channel_type: StreamChannelType,
|
||||
pub codec: usize,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub fps: f32,
|
||||
pub sample_rate: usize,
|
||||
pub format: usize,
|
||||
}
|
||||
|
||||
impl StreamInfoChannel {
|
||||
pub fn best_metric(&self) -> f32 {
|
||||
match self.channel_type {
|
||||
StreamChannelType::Video => self.width as f32 * self.height as f32 * self.fps,
|
||||
StreamChannelType::Audio => self.sample_rate as f32,
|
||||
StreamChannelType::Subtitle => 999. - self.index as f32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StreamInfoChannel {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let codec_name = unsafe { CStr::from_ptr(avcodec_get_name(transmute(self.codec as i32))) };
|
||||
write!(
|
||||
f,
|
||||
"{} #{}: codec={},size={}x{},fps={}",
|
||||
self.channel_type,
|
||||
self.index,
|
||||
codec_name.to_str().unwrap(),
|
||||
self.width,
|
||||
self.height,
|
||||
self.fps
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DemuxerInput {
|
||||
Url(String),
|
||||
Reader(Option<SlimBox<dyn Read + 'static>>),
|
||||
Reader(Option<SlimBox<dyn Read + 'static>>, Option<String>),
|
||||
}
|
||||
|
||||
pub struct Demuxer {
|
||||
@ -160,28 +35,35 @@ pub struct Demuxer {
|
||||
}
|
||||
|
||||
impl Demuxer {
|
||||
/// Create a new [Demuxer] from a file path or url
|
||||
pub fn new(input: &str) -> Self {
|
||||
unsafe {
|
||||
let ps = avformat_alloc_context();
|
||||
let ctx = avformat_alloc_context();
|
||||
Self {
|
||||
ctx: ps,
|
||||
ctx,
|
||||
input: DemuxerInput::Url(input.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_custom_io<R: Read + 'static>(reader: R) -> Self {
|
||||
/// Create a new [Demuxer] from an object that implements [Read]
|
||||
pub fn new_custom_io<R: Read + 'static>(reader: R, url: Option<String>) -> Self {
|
||||
unsafe {
|
||||
let ps = avformat_alloc_context();
|
||||
(*ps).flags |= AVFMT_FLAG_CUSTOM_IO;
|
||||
let ctx = avformat_alloc_context();
|
||||
(*ctx).flags |= AVFMT_FLAG_CUSTOM_IO;
|
||||
|
||||
Self {
|
||||
ctx: ps,
|
||||
input: DemuxerInput::Reader(Some(slimbox_unsize!(reader))),
|
||||
ctx,
|
||||
input: DemuxerInput::Reader(Some(slimbox_unsize!(reader)), url),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set [AVFormatContext] options
|
||||
pub fn set_opt(&mut self, options: HashMap<String, String>) -> Result<(), Error> {
|
||||
crate::set_opts(self.ctx as *mut libc::c_void, options)
|
||||
}
|
||||
|
||||
unsafe fn open_input(&mut self) -> libc::c_int {
|
||||
match &mut self.input {
|
||||
DemuxerInput::Url(input) => avformat_open_input(
|
||||
@ -190,7 +72,7 @@ impl Demuxer {
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
),
|
||||
DemuxerInput::Reader(input) => {
|
||||
DemuxerInput::Reader(input, url) => {
|
||||
let input = input.take().expect("input stream already taken");
|
||||
const BUFFER_SIZE: usize = 4096;
|
||||
let pb = avio_alloc_context(
|
||||
@ -206,7 +88,11 @@ impl Demuxer {
|
||||
(*self.ctx).pb = pb;
|
||||
avformat_open_input(
|
||||
&mut self.ctx,
|
||||
ptr::null_mut(),
|
||||
if let Some(url) = url {
|
||||
format!("{}\0", url).as_ptr() as *const libc::c_char
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
},
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
@ -221,7 +107,6 @@ impl Demuxer {
|
||||
if avformat_find_stream_info(self.ctx, ptr::null_mut()) < 0 {
|
||||
return Err(Error::msg("Could not find stream info"));
|
||||
}
|
||||
//av_dump_format(self.ctx, 0, ptr::null_mut(), 0);
|
||||
|
||||
let mut channel_infos = vec![];
|
||||
|
||||
@ -230,6 +115,7 @@ impl Demuxer {
|
||||
match (*(*stream).codecpar).codec_type {
|
||||
AVMediaType::AVMEDIA_TYPE_VIDEO => {
|
||||
channel_infos.push(StreamInfoChannel {
|
||||
stream,
|
||||
index: (*stream).index as usize,
|
||||
codec: (*(*stream).codecpar).codec_id as usize,
|
||||
channel_type: StreamChannelType::Video,
|
||||
@ -242,6 +128,7 @@ impl Demuxer {
|
||||
}
|
||||
AVMediaType::AVMEDIA_TYPE_AUDIO => {
|
||||
channel_infos.push(StreamInfoChannel {
|
||||
stream,
|
||||
index: (*stream).index as usize,
|
||||
codec: (*(*stream).codecpar).codec_id as usize,
|
||||
channel_type: StreamChannelType::Audio,
|
||||
@ -254,6 +141,7 @@ impl Demuxer {
|
||||
}
|
||||
AVMediaType::AVMEDIA_TYPE_SUBTITLE => {
|
||||
channel_infos.push(StreamInfoChannel {
|
||||
stream,
|
||||
index: (*stream).index as usize,
|
||||
codec: (*(*stream).codecpar).codec_id as usize,
|
||||
channel_type: StreamChannelType::Subtitle,
|
||||
@ -296,8 +184,9 @@ impl Demuxer {
|
||||
|
||||
impl Drop for Demuxer {
|
||||
fn drop(&mut self) {
|
||||
if !self.ctx.is_null() {
|
||||
unsafe {
|
||||
if let DemuxerInput::Reader(_) = self.input {
|
||||
if let DemuxerInput::Reader(_, _) = self.input {
|
||||
drop(SlimBox::<dyn Read>::from_raw((*(*self.ctx).pb).opaque));
|
||||
}
|
||||
avformat_free_context(self.ctx);
|
||||
@ -305,3 +194,4 @@ impl Drop for Demuxer {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
60
src/lib.rs
60
src/lib.rs
@ -1,10 +1,17 @@
|
||||
use ffmpeg_sys_the_third::av_make_error_string;
|
||||
use anyhow::Error;
|
||||
use ffmpeg_sys_the_third::{
|
||||
av_dict_set, av_make_error_string, av_opt_next, av_opt_set, AVDictionary, AVOption,
|
||||
AV_OPT_SEARCH_CHILDREN,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
|
||||
mod decode;
|
||||
mod demux;
|
||||
mod resample;
|
||||
mod scale;
|
||||
mod stream_info;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! return_ffmpeg_error {
|
||||
@ -24,8 +31,59 @@ fn get_ffmpeg_error_msg(ret: libc::c_int) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn options_to_dict(options: HashMap<String, String>) -> Result<*mut AVDictionary, Error> {
|
||||
let mut dict = ptr::null_mut();
|
||||
for (key, value) in options {
|
||||
let ret = av_dict_set(
|
||||
&mut dict,
|
||||
format!("{}\0", key).as_ptr() as *const libc::c_char,
|
||||
format!("{}\0", value).as_ptr() as *const libc::c_char,
|
||||
0,
|
||||
);
|
||||
return_ffmpeg_error!(ret);
|
||||
}
|
||||
Ok(dict)
|
||||
}
|
||||
|
||||
fn list_opts(ctx: *mut libc::c_void) -> Result<Vec<String>, Error> {
|
||||
let mut opt_ptr: *const AVOption = ptr::null_mut();
|
||||
|
||||
let mut ret = vec![];
|
||||
unsafe {
|
||||
loop {
|
||||
opt_ptr = av_opt_next(ctx, opt_ptr);
|
||||
if opt_ptr.is_null() {
|
||||
break;
|
||||
}
|
||||
|
||||
ret.push(
|
||||
CStr::from_ptr((*opt_ptr).name)
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn set_opts(ctx: *mut libc::c_void, options: HashMap<String, String>) -> Result<(), Error> {
|
||||
unsafe {
|
||||
for (key, value) in options {
|
||||
let ret = av_opt_set(
|
||||
ctx,
|
||||
format!("{}\0", key).as_ptr() as *const libc::c_char,
|
||||
format!("{}\0", value).as_ptr() as *const libc::c_char,
|
||||
AV_OPT_SEARCH_CHILDREN,
|
||||
);
|
||||
return_ffmpeg_error!(ret);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub use decode::*;
|
||||
pub use demux::*;
|
||||
pub use ffmpeg_sys_the_third;
|
||||
pub use resample::*;
|
||||
pub use scale::*;
|
||||
pub use stream_info::*;
|
||||
|
129
src/stream_info.rs
Normal file
129
src/stream_info.rs
Normal file
@ -0,0 +1,129 @@
|
||||
use ffmpeg_sys_the_third::{avcodec_get_name, AVMediaType, AVStream};
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::intrinsics::transmute;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct DemuxerInfo {
|
||||
pub bitrate: usize,
|
||||
pub duration: f32,
|
||||
pub channels: Vec<StreamInfoChannel>,
|
||||
}
|
||||
|
||||
impl DemuxerInfo {
|
||||
pub fn best_stream(&self, t: StreamChannelType) -> Option<&StreamInfoChannel> {
|
||||
self.channels
|
||||
.iter()
|
||||
.filter(|a| a.channel_type == t)
|
||||
.reduce(|acc, channel| {
|
||||
if channel.best_metric() > acc.best_metric() {
|
||||
channel
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn best_video(&self) -> Option<&StreamInfoChannel> {
|
||||
self.best_stream(StreamChannelType::Video)
|
||||
}
|
||||
|
||||
pub fn best_audio(&self) -> Option<&StreamInfoChannel> {
|
||||
self.best_stream(StreamChannelType::Audio)
|
||||
}
|
||||
|
||||
pub fn best_subtitle(&self) -> Option<&StreamInfoChannel> {
|
||||
self.best_stream(StreamChannelType::Subtitle)
|
||||
}
|
||||
|
||||
pub unsafe fn is_best_stream(&self, stream: *mut AVStream) -> bool {
|
||||
match (*(*stream).codecpar).codec_type {
|
||||
AVMediaType::AVMEDIA_TYPE_VIDEO => {
|
||||
(*stream).index == self.best_video().map_or(usize::MAX, |r| r.index) as libc::c_int
|
||||
}
|
||||
AVMediaType::AVMEDIA_TYPE_AUDIO => {
|
||||
(*stream).index == self.best_audio().map_or(usize::MAX, |r| r.index) as libc::c_int
|
||||
}
|
||||
AVMediaType::AVMEDIA_TYPE_SUBTITLE => {
|
||||
(*stream).index
|
||||
== self.best_subtitle().map_or(usize::MAX, |r| r.index) as libc::c_int
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DemuxerInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Demuxer Info:")?;
|
||||
for c in &self.channels {
|
||||
write!(f, "\n{}", c)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum StreamChannelType {
|
||||
Video,
|
||||
Audio,
|
||||
Subtitle,
|
||||
}
|
||||
|
||||
impl Display for StreamChannelType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
StreamChannelType::Video => "video",
|
||||
StreamChannelType::Audio => "audio",
|
||||
StreamChannelType::Subtitle => "subtitle",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct StreamInfoChannel {
|
||||
pub index: usize,
|
||||
pub channel_type: StreamChannelType,
|
||||
pub codec: usize,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub fps: f32,
|
||||
pub sample_rate: usize,
|
||||
pub format: usize,
|
||||
|
||||
// private stream pointer
|
||||
pub(crate) stream: *mut AVStream,
|
||||
}
|
||||
|
||||
unsafe impl Send for StreamInfoChannel {}
|
||||
unsafe impl Sync for StreamInfoChannel {}
|
||||
|
||||
impl StreamInfoChannel {
|
||||
pub fn best_metric(&self) -> f32 {
|
||||
match self.channel_type {
|
||||
StreamChannelType::Video => self.width as f32 * self.height as f32 * self.fps,
|
||||
StreamChannelType::Audio => self.sample_rate as f32,
|
||||
StreamChannelType::Subtitle => 999. - self.index as f32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StreamInfoChannel {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let codec_name = unsafe { CStr::from_ptr(avcodec_get_name(transmute(self.codec as i32))) };
|
||||
write!(
|
||||
f,
|
||||
"{} #{}: codec={},size={}x{},fps={}",
|
||||
self.channel_type,
|
||||
self.index,
|
||||
codec_name.to_str().unwrap(),
|
||||
self.width,
|
||||
self.height,
|
||||
self.fps
|
||||
)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user