├── .gitattributes ├── .gitignore ├── MathMF Demo.nb ├── MathMF ├── Kernel │ └── init.m ├── MathMF.cpp └── MathMF.m ├── optional pre-built v10 DLL └── MathMF.dll ├── optional pre-built v9 DLL └── MathMF.dll └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /MathMF Demo.nb: -------------------------------------------------------------------------------- 1 | (* Content-type: application/vnd.wolfram.mathematica *) 2 | 3 | (*** Wolfram Notebook File ***) 4 | (* http://www.wolfram.com/nb *) 5 | 6 | (* CreatedBy='Mathematica 9.0' *) 7 | 8 | (*CacheID: 234*) 9 | (* Internal cache information: 10 | NotebookFileLineBreakTest 11 | NotebookFileLineBreakTest 12 | NotebookDataPosition[ 157, 7] 13 | NotebookDataLength[ 35759, 839] 14 | NotebookOptionsPosition[ 31986, 715] 15 | NotebookOutlinePosition[ 33020, 745] 16 | CellTagsIndexPosition[ 32977, 742] 17 | WindowFrame->Normal*) 18 | 19 | (* Beginning of Notebook Content *) 20 | Notebook[{ 21 | 22 | Cell[CellGroupData[{ 23 | Cell["MathMF", "Title", 24 | CellChangeTimes->{{3.6074479170467224`*^9, 3.607447925501937*^9}, { 25 | 3.607448104511052*^9, 3.6074481046826525`*^9}}], 26 | 27 | Cell["\<\ 28 | A package for video import and export using Windows Media Foundation. 29 | Simon Woods 30 | April 2014\ 31 | \>", "Subtitle", 32 | CellChangeTimes->{{3.6074480599261737`*^9, 3.607448144384722*^9}, { 33 | 3.607448257038062*^9, 3.6074482862101135`*^9}, {3.6076756939008465`*^9, 34 | 3.6076756986808467`*^9}, {3.607675833271287*^9, 3.6076758563317485`*^9}}], 35 | 36 | Cell[TextData[{ 37 | "This was developed for ", 38 | StyleBox["Mathematica", 39 | FontSlant->"Italic"], 40 | " users on Windows who do not have Quicktime installed, because the built-in \ 41 | video import is limited to uncompressed AVI in those circumstances. The \ 42 | frame-by-frame export functionality may be useful even for users who ", 43 | StyleBox["do", 44 | FontWeight->"Bold"], 45 | " have Quicktime, as it does not require the entire image list to be created \ 46 | before export." 47 | }], "Subsubtitle", 48 | CellChangeTimes->{{3.6076757188208466`*^9, 3.6076757712408466`*^9}, 49 | 3.607675816110944*^9, {3.6076759313832493`*^9, 3.6076761004866314`*^9}, { 50 | 3.607678305338441*^9, 3.6076783217287693`*^9}, {3.6076785661019773`*^9, 51 | 3.6076785969725943`*^9}, {3.6076786540937366`*^9, 52 | 3.6076786845643463`*^9}, {3.6076787682460203`*^9, 3.607678768786031*^9}}], 53 | 54 | Cell[TextData[{ 55 | StyleBox["WARNING!", 56 | FontColor->RGBColor[1, 0, 0]], 57 | "\nThe package communicates with the Media Foundation platform via a \ 58 | LibraryLink DLL. This DLL is loaded into the ", 59 | StyleBox["Mathematica", 60 | FontSlant->"Italic"], 61 | " kernel process, so if the DLL crashes so will the kernel. While I have \ 62 | attempted to make the package handle errors safely, you should be aware that \ 63 | I am not a professional programmer and any sufficiently determined and \ 64 | creative user will be able to make it crash. I accept no responsibility if \ 65 | you lose a three week computation because you found a way to send complex \ 66 | numbers to the media sink writer :-)" 67 | }], "Subsubtitle", 68 | CellChangeTimes->{{3.6076757188208466`*^9, 3.6076757712408466`*^9}, 69 | 3.607675816110944*^9, {3.6076759313832493`*^9, 3.6076760992266064`*^9}, { 70 | 3.6076761375973735`*^9, 3.607676141107444*^9}, {3.6076761907184362`*^9, 71 | 3.607676192058463*^9}, {3.607676233179285*^9, 3.6076762340793033`*^9}, { 72 | 3.6076763440715027`*^9, 3.607676391572453*^9}, {3.6076769197713766`*^9, 73 | 3.6076769223796377`*^9}, {3.607676987465145*^9, 3.6076770079501934`*^9}, { 74 | 3.607677611127198*^9, 3.607677611997207*^9}, {3.6076777141482286`*^9, 75 | 3.6076778588996763`*^9}, 3.6076779022103786`*^9, {3.6076779582415*^9, 76 | 3.6076779792819204`*^9}, {3.6076780178426914`*^9, 77 | 3.6076780676136866`*^9}, {3.607678298908313*^9, 3.6076783033884025`*^9}, { 78 | 3.607699017385714*^9, 3.607699017942746*^9}}], 79 | 80 | Cell[CellGroupData[{ 81 | 82 | Cell["Load the package", "Section", 83 | CellChangeTimes->{{3.6076758042608466`*^9, 3.6076758063608465`*^9}}], 84 | 85 | Cell["\<\ 86 | When the package loads it will try to find the MathMF.DLL and if not present \ 87 | it will attempt to compile it from source. 88 | If this step fails you may need to use the pre-built DLL (see the readme).\ 89 | \>", "Text", 90 | CellChangeTimes->{{3.6076174643286695`*^9, 3.6076175218927703`*^9}, { 91 | 3.607679233625327*^9, 3.6076792755261655`*^9}, {3.607679306666788*^9, 92 | 3.607679308406823*^9}}], 93 | 94 | Cell[CellGroupData[{ 95 | 96 | Cell[BoxData[ 97 | RowBox[{"Needs", "[", "\"\\"", "]"}]], "Input", 98 | CellChangeTimes->{{3.6076150950495386`*^9, 3.607615100724863*^9}, { 99 | 3.6076705704917326`*^9, 3.6076705724919324`*^9}, {3.6076707517718587`*^9, 100 | 3.607670751956877*^9}}], 101 | 102 | Cell[CellGroupData[{ 103 | 104 | Cell[BoxData["\<\"No MathMF.DLL found.\\nMathMF will attempt to build the \ 105 | library from source.\"\>"], "Print", 106 | CellChangeTimes->{3.607698444333123*^9}], 107 | 108 | Cell[BoxData["\<\"MathMF.DLL successfully created at \ 109 | C:\\\\Users\\\\Simon\\\\AppData\\\\Roaming\\\\Mathematica\\\\SystemFiles\\\\\ 110 | LibraryResources\\\\Windows-x86-64\\\\MathMF.dll\"\>"], "Print", 111 | CellChangeTimes->{3.607698446392327*^9}] 112 | }, Open ]] 113 | }, Open ]] 114 | }, Open ]], 115 | 116 | Cell[CellGroupData[{ 117 | 118 | Cell["Importing", "Section", 119 | CellChangeTimes->{{3.607412095140934*^9, 3.6074120955309343`*^9}, { 120 | 3.607428835592741*^9, 3.6074288364127426`*^9}, {3.6074480482261534`*^9, 121 | 3.607448048865754*^9}, {3.607448125664689*^9, 3.6074481292682953`*^9}, { 122 | 3.607448176832779*^9, 3.607448198906818*^9}, {3.6074482441992397`*^9, 123 | 3.607448244885641*^9}, {3.6074483321833944`*^9, 3.6074483323705945`*^9}, { 124 | 3.607680302472842*^9, 3.607680303392861*^9}}], 125 | 126 | Cell[TextData[{ 127 | "The package should be able to import frames from any video file which Media \ 128 | Foundation can read. Details on the container formats and video codecs \ 129 | supported by Media Foundation can be found ", 130 | ButtonBox["here", 131 | BaseStyle->"Hyperlink", 132 | ButtonData->{ 133 | URL["http://msdn.microsoft.com/en-us/library/windows/desktop/dd757927(v=\ 134 | vs.85).aspx"], None}, 135 | ButtonNote-> 136 | "http://msdn.microsoft.com/en-us/library/windows/desktop/dd757927(v=vs.85).\ 137 | aspx"], 138 | ".\n\nImporting consists of two steps:\n1. Call ", 139 | StyleBox["MFInitSourceReader", "Code"], 140 | " to initialise the source reader.\n2. Call ", 141 | StyleBox["MFGrabFrame", "Code"], 142 | " to grab frames one at a time." 143 | }], "Text", 144 | CellChangeTimes->{{3.607679328807231*^9, 3.607679329547246*^9}, { 145 | 3.6076793888084307`*^9, 3.607679403718729*^9}, {3.6076794872503996`*^9, 146 | 3.6076795475216055`*^9}, {3.607679592098635*^9, 3.607679630939412*^9}, { 147 | 3.607679680900411*^9, 3.6076797578219495`*^9}, {3.6076817953726997`*^9, 148 | 3.6076817956627054`*^9}}], 149 | 150 | Cell[CellGroupData[{ 151 | 152 | Cell["MFInitSourceReader", "Subsection", 153 | CellChangeTimes->{{3.6076801960907145`*^9, 3.6076802001307955`*^9}}], 154 | 155 | Cell[TextData[{ 156 | StyleBox["MFInitSourceReader", "Code"], 157 | " takes one argument which is the path to the video file. If successful it \ 158 | returns a list:\n{ duration of the video in seconds, average frame rate in \ 159 | frames per second,\n frame width in pixels, frame height in pixels }" 160 | }], "Text", 161 | CellChangeTimes->{{3.607448156693144*^9, 3.6074481709983687`*^9}, { 162 | 3.607448228318412*^9, 3.6074482372260275`*^9}, {3.6074482936201267`*^9, 163 | 3.6074483523074293`*^9}, {3.607448493300477*^9, 3.6074484963268824`*^9}, 164 | 3.6076793258771725`*^9, 3.607679733491463*^9, {3.607679776282319*^9, 165 | 3.607679827733348*^9}, {3.6076799553659005`*^9, 3.607680116599125*^9}, { 166 | 3.607680226481323*^9, 3.6076802660321136`*^9}}] 167 | }, Open ]], 168 | 169 | Cell[CellGroupData[{ 170 | 171 | Cell["MFGrabFrame", "Subsection", 172 | CellChangeTimes->{{3.607680235511503*^9, 3.607680238281559*^9}}], 173 | 174 | Cell[TextData[{ 175 | StyleBox["MFGrabFrame", "Code"], 176 | " grabs the next frame from the input stream and returns it to ", 177 | StyleBox["Mathematica", 178 | FontSlant->"Italic"], 179 | ".\n\n", 180 | StyleBox["MFGrabFrame[\[OpenCurlyDoubleQuote]Byte\[CloseCurlyDoubleQuote]]", 181 | "Code"], 182 | " returns the data as an array of \[OpenCurlyDoubleQuote]bytes\ 183 | \[CloseCurlyDoubleQuote] (actually Integers in the range 0 to 255)\n", 184 | StyleBox["MFGrabFrame[\[OpenCurlyDoubleQuote]Real\[CloseCurlyDoubleQuote]]", 185 | "Code"], 186 | " returns the data as an array of Reals in the range 0 to 1\n", 187 | StyleBox["MFGrabFrame[\[OpenCurlyDoubleQuote]ByteImage\ 188 | \[CloseCurlyDoubleQuote]]", "Code"], 189 | " returns the data as a byte Image\n", 190 | StyleBox["MFGrabFrame[\[OpenCurlyDoubleQuote]RealImage\ 191 | \[CloseCurlyDoubleQuote]]", "Code"], 192 | " returns the data as a real Image" 193 | }], "Text", 194 | CellChangeTimes->{{3.6076805282973585`*^9, 3.607680616549124*^9}, { 195 | 3.607680799602785*^9, 3.6076808004828024`*^9}, {3.6076808477437477`*^9, 196 | 3.6076808861445155`*^9}}], 197 | 198 | Cell[TextData[{ 199 | "After the final frame has been read, subsequent calls will return ", 200 | StyleBox["EndOfFile", "Code"], 201 | ". \n\nNote: To import all the frames from a video file, call ", 202 | StyleBox["MFGrabFrame", "Code"], 203 | " in a loop until ", 204 | StyleBox["EndOfFile", "Code"], 205 | " is returned. Do not rely on computing the total number of frames from the \ 206 | frame rate and the duration. The frame rate returned by ", 207 | StyleBox["MFInitSourceReader", "Code"], 208 | " is an approximate average, and the number of frames calculated by ", 209 | StyleBox["duration/framerate", "Code"], 210 | " is not always accurate (and frequently is not an integer)" 211 | }], "Text", 212 | CellChangeTimes->{{3.6074485018960924`*^9, 3.6074485530641823`*^9}, { 213 | 3.607448617242695*^9, 3.6074486917640257`*^9}, {3.607500752240472*^9, 214 | 3.6075008889312897`*^9}, {3.607500924197307*^9, 3.607500927284484*^9}, { 215 | 3.6075012267696133`*^9, 3.607501384833654*^9}, 3.607679740141596*^9, 216 | 3.6076806165291233`*^9, {3.607680887064534*^9, 3.6076808953346996`*^9}, { 217 | 3.607680993256658*^9, 3.607680994266678*^9}, {3.607681061028013*^9, 218 | 3.607681087938552*^9}, {3.607681145419701*^9, 3.6076811886805663`*^9}, { 219 | 3.607681226271318*^9, 3.6076812276713457`*^9}, {3.6076812612220173`*^9, 220 | 3.607681413425061*^9}}] 221 | }, Open ]], 222 | 223 | Cell[CellGroupData[{ 224 | 225 | Cell["\<\ 226 | Example: 227 | Read frames from the input file and display them using Monitor\ 228 | \>", "Subsection", 229 | CellChangeTimes->{{3.607680326113315*^9, 3.6076803290933747`*^9}, { 230 | 3.6076987853834443`*^9, 3.607698804610544*^9}}], 231 | 232 | Cell[TextData[StyleBox["Reminder: change the file path to the location of a \ 233 | movie file on your PC", 234 | FontColor->RGBColor[1, 0, 0]]], "Text", 235 | CellChangeTimes->{{3.6076815501477957`*^9, 3.6076815855885043`*^9}, { 236 | 3.607698612163537*^9, 3.6076986565790772`*^9}, {3.6076987538706417`*^9, 237 | 3.607698796814098*^9}}], 238 | 239 | Cell[CellGroupData[{ 240 | 241 | Cell[BoxData[ 242 | RowBox[{ 243 | RowBox[{"{", 244 | RowBox[{"duration", ",", "framerate", ",", "width", ",", "height"}], "}"}], 245 | "=", 246 | RowBox[{ 247 | "MFInitSourceReader", "[", 248 | "\"\\"", 249 | "]"}]}]], "Input", 250 | CellChangeTimes->{{3.6073428494735847`*^9, 3.6073428562536526`*^9}, { 251 | 3.6074121005009413`*^9, 3.6074121058269944`*^9}, {3.60741581254827*^9, 252 | 3.607415862073103*^9}, {3.6074176847443542`*^9, 3.6074176880265417`*^9}, { 253 | 3.607428840232748*^9, 3.607428844142753*^9}, {3.6074394758628225`*^9, 254 | 3.607439483366435*^9}, {3.6074442774887557`*^9, 3.6074442820595636`*^9}, { 255 | 3.607616732361743*^9, 3.607616746105367*^9}, 3.607670766657347*^9, 256 | 3.6076708037810593`*^9, 3.6076798488737707`*^9, 3.607680310202997*^9, { 257 | 3.6076804990267735`*^9, 3.6076805036668663`*^9}, {3.607698497232963*^9, 258 | 3.6076985033593135`*^9}}], 259 | 260 | Cell[BoxData[ 261 | RowBox[{"{", 262 | RowBox[{"30.093`", ",", "29.97000002997`", ",", "1280", ",", "720"}], 263 | "}"}]], "Output", 264 | CellChangeTimes->{ 265 | 3.6073428568136578`*^9, 3.6074120341208487`*^9, 3.607412107216996*^9, 266 | 3.607412406051529*^9, 3.6074125091186886`*^9, 3.6074125940048532`*^9, { 267 | 3.6074127010740566`*^9, 3.6074127112140703`*^9}, 3.6074128016342115`*^9, 268 | 3.6074132839305196`*^9, 3.607415172503662*^9, 3.607415245267824*^9, 269 | 3.607415305854289*^9, 3.6074154992083483`*^9, 3.6074156409524555`*^9, 270 | 3.607415814135361*^9, 3.6074158643352327`*^9, {3.60741768938962*^9, 271 | 3.6074177049755116`*^9}, 3.6074185728979583`*^9, 3.6074288478527584`*^9, 272 | 3.607439485051238*^9, 3.607444559365651*^9, 3.607448430759967*^9, 273 | 3.607450967680019*^9, 3.6075009930502453`*^9, {3.607501178684863*^9, 274 | 3.6075012031632633`*^9}, 3.6075028209532127`*^9, 3.6075984883942432`*^9, 275 | 3.607599386302625*^9, 3.6076159654487963`*^9, 3.6076167470413685`*^9, 276 | 3.6076708053742185`*^9, 3.607679849193777*^9, 3.6076803347034874`*^9, { 277 | 3.607680471546224*^9, 3.60768050435688*^9}, 3.607698506604499*^9}] 278 | }, Open ]], 279 | 280 | Cell[BoxData[ 281 | RowBox[{ 282 | RowBox[{"While", "[", 283 | RowBox[{ 284 | RowBox[{"(", 285 | RowBox[{"image", "=", 286 | RowBox[{"MFGrabFrame", "[", "\"\\"", "]"}]}], ")"}], "=!=", 287 | "EndOfFile"}], "]"}], "~", "Monitor", "~", "image"}]], "Input", 288 | CellChangeTimes->{{3.607448382197082*^9, 3.6074484571396136`*^9}, { 289 | 3.6075009380541*^9, 3.6075009520729017`*^9}, {3.6075027910479603`*^9, 290 | 3.6075027942147655`*^9}}] 291 | }, Open ]] 292 | }, Open ]], 293 | 294 | Cell[CellGroupData[{ 295 | 296 | Cell["Exporting", "Section", 297 | CellChangeTimes->{{3.607330252310952*^9, 3.607330252660987*^9}, { 298 | 3.607427474906537*^9, 3.607427475526538*^9}, {3.607448696334834*^9, 299 | 3.6074486978636365`*^9}, 3.6074493518179817`*^9, {3.607681635839509*^9, 300 | 3.607681637479542*^9}}], 301 | 302 | Cell[TextData[{ 303 | "Currently only two export formats are supported:\n.wmv files containing a \ 304 | WMV3 stream\n.mp4 files containing a H264 stream\n\nExporting consists of \ 305 | three steps:\n1. Call ", 306 | StyleBox["MFInitSinkWriter", "Code"], 307 | " to initialise the sink writer.\n2. Call ", 308 | StyleBox["MFSendFrame", "Code"], 309 | " to send frames one at a time to the output stream.\n3. Call ", 310 | StyleBox["MFFinaliseSink", "Code"], 311 | " to finish writing and close the file." 312 | }], "Text", 313 | CellChangeTimes->{{3.607448700968042*^9, 3.6074487933670044`*^9}, { 314 | 3.607448827484264*^9, 3.6074488824899607`*^9}, {3.6074489271996393`*^9, 315 | 3.607449124524386*^9}, {3.6074491949429097`*^9, 3.607449293191882*^9}, { 316 | 3.607499429098792*^9, 3.6074996722476997`*^9}, {3.607499705187584*^9, 317 | 3.6074997390115185`*^9}, {3.607503367886692*^9, 3.607503368682294*^9}, { 318 | 3.6076816730002527`*^9, 3.6076818210132127`*^9}}], 319 | 320 | Cell[CellGroupData[{ 321 | 322 | Cell["MFInitSinkWriter", "Subsection", 323 | CellChangeTimes->{{3.6076818223532395`*^9, 3.607681832543443*^9}}], 324 | 325 | Cell[TextData[{ 326 | StyleBox["MFInitSinkWriter[filename, width, height, options]", "Code"], 327 | " opens the named file for writing video frames of dimensions \ 328 | width\[Times]height.\n\nThe available options are ", 329 | StyleBox["\[OpenCurlyDoubleQuote]FrameRate\[CloseCurlyDoubleQuote]", "Code"], 330 | ", ", 331 | StyleBox["\[OpenCurlyDoubleQuote]CompressionRatio\[CloseCurlyDoubleQuote]", 332 | "Code"], 333 | " and ", 334 | StyleBox["\[OpenCurlyDoubleQuote]BitRate\[CloseCurlyDoubleQuote]", "Code"], 335 | ":\n", 336 | StyleBox["\[OpenCurlyDoubleQuote]FrameRate\[CloseCurlyDoubleQuote]", "Code"], 337 | " is in frames per second and defaults to 29.97.\n", 338 | StyleBox["\[OpenCurlyDoubleQuote]CompressionRatio\[CloseCurlyDoubleQuote]", 339 | "Code"], 340 | " is defined as the bitrate of the uncompressed RGB24 stream divided by the \ 341 | bitrate of the compressed stream. It defaults to 100 which should usually \ 342 | give good quality video. Smaller compression ratio values will lead to larger \ 343 | files of higher quality.\n", 344 | StyleBox["\[OpenCurlyDoubleQuote]BitRate\[CloseCurlyDoubleQuote]", "Code"], 345 | " is in kb/s and defaults to ", 346 | StyleBox["Automatic", "Code"], 347 | ", meaning the bit rate will be computed from the compression ratio. If a \ 348 | number is supplied as the bit rate the ", 349 | StyleBox["\[OpenCurlyDoubleQuote]CompressionRatio\[CloseCurlyDoubleQuote]", 350 | "Code"], 351 | " is ignored.\n\nIn general it is recommended to use ", 352 | StyleBox["\[OpenCurlyDoubleQuote]CompressionRatio\[CloseCurlyDoubleQuote]", 353 | "Code"], 354 | " to determine the quality of the output video, unless a specific bit rate \ 355 | is required." 356 | }], "Text", 357 | CellChangeTimes->{{3.6076819009048104`*^9, 3.607681924315279*^9}, { 358 | 3.6076819568959303`*^9, 3.6076823795743837`*^9}, {3.6076824863765197`*^9, 359 | 3.607682611649025*^9}}] 360 | }, Open ]], 361 | 362 | Cell[CellGroupData[{ 363 | 364 | Cell["MFSendFrame", "Subsection", 365 | CellChangeTimes->{{3.6076818264133205`*^9, 3.607681832543443*^9}}], 366 | 367 | Cell[TextData[{ 368 | StyleBox["MFSendFrame[data]", "Code"], 369 | " sends ", 370 | StyleBox["data", "Code"], 371 | " to the output stream. The data may be a numerical array or Image of the \ 372 | correct height and width, and can be in real or byte format. The pixel data \ 373 | may be single channel (greyscale) or three channel (RGB)." 374 | }], "Text", 375 | CellChangeTimes->{{3.6076826430196524`*^9, 3.607682711481022*^9}, { 376 | 3.6076834777641153`*^9, 3.607683534033553*^9}, {3.607683575393139*^9, 377 | 3.607683614502748*^9}}] 378 | }, Open ]], 379 | 380 | Cell[CellGroupData[{ 381 | 382 | Cell["MFFinaliseSink", "Subsection", 383 | CellChangeTimes->{{3.607681829243377*^9, 3.607681832543443*^9}}], 384 | 385 | Cell[TextData[{ 386 | StyleBox["MFFinaliseSink[]", "Code"], 387 | " finalises the writing process and closes the output file." 388 | }], "Text", 389 | CellChangeTimes->{{3.607683636202531*^9, 3.607683650772385*^9}}] 390 | }, Open ]], 391 | 392 | Cell[CellGroupData[{ 393 | 394 | Cell["\<\ 395 | Example: 396 | Write 100 frames to a WMV file with a frame rate of 25 fps\ 397 | \>", "Subsection", 398 | CellChangeTimes->{{3.6076818813044186`*^9, 3.6076818881645555`*^9}, { 399 | 3.607698818606344*^9, 3.6076988195513983`*^9}}], 400 | 401 | Cell[TextData[StyleBox["Reminder: change the file path to somewhere suitable \ 402 | to create a movie file on your PC", 403 | FontColor->RGBColor[1, 0, 0]]], "Text", 404 | CellChangeTimes->{{3.60768417689133*^9, 3.607684194751687*^9}, { 405 | 3.6076986662386293`*^9, 3.607698684818692*^9}, {3.6076987346705437`*^9, 406 | 3.607698748294323*^9}, {3.607698816863245*^9, 3.607698817690292*^9}}], 407 | 408 | Cell[BoxData[{ 409 | RowBox[{ 410 | RowBox[{ 411 | RowBox[{"{", 412 | RowBox[{"w", ",", "h"}], "}"}], "=", 413 | RowBox[{"{", 414 | RowBox[{"300", ",", "200"}], "}"}]}], ";"}], "\[IndentingNewLine]", 415 | RowBox[{ 416 | RowBox[{"frame", "[", "i_", "]"}], ":=", 417 | RowBox[{"Rasterize", "[", 418 | RowBox[{"i", ",", "\"\\"", ",", 419 | RowBox[{"ImageSize", "\[Rule]", 420 | RowBox[{"{", 421 | RowBox[{"w", ",", "h"}], "}"}]}]}], "]"}]}]}], "Input", 422 | CellChangeTimes->{{3.607330589344652*^9, 3.6073306263993573`*^9}, { 423 | 3.6074189475109825`*^9, 3.6074189511909876`*^9}, {3.6074190861511765`*^9, 424 | 3.6074190907211833`*^9}, {3.607419136121247*^9, 3.6074191673837905`*^9}, { 425 | 3.607420529692452*^9, 3.607420543182471*^9}, 3.6074240635385094`*^9, { 426 | 3.6075029212457886`*^9, 3.607502928749402*^9}}], 427 | 428 | Cell[BoxData[ 429 | RowBox[{"MFInitSinkWriter", "[", 430 | RowBox[{ 431 | "\"\\"", ",", "w", ",", "h", 432 | ",", 433 | RowBox[{"\"\\"", "\[Rule]", "25"}]}], "]"}]], "Input", 434 | CellChangeTimes->{{3.6073302607827992`*^9, 3.6073303022239428`*^9}, { 435 | 3.607330602116929*^9, 3.607330603264044*^9}, {3.6074187859282565`*^9, 436 | 3.607418842758336*^9}, {3.607419216433859*^9, 3.60741921689386*^9}, 437 | 3.6074204938099017`*^9, 3.607420537742463*^9, {3.607423594431678*^9, 438 | 3.607423618697066*^9}, {3.607424066469677*^9, 3.6074240666626883`*^9}, { 439 | 3.6074242345892925`*^9, 3.6074242369994307`*^9}, {3.6074244168837194`*^9, 440 | 3.6074244202029095`*^9}, {3.607427489706558*^9, 3.6074275065365815`*^9}, { 441 | 3.607428128446751*^9, 3.6074281357167616`*^9}, {3.6074301987762003`*^9, 442 | 3.6074301990862007`*^9}, {3.6074394099731092`*^9, 3.607439415230318*^9}, 443 | 3.6074397676777315`*^9, 3.60743981221581*^9, 3.607444991487418*^9, { 444 | 3.6074491369888077`*^9, 3.6074491433536186`*^9}, {3.607499413075876*^9, 445 | 3.6074994166310797`*^9}, {3.607502901464954*^9, 3.607502901589754*^9}, { 446 | 3.6075029372514167`*^9, 3.6075029420406256`*^9}, {3.6075032558248944`*^9, 447 | 3.6075033274202213`*^9}, {3.607670823485029*^9, 3.607670824374118*^9}, { 448 | 3.607683978657365*^9, 3.6076839788473687`*^9}, {3.6076841337004657`*^9, 449 | 3.6076841399405904`*^9}, {3.607684490077593*^9, 3.6076844954477005`*^9}, { 450 | 3.607698519843256*^9, 3.6076985493219423`*^9}}], 451 | 452 | Cell[BoxData[ 453 | RowBox[{ 454 | RowBox[{"Do", "[", 455 | RowBox[{ 456 | RowBox[{"MFSendFrame", "@", 457 | RowBox[{"frame", "[", "i", "]"}]}], ",", 458 | RowBox[{"{", 459 | RowBox[{"i", ",", "1", ",", "100"}], "}"}]}], "]"}], "~", "Monitor", "~", 460 | "i"}]], "Input", 461 | CellChangeTimes->{{3.6073302607827992`*^9, 3.6073303022239428`*^9}, { 462 | 3.607330602116929*^9, 3.607330603264044*^9}, {3.6074187859282565`*^9, 463 | 3.607418842758336*^9}, {3.607419216433859*^9, 3.60741921689386*^9}, 464 | 3.6074204938099017`*^9, 3.607420537742463*^9, {3.607423594431678*^9, 465 | 3.607423618697066*^9}, {3.607424066469677*^9, 3.6074240666626883`*^9}, { 466 | 3.6074242345892925`*^9, 3.6074242369994307`*^9}, {3.6074244168837194`*^9, 467 | 3.6074244202029095`*^9}, {3.607427489706558*^9, 3.6074275065365815`*^9}, { 468 | 3.607428128446751*^9, 3.6074281357167616`*^9}, {3.6074301987762003`*^9, 469 | 3.6074301990862007`*^9}, {3.6074394099731092`*^9, 3.607439415230318*^9}, 470 | 3.6074397676777315`*^9, 3.60743981221581*^9, 3.607444991487418*^9, { 471 | 3.6074491369888077`*^9, 3.6074491433536186`*^9}, {3.607499413075876*^9, 472 | 3.6074994166310797`*^9}, {3.607502901464954*^9, 3.607502901589754*^9}, { 473 | 3.6075029372514167`*^9, 3.6075029420406256`*^9}, {3.6075032558248944`*^9, 474 | 3.6075033274202213`*^9}, {3.607670823485029*^9, 3.607670824374118*^9}, { 475 | 3.607683978657365*^9, 3.6076839788473687`*^9}, {3.6076841337004657`*^9, 476 | 3.607684139290578*^9}}], 477 | 478 | Cell[BoxData[ 479 | RowBox[{"MFFinaliseSink", "[", "]"}]], "Input", 480 | CellChangeTimes->{{3.6073302607827992`*^9, 3.6073303022239428`*^9}, { 481 | 3.607330602116929*^9, 3.607330603264044*^9}, {3.6074187859282565`*^9, 482 | 3.607418842758336*^9}, {3.607419216433859*^9, 3.60741921689386*^9}, 483 | 3.6074204938099017`*^9, 3.607420537742463*^9, {3.607423594431678*^9, 484 | 3.607423618697066*^9}, {3.607424066469677*^9, 3.6074240666626883`*^9}, { 485 | 3.6074242345892925`*^9, 3.6074242369994307`*^9}, {3.6074244168837194`*^9, 486 | 3.6074244202029095`*^9}, {3.607427489706558*^9, 3.6074275065365815`*^9}, { 487 | 3.607428128446751*^9, 3.6074281357167616`*^9}, {3.6074301987762003`*^9, 488 | 3.6074301990862007`*^9}, {3.6074394099731092`*^9, 3.607439415230318*^9}, 489 | 3.6074397676777315`*^9, 3.60743981221581*^9, 3.607444991487418*^9, { 490 | 3.6074491369888077`*^9, 3.6074491433536186`*^9}, {3.607499413075876*^9, 491 | 3.6074994166310797`*^9}, {3.607502901464954*^9, 3.607502901589754*^9}, { 492 | 3.6075029372514167`*^9, 3.6075029420406256`*^9}, {3.6075032558248944`*^9, 493 | 3.6075033274202213`*^9}, {3.607670823485029*^9, 3.607670824374118*^9}, { 494 | 3.607683978657365*^9, 3.6076839788473687`*^9}, {3.6076841337004657`*^9, 495 | 3.607684134740486*^9}}] 496 | }, Open ]] 497 | }, Open ]], 498 | 499 | Cell[CellGroupData[{ 500 | 501 | Cell["Filtering", "Section", 502 | CellChangeTimes->{{3.6073302443621573`*^9, 3.6073302449222136`*^9}, { 503 | 3.6074291064031205`*^9, 3.607429108573124*^9}, {3.607449354610387*^9, 504 | 3.6074493562483892`*^9}, {3.6076842329924517`*^9, 3.607684233272457*^9}}], 505 | 506 | Cell[TextData[{ 507 | "MathMF can have an input stream and an output stream open at the same time, \ 508 | so it is possible to use ", 509 | StyleBox["Mathematica", 510 | FontSlant->"Italic"], 511 | " as a video filter, just repeatedly read a frame from the input stream, \ 512 | process it, and send it to the output stream.\n\nExample - read frames from \ 513 | the WMV file we just created, process them with a GradientFilter, and write \ 514 | the results to an MP4 output file, changing the frame rate to 10 fps:" 515 | }], "Text", 516 | CellChangeTimes->{{3.6074291211631413`*^9, 3.6074291298631535`*^9}, { 517 | 3.607449360975198*^9, 3.6074494257621117`*^9}, {3.607450191719056*^9, 518 | 3.6074502302511234`*^9}, {3.6074503467677283`*^9, 3.6074503548641424`*^9}, { 519 | 3.6074511260878963`*^9, 3.607451167771169*^9}, {3.607502536458313*^9, 520 | 3.607502549468736*^9}, {3.6075030798145866`*^9, 3.6075030826693916`*^9}, { 521 | 3.607503133926281*^9, 3.6075031348154826`*^9}, {3.6075032462776775`*^9, 522 | 3.6075032466052785`*^9}, {3.607684281503422*^9, 3.607684320464201*^9}, { 523 | 3.607684391765627*^9, 3.607684392035632*^9}, {3.6076844530668526`*^9, 524 | 3.607684454256877*^9}, {3.6076845054379005`*^9, 3.6076845988797693`*^9}, { 525 | 3.607684635170495*^9, 3.607684640590603*^9}, {3.6076846852514963`*^9, 526 | 3.607684721882229*^9}, {3.6076848078039474`*^9, 3.6076848209542103`*^9}}], 527 | 528 | Cell[CellGroupData[{ 529 | 530 | Cell["Prepare source and sink", "Subsection", 531 | CellChangeTimes->{{3.607329081380389*^9, 3.607329084157389*^9}, { 532 | 3.607429224663286*^9, 3.60742922749329*^9}}], 533 | 534 | Cell[TextData[StyleBox["Reminder: change the file paths!", 535 | FontColor->RGBColor[1, 0, 0]]], "Text", 536 | CellChangeTimes->{{3.607698691387068*^9, 3.607698727625141*^9}}], 537 | 538 | Cell[CellGroupData[{ 539 | 540 | Cell[BoxData[ 541 | RowBox[{ 542 | RowBox[{"{", 543 | RowBox[{ 544 | "duration", ",", " ", "framerate", ",", "width", ",", " ", "height"}], 545 | "}"}], "=", "\[IndentingNewLine]", 546 | RowBox[{ 547 | "MFInitSourceReader", "[", 548 | "\"\\"", "]"}]}]], "Input", 549 | CellChangeTimes->{{3.60744941340689*^9, 3.607449462484576*^9}, { 550 | 3.607670838721553*^9, 3.6076708395916395`*^9}, 3.607684498517762*^9, { 551 | 3.607698527987722*^9, 3.6076985284117465`*^9}}], 552 | 553 | Cell[BoxData[ 554 | RowBox[{"{", 555 | RowBox[{"4.`", ",", "25.`", ",", "300", ",", "200"}], "}"}]], "Output", 556 | CellChangeTimes->{ 557 | 3.6074293601434755`*^9, 3.6074296426838713`*^9, 3.607439549728952*^9, 558 | 3.6074396050298486`*^9, {3.6074494305981197`*^9, 3.607449462858977*^9}, 559 | 3.607449699823393*^9, 3.607449873872899*^9, 3.607449917708976*^9, { 560 | 3.607449996005513*^9, 3.607450013782345*^9}, 3.607450057899222*^9, 561 | 3.6074501085681114`*^9, 3.6074501697514186`*^9, 3.6074510430281515`*^9, 562 | 3.607502471957402*^9, 3.6075025953640165`*^9, 3.607503111763443*^9, 563 | 3.6075031849695706`*^9, 3.6076160378953233`*^9, 3.6076167794738255`*^9, 564 | 3.6076708424979305`*^9, 3.6076848451946955`*^9, 3.6076988802698717`*^9}] 565 | }, Open ]], 566 | 567 | Cell[BoxData[ 568 | RowBox[{"MFInitSinkWriter", "[", 569 | RowBox[{ 570 | "\"\\"", ",", "width", 571 | ",", "height", ",", 572 | RowBox[{"\"\\"", "\[Rule]", "10"}]}], "]"}]], "Input", 573 | CellChangeTimes->{{3.607429344913454*^9, 3.6074293547834682`*^9}, { 574 | 3.6074395210405016`*^9, 3.6074395267969117`*^9}, 3.6074397735121417`*^9, { 575 | 3.607449466992984*^9, 3.607449480502608*^9}, 3.6074501616550045`*^9, 576 | 3.6074503692005672`*^9, {3.607451057754577*^9, 3.607451058799779*^9}, { 577 | 3.6075023872336535`*^9, 3.607502388622056*^9}, 3.6075025942720146`*^9, { 578 | 3.6075031093610387`*^9, 3.6075031379042883`*^9}, {3.607503183347168*^9, 579 | 3.6075031836903687`*^9}, 3.607503242658471*^9, {3.6076708445111313`*^9, 580 | 3.607670845400221*^9}, {3.607684465157095*^9, 3.6076844655171022`*^9}, { 581 | 3.6076846651110935`*^9, 3.607684667391139*^9}, {3.6076848302143955`*^9, 582 | 3.607684830324398*^9}, {3.60769853092389*^9, 3.6076985314919224`*^9}}] 583 | }, Open ]], 584 | 585 | Cell[CellGroupData[{ 586 | 587 | Cell["Run the filter", "Subsection", 588 | CellChangeTimes->{{3.6073287795773892`*^9, 3.607328781671389*^9}, { 589 | 3.607429387363514*^9, 3.6074293921535206`*^9}}], 590 | 591 | Cell[BoxData[ 592 | RowBox[{ 593 | RowBox[{"While", "[", "\[IndentingNewLine]", 594 | RowBox[{ 595 | RowBox[{ 596 | RowBox[{"(", 597 | RowBox[{"image", "=", 598 | RowBox[{"MFGrabFrame", "[", "\"\\"", "]"}]}], ")"}], "=!=", 599 | "EndOfFile"}], ",", "\[IndentingNewLine]", 600 | RowBox[{"MFSendFrame", " ", "@", 601 | RowBox[{"GradientFilter", "[", 602 | RowBox[{"image", ",", "2"}], "]"}]}]}], "\[IndentingNewLine]", "]"}], 603 | "~", "Monitor", "~", "image"}]], "Input", 604 | CellChangeTimes->{{3.6075024308669305`*^9, 3.6075024651089907`*^9}, { 605 | 3.607502584553198*^9, 3.607502588812005*^9}, {3.6075026223024635`*^9, 606 | 3.607502638807293*^9}, {3.6075031757031546`*^9, 3.607503207059209*^9}}], 607 | 608 | Cell[BoxData[ 609 | RowBox[{"MFFinaliseSink", "[", "]"}]], "Input"] 610 | }, Open ]] 611 | }, Open ]], 612 | 613 | Cell[CellGroupData[{ 614 | 615 | Cell["Notes on the code", "Section", 616 | CellChangeTimes->{{3.607678149005315*^9, 3.6076781644856243`*^9}}], 617 | 618 | Cell[CellGroupData[{ 619 | 620 | Cell[TextData[{ 621 | StyleBox["Mathematica", 622 | FontSlant->"Italic"], 623 | " side" 624 | }], "Subsection", 625 | CellChangeTimes->{{3.607685568709165*^9, 3.6076855748192873`*^9}}], 626 | 627 | Cell["\<\ 628 | Hopefully there is not much to apologise for in the .m package. The various \ 629 | functions do not do much except provide an interface to the LibraryLink \ 630 | functions with a little bit of error handling.\ 631 | \>", "Text", 632 | CellChangeTimes->{{3.6076855975897427`*^9, 3.607685604169874*^9}, { 633 | 3.60768564846076*^9, 3.607685759882989*^9}, {3.6076858085239615`*^9, 634 | 3.607685821984231*^9}, 3.6076860407286053`*^9}] 635 | }, Open ]], 636 | 637 | Cell[CellGroupData[{ 638 | 639 | Cell["C side", "Subsection", 640 | CellChangeTimes->{{3.6076855805594025`*^9, 3.60768558594951*^9}}], 641 | 642 | Cell["\<\ 643 | First, the excuses. This was my first attempt at C++ and COM programming, and \ 644 | LibraryLink too. So if the source code looks a bit dodgy, that\ 645 | \[CloseCurlyQuote]s because it is! I basically fumbled my way through until I \ 646 | got something working, so if you\[CloseCurlyQuote]re a real C/C++ programmer \ 647 | and the code made your eyes bleed, please please take it apart and put it \ 648 | back together properly... 649 | \ 650 | \>", "Text", 651 | CellChangeTimes->{{3.6076860825794425`*^9, 3.6076860870795326`*^9}, { 652 | 3.6076861319804306`*^9, 3.607686206161914*^9}, {3.607686241032612*^9, 653 | 3.6076863111440134`*^9}, {3.607686369005171*^9, 3.607686392315637*^9}, { 654 | 3.6076864408766084`*^9, 3.6076864423866386`*^9}, {3.6076868308444076`*^9, 655 | 3.607686831704425*^9}, {3.60768687747534*^9, 3.6076868932256556`*^9}, { 656 | 3.607688669057848*^9, 3.6076888250809684`*^9}, 3.6076890071946106`*^9, { 657 | 3.6076892183388333`*^9, 3.6076894002924724`*^9}, {3.607689446373394*^9, 658 | 3.607689508504637*^9}, {3.6076895397152605`*^9, 3.607689539785262*^9}, { 659 | 3.607689597426415*^9, 3.6076896172868123`*^9}, {3.6076896611876903`*^9, 660 | 3.6076898263209925`*^9}, {3.607690416492796*^9, 3.6076904367132006`*^9}}], 661 | 662 | Cell["\<\ 663 | In writing the code I wanted to keep the LibraryLink side of things separate \ 664 | from the Media Foundation parts. This made it much easier to debug the MF \ 665 | bits in a console application (I couldn't figure out how to get the debugger \ 666 | working with the DLL). So the MF code is contained within two classes, \ 667 | VidReader and VidWriter, and the LL stuff is all inside the exported DLL \ 668 | functions. At startup the library creates a global instance of VidReader and \ 669 | of VidWriter, the LL functions then call those objects\[CloseCurlyQuote] \ 670 | methods to access MF. In principle one could have multiple readers and \ 671 | writers but I\[CloseCurlyQuote]m not sure there\[CloseCurlyQuote]s really \ 672 | much need. 673 | 674 | The code was developed in Visual Studio Express 2012, with the source neatly \ 675 | partitioned into separate files. For the final package I just crudely mashed \ 676 | those files into one, no doubt breaking all the rules and conventions about \ 677 | how to structure a C++ file.\ 678 | \>", "Text", 679 | CellChangeTimes->{{3.6076860825794425`*^9, 3.6076860870795326`*^9}, { 680 | 3.6076861319804306`*^9, 3.607686206161914*^9}, {3.607686241032612*^9, 681 | 3.6076863111440134`*^9}, {3.607686369005171*^9, 3.607686392315637*^9}, { 682 | 3.6076864408766084`*^9, 3.6076864423866386`*^9}, {3.6076868308444076`*^9, 683 | 3.607686831704425*^9}, {3.60768687747534*^9, 3.6076868932256556`*^9}, { 684 | 3.607688669057848*^9, 3.6076888250809684`*^9}, 3.6076890071946106`*^9, { 685 | 3.6076892183388333`*^9, 3.6076894002924724`*^9}, {3.607689446373394*^9, 686 | 3.607689508504637*^9}, {3.6076895397152605`*^9, 3.607689539785262*^9}, { 687 | 3.607689597426415*^9, 3.6076896172868123`*^9}, {3.6076896611876903`*^9, 688 | 3.6076898486414394`*^9}, {3.6076898814120946`*^9, 3.607689993224331*^9}, { 689 | 3.6076900341151485`*^9, 3.607690074825963*^9}, {3.6076901214068947`*^9, 690 | 3.607690302940525*^9}, {3.6076903364111943`*^9, 3.6076903739619455`*^9}}] 691 | }, Open ]], 692 | 693 | Cell[CellGroupData[{ 694 | 695 | Cell["Bugs & contributions", "Subsection", 696 | CellChangeTimes->{{3.607685992217635*^9, 3.6076859928776484`*^9}, { 697 | 3.6076862296423836`*^9, 3.607686234222475*^9}}], 698 | 699 | Cell[TextData[{ 700 | "Feel free to contact me at mathmf@siwoods.co.uk if you are having any \ 701 | problems, in particular anything that crashes ", 702 | StyleBox["Mathematica", 703 | FontSlant->"Italic"], 704 | ".\n\nEven better, fix it yourself and let me know how :-)\n\nYou can \ 705 | contribute through GitHub or just send an email." 706 | }], "Text", 707 | CellChangeTimes->{{3.6076869711872144`*^9, 3.6076870457687063`*^9}, { 708 | 3.607687094609683*^9, 3.607687100869808*^9}, {3.6076884346631603`*^9, 709 | 3.6076886322271113`*^9}, {3.6076888902422714`*^9, 3.6076889403332734`*^9}, { 710 | 3.6076891691978507`*^9, 3.607689197568418*^9}, {3.607690510784682*^9, 711 | 3.6076905529155245`*^9}, {3.6076905882562313`*^9, 3.6076905887762413`*^9}}] 712 | }, Open ]] 713 | }, Closed]] 714 | }, Open ]] 715 | }, 716 | WindowSize->{899, 750}, 717 | WindowMargins->{{Automatic, 115}, {Automatic, 58}}, 718 | ShowSelection->True, 719 | FrontEndVersion->"9.0 for Microsoft Windows (64-bit) (January 25, 2013)", 720 | StyleDefinitions->Notebook[{ 721 | Cell[ 722 | StyleData[StyleDefinitions -> "Default.nb"]], 723 | Cell[ 724 | StyleData["Text"], FontFamily -> "Cambria", FontSize -> 14, FontWeight -> 725 | "Plain", FontSlant -> "Plain", 726 | FontVariations -> {"StrikeThrough" -> False, "Underline" -> False}, 727 | FontColor -> GrayLevel[0]], 728 | Cell[ 729 | StyleData["Code"], FontFamily -> "Courier New", FontSize -> 14, 730 | FontWeight -> "Bold", FontSlant -> "Plain", 731 | FontVariations -> {"StrikeThrough" -> False, "Underline" -> False}, 732 | Background -> None]}, Visible -> False, FrontEndVersion -> 733 | "9.0 for Microsoft Windows (64-bit) (January 25, 2013)", StyleDefinitions -> 734 | "PrivateStylesheetFormatting.nb"] 735 | ] 736 | (* End of Notebook Content *) 737 | 738 | (* Internal cache information *) 739 | (*CellTagsOutline 740 | CellTagsIndex->{} 741 | *) 742 | (*CellTagsIndex 743 | CellTagsIndex->{} 744 | *) 745 | (*NotebookFileOutline 746 | Notebook[{ 747 | Cell[CellGroupData[{ 748 | Cell[579, 22, 142, 2, 101, "Title"], 749 | Cell[724, 26, 340, 7, 126, "Subtitle"], 750 | Cell[1067, 35, 826, 16, 81, "Subsubtitle"], 751 | Cell[1896, 53, 1459, 24, 153, "Subsubtitle"], 752 | Cell[CellGroupData[{ 753 | Cell[3380, 81, 105, 1, 86, "Section"], 754 | Cell[3488, 84, 390, 7, 49, "Text"], 755 | Cell[CellGroupData[{ 756 | Cell[3903, 95, 244, 4, 31, "Input"], 757 | Cell[CellGroupData[{ 758 | Cell[4172, 103, 154, 2, 43, "Print"], 759 | Cell[4329, 107, 239, 3, 43, "Print"] 760 | }, Open ]] 761 | }, Open ]] 762 | }, Open ]], 763 | Cell[CellGroupData[{ 764 | Cell[4629, 117, 445, 6, 86, "Section"], 765 | Cell[5077, 125, 1019, 22, 127, "Text"], 766 | Cell[CellGroupData[{ 767 | Cell[6121, 151, 110, 1, 49, "Subsection"], 768 | Cell[6234, 154, 722, 11, 69, "Text"] 769 | }, Open ]], 770 | Cell[CellGroupData[{ 771 | Cell[6993, 170, 99, 1, 49, "Subsection"], 772 | Cell[7095, 173, 1005, 22, 130, "Text"], 773 | Cell[8103, 197, 1260, 22, 128, "Text"] 774 | }, Open ]], 775 | Cell[CellGroupData[{ 776 | Cell[9400, 224, 220, 5, 79, "Subsection"], 777 | Cell[9623, 231, 314, 5, 30, "Text"], 778 | Cell[CellGroupData[{ 779 | Cell[9962, 240, 897, 17, 52, "Input"], 780 | Cell[10862, 259, 1100, 17, 31, "Output"] 781 | }, Open ]], 782 | Cell[11977, 279, 423, 10, 31, "Input"] 783 | }, Open ]] 784 | }, Open ]], 785 | Cell[CellGroupData[{ 786 | Cell[12449, 295, 270, 4, 86, "Section"], 787 | Cell[12722, 301, 889, 16, 166, "Text"], 788 | Cell[CellGroupData[{ 789 | Cell[13636, 321, 106, 1, 49, "Subsection"], 790 | Cell[13745, 324, 1734, 34, 265, "Text"] 791 | }, Open ]], 792 | Cell[CellGroupData[{ 793 | Cell[15516, 363, 101, 1, 49, "Subsection"], 794 | Cell[15620, 366, 488, 10, 50, "Text"] 795 | }, Open ]], 796 | Cell[CellGroupData[{ 797 | Cell[16145, 381, 102, 1, 49, "Subsection"], 798 | Cell[16250, 384, 194, 4, 31, "Text"] 799 | }, Open ]], 800 | Cell[CellGroupData[{ 801 | Cell[16481, 393, 218, 5, 79, "Subsection"], 802 | Cell[16702, 400, 368, 5, 30, "Text"], 803 | Cell[17073, 407, 778, 18, 52, "Input"], 804 | Cell[17854, 427, 1474, 22, 31, "Input"], 805 | Cell[19331, 451, 1410, 24, 31, "Input"], 806 | Cell[20744, 477, 1218, 17, 31, "Input"] 807 | }, Open ]] 808 | }, Open ]], 809 | Cell[CellGroupData[{ 810 | Cell[22011, 500, 248, 3, 86, "Section"], 811 | Cell[22262, 505, 1309, 20, 106, "Text"], 812 | Cell[CellGroupData[{ 813 | Cell[23596, 529, 159, 2, 49, "Subsection"], 814 | Cell[23758, 533, 165, 2, 30, "Text"], 815 | Cell[CellGroupData[{ 816 | Cell[23948, 539, 474, 11, 52, "Input"], 817 | Cell[24425, 552, 713, 11, 31, "Output"] 818 | }, Open ]], 819 | Cell[25153, 566, 978, 15, 52, "Input"] 820 | }, Open ]], 821 | Cell[CellGroupData[{ 822 | Cell[26168, 586, 155, 2, 49, "Subsection"], 823 | Cell[26326, 590, 687, 15, 92, "Input"], 824 | Cell[27016, 607, 62, 1, 31, "Input"] 825 | }, Open ]] 826 | }, Open ]], 827 | Cell[CellGroupData[{ 828 | Cell[27127, 614, 104, 1, 86, "Section"], 829 | Cell[CellGroupData[{ 830 | Cell[27256, 619, 159, 5, 49, "Subsection"], 831 | Cell[27418, 626, 415, 7, 49, "Text"] 832 | }, Open ]], 833 | Cell[CellGroupData[{ 834 | Cell[27870, 638, 95, 1, 49, "Subsection"], 835 | Cell[27968, 641, 1184, 18, 87, "Text"], 836 | Cell[29155, 661, 1894, 28, 201, "Text"] 837 | }, Open ]], 838 | Cell[CellGroupData[{ 839 | Cell[31086, 694, 161, 2, 49, "Subsection"], 840 | Cell[31250, 698, 696, 12, 125, "Text"] 841 | }, Open ]] 842 | }, Closed]] 843 | }, Open ]] 844 | } 845 | ] 846 | *) 847 | 848 | (* End of internal cache information *) 849 | -------------------------------------------------------------------------------- /MathMF/Kernel/init.m: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | (* initialization file for the package MathMF` *) 4 | 5 | Get["MathMF`MathMF`"] 6 | -------------------------------------------------------------------------------- /MathMF/MathMF.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////// 2 | // MathMF // 3 | // Mathematica LibraryLink code for video frame import // 4 | // and export using Windows Media Foundation // 5 | // // 6 | // Simon Woods 2014 // 7 | // // 8 | ////////////////////////////////////////////////////////////// 9 | 10 | 11 | #include 12 | #define WIN32_LEAN_AND_MEAN 13 | #include 14 | 15 | // Wolfram Library 16 | #include "WolframLibrary.h" 17 | 18 | // Media Foundation 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | // Other bits 25 | #include 26 | #include 27 | 28 | // SafeRelease for COM Interface pointers 29 | template void SafeRelease(T **ppT) 30 | { 31 | if (*ppT) 32 | { 33 | (*ppT)->Release(); 34 | *ppT = NULL; 35 | } 36 | } 37 | 38 | class VidReader 39 | { 40 | private: 41 | 42 | IMFSourceReader *m_pReader; 43 | IMFMediaBuffer *m_pBuffer; 44 | 45 | public: 46 | 47 | VidReader(); 48 | ~VidReader(); 49 | 50 | LONGLONG m_timestamp; 51 | double m_duration; 52 | double m_framerate; 53 | UINT32 m_imageheight; 54 | UINT32 m_imagewidth; 55 | 56 | HRESULT initSourceReader(WCHAR *filename); 57 | HRESULT getReadBuffer(BYTE **ppData); 58 | HRESULT releaseBuffer(); 59 | 60 | private: 61 | HRESULT getDuration(); 62 | HRESULT selectVideoStream(); 63 | HRESULT getVideoFormat(); 64 | }; 65 | 66 | //------------------------------------------------------------------- 67 | // VidReader constructor 68 | // 69 | VidReader::VidReader() 70 | : m_pReader(NULL), m_pBuffer(NULL) 71 | { 72 | m_timestamp = 0; 73 | m_imageheight = 0; 74 | m_imagewidth = 0; 75 | m_duration = 0; 76 | m_framerate = 0; 77 | } 78 | 79 | //------------------------------------------------------------------- 80 | // VidReader destructor 81 | // 82 | VidReader::~VidReader() 83 | { 84 | SafeRelease(&m_pBuffer); 85 | SafeRelease(&m_pReader); 86 | } 87 | 88 | //------------------------------------------------------------------- 89 | // Initialise the source reader 90 | // 91 | HRESULT VidReader::initSourceReader(WCHAR *filename) 92 | { 93 | HRESULT hr = S_OK; 94 | IMFAttributes *pAttributes = NULL; 95 | 96 | SafeRelease(&m_pReader); 97 | 98 | // Configure the source reader to perform video processing 99 | hr = MFCreateAttributes(&pAttributes, 1); 100 | if (FAILED(hr)) goto done; 101 | hr = pAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, TRUE); 102 | if (FAILED(hr)) goto done; 103 | 104 | // Create the source reader from the URL 105 | hr = MFCreateSourceReaderFromURL(filename, pAttributes, &m_pReader); 106 | if (FAILED(hr)) goto done; 107 | 108 | // Attempt to find a video stream 109 | hr = selectVideoStream(); 110 | if (FAILED(hr)) goto done; 111 | 112 | // Get the stream format 113 | hr = getVideoFormat(); 114 | if (FAILED(hr)) goto done; 115 | 116 | // Get the duration 117 | hr = getDuration(); 118 | 119 | done: 120 | return hr; 121 | } 122 | 123 | //------------------------------------------------------------------- 124 | // Read a frame and provide access to the data 125 | // 126 | HRESULT VidReader::getReadBuffer(BYTE **ppData) 127 | { 128 | HRESULT hr = S_OK; 129 | DWORD dwFlags = 0; 130 | DWORD cbBitmapData = 0; // Size of data, in bytes 131 | IMFSample *pSample; 132 | 133 | if (!m_pReader) return E_ABORT; // if no source reader run away 134 | 135 | while (1) 136 | { 137 | hr = m_pReader->ReadSample( 138 | (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 139 | 0, NULL, &dwFlags, &m_timestamp, &pSample ); 140 | 141 | if (FAILED(hr)) goto done; 142 | 143 | if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM) 144 | { 145 | break; 146 | } 147 | 148 | if (dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) 149 | { 150 | // Type change. Get the new format. 151 | hr = getVideoFormat(); 152 | if (FAILED(hr)) goto done; 153 | } 154 | 155 | if (pSample == NULL) 156 | { 157 | continue; 158 | } 159 | 160 | // We got a sample. 161 | break; 162 | } 163 | 164 | if (pSample) 165 | { 166 | UINT32 pitch = 4 * m_imagewidth; 167 | 168 | hr = pSample->ConvertToContiguousBuffer(&m_pBuffer); 169 | if (FAILED(hr)) goto done; 170 | 171 | hr = m_pBuffer->Lock(ppData, NULL, &cbBitmapData); 172 | if (FAILED(hr)) goto done; 173 | 174 | assert(cbBitmapData == (pitch * m_imageheight)); 175 | } 176 | else 177 | { 178 | hr = MF_E_END_OF_STREAM; 179 | } 180 | 181 | done: 182 | SafeRelease(&pSample); 183 | return hr; 184 | } 185 | 186 | //------------------------------------------------------------------- 187 | // Release the buffer 188 | // 189 | HRESULT VidReader::releaseBuffer() 190 | { 191 | HRESULT hr = S_OK; 192 | 193 | if (m_pBuffer) hr = m_pBuffer->Unlock(); 194 | SafeRelease(&m_pBuffer); 195 | 196 | return hr; 197 | } 198 | 199 | //------------------------------------------------------------------- 200 | // selectVideoStream: Finds the first video stream and sets the format to RGB32. 201 | // 202 | HRESULT VidReader::selectVideoStream() 203 | { 204 | HRESULT hr = S_OK; 205 | IMFMediaType *pType = NULL; 206 | 207 | // Configure the source reader to give us progressive RGB32 frames. 208 | // The source reader will load the decoder if needed. 209 | 210 | hr = MFCreateMediaType(&pType); 211 | if (FAILED(hr)) goto done; 212 | 213 | hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 214 | if (FAILED(hr)) goto done; 215 | 216 | hr = pType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32); 217 | if (FAILED(hr)) goto done; 218 | 219 | hr = m_pReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pType); 220 | if (FAILED(hr)) goto done; 221 | 222 | // Ensure the stream is selected. 223 | hr = m_pReader->SetStreamSelection((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, TRUE); 224 | if (FAILED(hr)) goto done; 225 | 226 | done: 227 | 228 | SafeRelease(&pType); 229 | return hr; 230 | } 231 | 232 | //------------------------------------------------------------------- 233 | // getVideoFormat: Gets format information for the video stream. 234 | // 235 | HRESULT VidReader::getVideoFormat() 236 | { 237 | HRESULT hr = S_OK; 238 | IMFMediaType *pType = NULL; 239 | GUID subtype = { 0 }; 240 | 241 | // Get the media type from the stream. 242 | hr = m_pReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, &pType); 243 | if (FAILED(hr)) goto done; 244 | 245 | // Make sure it is a video format. 246 | 247 | hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype); 248 | if (subtype != MFVideoFormat_RGB32) 249 | { 250 | hr = E_UNEXPECTED; 251 | goto done; 252 | } 253 | 254 | // Get the width and height 255 | hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &m_imagewidth, &m_imageheight); 256 | if (FAILED(hr)) goto done; 257 | 258 | // Get the frame rate 259 | UINT32 frN, frD; 260 | hr = MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, &frN, &frD); 261 | if (FAILED(hr)) goto done; 262 | m_framerate = (double)frN / (double)frD; 263 | 264 | done: 265 | 266 | SafeRelease(&pType); 267 | return hr; 268 | } 269 | 270 | //------------------------------------------------------------------- 271 | // getDuration: Finds the duration of the current video file 272 | // 273 | HRESULT VidReader::getDuration() 274 | { 275 | HRESULT hr = S_OK; 276 | PROPVARIANT var; 277 | LONGLONG hnsDuration; 278 | 279 | if (m_pReader == NULL) return MF_E_NOT_INITIALIZED; 280 | 281 | PropVariantInit(&var); 282 | 283 | hr = m_pReader->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var); 284 | 285 | if (SUCCEEDED(hr)) 286 | { 287 | assert(var.vt == VT_UI8); 288 | hnsDuration = var.hVal.QuadPart; 289 | } 290 | 291 | PropVariantClear(&var); 292 | 293 | // update member 294 | m_duration = (double)hnsDuration * 0.0000001; 295 | 296 | return hr; 297 | } 298 | 299 | class VidWriter 300 | { 301 | private: 302 | 303 | IMFSinkWriter *m_pWriter; 304 | IMFMediaBuffer *m_pBuffer; 305 | DWORD m_streamIndex; 306 | 307 | HRESULT configureOutput(IMFMediaType *pMT); 308 | HRESULT configureInput(IMFMediaType *pMT); 309 | 310 | public: 311 | 312 | VidWriter(); 313 | ~VidWriter(); 314 | 315 | GUID m_encodeformat; 316 | UINT32 m_bitrate; 317 | UINT64 m_frametime; 318 | UINT32 m_width, m_height; 319 | UINT64 m_rtStart; // start time of current frame 320 | 321 | HRESULT initSinkWriter(WCHAR *filename); 322 | HRESULT setParams(long encoderformat, long width, long height, double framerate, long kbps); 323 | HRESULT getWriteBuffer(BYTE **ppData); 324 | HRESULT writeFrame(BYTE *pData); 325 | HRESULT finalise(); 326 | 327 | }; 328 | 329 | //------------------------------------------------------------------- 330 | // VidWriter constructor 331 | // 332 | VidWriter::VidWriter() 333 | : m_pWriter(NULL), m_pBuffer(NULL) 334 | { 335 | } 336 | 337 | //------------------------------------------------------------------- 338 | // VidWriter destructor 339 | // 340 | VidWriter::~VidWriter() 341 | { 342 | SafeRelease(&m_pWriter); 343 | SafeRelease(&m_pBuffer); 344 | } 345 | 346 | //------------------------------------------------------------------- 347 | // Open a file for writing and prepare the sink writer 348 | // 349 | HRESULT VidWriter::initSinkWriter(WCHAR *filename) 350 | { 351 | HRESULT hr = S_OK; 352 | IMFMediaType *pMediaTypeOut = NULL; 353 | IMFMediaType *pMediaTypeIn = NULL; 354 | 355 | // Create the sink writer 356 | SafeRelease(&m_pWriter); 357 | hr = MFCreateSinkWriterFromURL(filename, NULL, NULL, &m_pWriter); 358 | if (FAILED(hr)) goto done; 359 | 360 | // Create the output media type 361 | hr = MFCreateMediaType(&pMediaTypeOut); 362 | if (FAILED(hr)) goto done; 363 | 364 | // Configure it 365 | hr = configureOutput(pMediaTypeOut); 366 | if (FAILED(hr)) goto done; 367 | 368 | // Add it to the sink writer 369 | hr = m_pWriter->AddStream(pMediaTypeOut, &m_streamIndex); 370 | if (FAILED(hr)) goto done; 371 | 372 | // Create the input media type 373 | hr = MFCreateMediaType(&pMediaTypeIn); 374 | if (FAILED(hr)) goto done; 375 | 376 | // Configure it 377 | hr = configureInput(pMediaTypeIn); 378 | if (FAILED(hr)) goto done; 379 | 380 | // Add it to the sink writer 381 | hr = m_pWriter->SetInputMediaType(m_streamIndex, pMediaTypeIn, NULL); 382 | if (FAILED(hr)) goto done; 383 | 384 | // Tell the sink writer to start accepting data 385 | hr = m_pWriter->BeginWriting(); 386 | 387 | // Reset the frame timer 388 | m_rtStart = 0; 389 | 390 | done: 391 | SafeRelease(&pMediaTypeOut); 392 | SafeRelease(&pMediaTypeIn); 393 | return hr; 394 | } 395 | 396 | //------------------------------------------------------------------- 397 | // Configure output & input media types 398 | // 399 | HRESULT VidWriter::configureOutput(IMFMediaType *pMT) 400 | { 401 | HRESULT hr = S_OK; 402 | UINT32 frNumerator, frDenominator; 403 | 404 | hr = pMT->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 405 | if (FAILED(hr)) goto done; 406 | 407 | // Set format 408 | hr = pMT->SetGUID(MF_MT_SUBTYPE, m_encodeformat); 409 | if (FAILED(hr)) goto done; 410 | 411 | // Set bit rate 412 | hr = pMT->SetUINT32(MF_MT_AVG_BITRATE, m_bitrate); 413 | if (FAILED(hr)) goto done; 414 | 415 | // Set progressive frames 416 | hr = pMT->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); 417 | if (FAILED(hr)) goto done; 418 | 419 | // Set frame size 420 | hr = MFSetAttributeSize(pMT, MF_MT_FRAME_SIZE, m_width, m_height); 421 | if (FAILED(hr)) goto done; 422 | 423 | // Set frame rate 424 | hr = MFAverageTimePerFrameToFrameRate(m_frametime, &frNumerator, &frDenominator); 425 | if (FAILED(hr)) goto done; 426 | hr = MFSetAttributeRatio(pMT, MF_MT_FRAME_RATE, frNumerator, frDenominator); 427 | if (FAILED(hr)) goto done; 428 | 429 | // Set PAR 430 | hr = MFSetAttributeRatio(pMT, MF_MT_PIXEL_ASPECT_RATIO, 1, 1); 431 | if (FAILED(hr)) goto done; 432 | 433 | done: 434 | return hr; 435 | } 436 | 437 | HRESULT VidWriter::configureInput(IMFMediaType *pMT) 438 | { 439 | HRESULT hr = S_OK; 440 | UINT32 frNumerator, frDenominator; 441 | 442 | hr = pMT->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 443 | if (FAILED(hr)) goto done; 444 | 445 | // Set format 446 | hr = pMT->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32); 447 | if (FAILED(hr)) goto done; 448 | 449 | // Set progressive frames 450 | hr = pMT->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); 451 | if (FAILED(hr)) goto done; 452 | 453 | // Set frame size 454 | hr = MFSetAttributeSize(pMT, MF_MT_FRAME_SIZE, m_width, m_height); 455 | if (FAILED(hr)) goto done; 456 | 457 | // Set frame rate (not sure why we need to set a frame rate on the input) 458 | hr = MFAverageTimePerFrameToFrameRate(m_frametime, &frNumerator, &frDenominator); 459 | if (FAILED(hr)) goto done; 460 | hr = MFSetAttributeRatio(pMT, MF_MT_FRAME_RATE, frNumerator, frDenominator); 461 | if (FAILED(hr)) goto done; 462 | 463 | // Set PAR 464 | hr = MFSetAttributeRatio(pMT, MF_MT_PIXEL_ASPECT_RATIO, 1, 1); 465 | if (FAILED(hr)) goto done; 466 | 467 | done: 468 | return hr; 469 | } 470 | 471 | //------------------------------------------------------------------- 472 | // Set the output parameters 473 | // 474 | HRESULT VidWriter::setParams(long encoderformat, long width, long height, double framerate, long kbps) 475 | { 476 | // Select encoding format 477 | if (encoderformat == 1) 478 | { 479 | m_encodeformat = MFVideoFormat_WMV3; 480 | } 481 | else 482 | { 483 | m_encodeformat = MFVideoFormat_H264; 484 | } 485 | 486 | //Set frame size 487 | m_width = (UINT32) width; 488 | m_height = (UINT32) height; 489 | 490 | // Set frame time 491 | m_frametime = (UINT64) (10000000 / framerate); 492 | 493 | //Set bitrate 494 | m_bitrate = (UINT32) (1000 * kbps); 495 | 496 | return S_OK; 497 | } 498 | 499 | //------------------------------------------------------------------- 500 | // Get a buffer to write data into 501 | // 502 | HRESULT VidWriter::getWriteBuffer(BYTE **ppData) 503 | { 504 | HRESULT hr; 505 | const DWORD cbBuffer = 4 * m_width * m_height; 506 | 507 | // Create a new memory buffer 508 | SafeRelease(&m_pBuffer); 509 | hr = MFCreateMemoryBuffer(cbBuffer, &m_pBuffer); 510 | if (FAILED(hr)) goto done; 511 | 512 | // Lock the buffer 513 | hr = m_pBuffer->Lock(ppData, NULL, NULL); 514 | 515 | done: 516 | return hr; 517 | } 518 | 519 | //------------------------------------------------------------------- 520 | // Write the sample 521 | // 522 | HRESULT VidWriter::writeFrame(BYTE *pData) 523 | { 524 | HRESULT hr; 525 | IMFSample *pSample = NULL; 526 | const DWORD cbBuffer = 4 * m_width * m_height; 527 | 528 | // Unlock the buffer 529 | if (m_pBuffer) m_pBuffer->Unlock(); 530 | 531 | // Set the data length of the buffer 532 | hr = m_pBuffer->SetCurrentLength(cbBuffer); 533 | if (FAILED(hr)) goto done; 534 | 535 | // Create a media sample and add the buffer to it 536 | hr = MFCreateSample(&pSample); 537 | if (FAILED(hr)) goto done; 538 | hr = pSample->AddBuffer(m_pBuffer); 539 | if (FAILED(hr)) goto done; 540 | 541 | // Set the time stamp and the duration 542 | hr = pSample->SetSampleTime(m_rtStart); 543 | if (FAILED(hr)) goto done; 544 | hr = pSample->SetSampleDuration(m_frametime); 545 | if (FAILED(hr)) goto done; 546 | 547 | // increment the time stamp 548 | m_rtStart += m_frametime; 549 | 550 | // Send the sample to the Sink Writer 551 | hr = m_pWriter->WriteSample(m_streamIndex, pSample); 552 | 553 | done: 554 | SafeRelease(&pSample); 555 | return hr; 556 | } 557 | 558 | //------------------------------------------------------------------- 559 | // Finalise the sink writer 560 | // 561 | HRESULT VidWriter::finalise() 562 | { 563 | if (!m_pWriter) return E_ABORT; 564 | 565 | HRESULT hr = m_pWriter->Finalize(); 566 | SafeRelease(&m_pWriter); 567 | return hr; 568 | } 569 | 570 | //////////////////////////////// 571 | // Globals 572 | //////////////////////////////// 573 | 574 | VidReader VR; 575 | VidWriter VW; 576 | static HRESULT hr; 577 | 578 | //////////////////////////////// 579 | // LibraryLink stuff 580 | //////////////////////////////// 581 | 582 | EXTERN_C DLLEXPORT mint WolframLibrary_getVersion( ) { 583 | return WolframLibraryVersion; 584 | } 585 | 586 | EXTERN_C DLLEXPORT int WolframLibrary_initialize(WolframLibraryData libData) { 587 | 588 | // Initialize the COM library 589 | hr = CoInitialize(NULL); 590 | if (FAILED(hr)) 591 | { 592 | libData->Message("cominitfail"); 593 | return LIBRARY_FUNCTION_ERROR; 594 | } 595 | 596 | // Initialize Media Foundation. 597 | hr = MFStartup(MF_VERSION); 598 | if (FAILED(hr)) 599 | { 600 | libData->Message("mfinitfail"); 601 | return LIBRARY_FUNCTION_ERROR; 602 | } 603 | 604 | return LIBRARY_NO_ERROR; 605 | } 606 | 607 | EXTERN_C DLLEXPORT void WolframLibrary_uninitialize(WolframLibraryData libData) { 608 | 609 | VR.~VidReader(); // destruct the class instances 610 | VW.~VidWriter(); 611 | 612 | // Shut down media foundation and close the COM library 613 | hr = MFShutdown(); 614 | if (FAILED(hr)) 615 | { 616 | libData->Message("mfshutdownfail"); 617 | } 618 | CoUninitialize(); 619 | 620 | return; 621 | } 622 | 623 | 624 | // Source Reader exports 625 | 626 | EXTERN_C DLLEXPORT int InitSourceReader(WolframLibraryData libData, mint Argc, MArgument * Args, MArgument Res) { 627 | 628 | // Get the arguments from MArgument 629 | char *videofile = MArgument_getUTF8String(Args[0]); 630 | 631 | // convert UTF8 filename to wide chars 632 | int size = MultiByteToWideChar(CP_ACP, 0, videofile, -1, NULL, 0); 633 | WCHAR* videofileW = new WCHAR[size]; 634 | size = MultiByteToWideChar(CP_ACP, 0, videofile, -1, videofileW, size); 635 | 636 | // Open the file 637 | hr = VR.initSourceReader(videofileW); 638 | 639 | // finished with the filename now 640 | delete videofileW; 641 | 642 | if (FAILED(hr)) 643 | { 644 | libData->Message("initsourcefail"); 645 | return LIBRARY_FUNCTION_ERROR; 646 | } 647 | 648 | // Return the duration and frame rate 649 | MTensor T0; 650 | mint dims[1] = {4}; 651 | mint pos[1]; 652 | 653 | libData->MTensor_new(MType_Real, 1, dims, &T0); 654 | 655 | pos[0] = 1; 656 | libData->MTensor_setReal(T0, pos, VR.m_duration); 657 | 658 | pos[0] = 2; 659 | libData->MTensor_setReal(T0, pos, VR.m_framerate); 660 | 661 | pos[0] = 3; 662 | libData->MTensor_setReal(T0, pos, VR.m_imagewidth); 663 | 664 | pos[0] = 4; 665 | libData->MTensor_setReal(T0, pos, VR.m_imageheight); 666 | 667 | MArgument_setMTensor(Res, T0); 668 | 669 | return LIBRARY_NO_ERROR; 670 | } 671 | 672 | EXTERN_C DLLEXPORT int GrabFrame(WolframLibraryData libData, mint Argc, MArgument * Args, MArgument Res) { 673 | 674 | int err = LIBRARY_NO_ERROR; 675 | 676 | // get a pointer to the byte buffer: 677 | BYTE *pData; 678 | hr = VR.getReadBuffer(&pData); 679 | 680 | if (hr == MF_E_END_OF_STREAM) 681 | { 682 | libData->Message("endofstream"); 683 | return LIBRARY_FUNCTION_ERROR; 684 | } 685 | 686 | if (FAILED(hr)) 687 | { 688 | libData->Message("getdatafail"); 689 | return LIBRARY_FUNCTION_ERROR; 690 | } 691 | 692 | // create the rank 3 MTensor 693 | MTensor T0; 694 | mint dims[3]; 695 | dims[0] = (mint) VR.m_imageheight; 696 | dims[1] = (mint) VR.m_imagewidth; 697 | dims[2] = 3; 698 | err = libData->MTensor_new(MType_Integer, 3, dims, &T0); 699 | 700 | // fill the MTensor from the byte buffer 701 | mint *mtdata; 702 | mtdata = libData->MTensor_getIntegerData(T0); 703 | int npixels = VR.m_imageheight * VR.m_imagewidth; 704 | 705 | for (int i = 0; i < npixels; i++) { 706 | // we read B, G, R, (A) bytes and put R, G, B into the pixel buffer 707 | mtdata[3 * i ] = (mint) pData[4 * i + 2]; // R 708 | mtdata[3 * i + 1] = (mint) pData[4 * i + 1]; // G 709 | mtdata[3 * i + 2] = (mint) pData[4 * i ]; // B 710 | } 711 | 712 | // unlock the buffer 713 | hr = VR.releaseBuffer(); 714 | if (FAILED(hr)) 715 | { 716 | libData->Message("releaseBufferfail"); 717 | return LIBRARY_FUNCTION_ERROR; 718 | } 719 | 720 | // return the MTensor to Mathematica 721 | MArgument_setMTensor(Res, T0); 722 | return err; 723 | 724 | } 725 | 726 | EXTERN_C DLLEXPORT int SourceTime(WolframLibraryData libData, mint Argc, MArgument * Args, MArgument Res) { 727 | 728 | // Get the timestamp 729 | mreal time; 730 | time = (mreal)VR.m_timestamp * 0.0000001; 731 | 732 | MArgument_setReal(Res, time); 733 | 734 | return LIBRARY_NO_ERROR; 735 | } 736 | 737 | // Sink Writer exports 738 | 739 | EXTERN_C DLLEXPORT int InitSinkWriter(WolframLibraryData libData, mint Argc, MArgument * Args, MArgument Res) { 740 | 741 | // Get the arguments from MArgument 742 | char *videofile = MArgument_getUTF8String(Args[0]); 743 | 744 | // convert UTF8 filename to wide chars 745 | int size = MultiByteToWideChar(CP_ACP, 0, videofile, -1, NULL, 0); 746 | WCHAR* videofileW = new WCHAR[size]; 747 | size = MultiByteToWideChar(CP_ACP, 0, videofile, -1, videofileW, size); 748 | 749 | // extract the sink parameters 750 | mint encoder = MArgument_getInteger(Args[1]); 751 | mint width = MArgument_getInteger(Args[2]); 752 | mint height = MArgument_getInteger(Args[3]); 753 | mreal framerate = MArgument_getReal(Args[4]); 754 | mint kbps = MArgument_getInteger(Args[5]); 755 | 756 | hr = VW.setParams(encoder, width, height, framerate, kbps); 757 | if (FAILED(hr)) 758 | { 759 | libData->Message("setsinkparamsfail"); 760 | return LIBRARY_FUNCTION_ERROR; 761 | } 762 | 763 | // Initialise the sink writer 764 | hr = VW.initSinkWriter(videofileW); 765 | 766 | // finished with the filename now 767 | delete videofileW; 768 | 769 | if (FAILED(hr)) 770 | { 771 | libData->Message("initsinkfail"); 772 | return LIBRARY_FUNCTION_ERROR; 773 | } 774 | 775 | return LIBRARY_NO_ERROR; 776 | } 777 | 778 | EXTERN_C DLLEXPORT int SendFrame(WolframLibraryData libData, mint Argc, MArgument * Args, MArgument Res) { 779 | 780 | MTensor T0 = MArgument_getMTensor(Args[0]); 781 | mint len = libData->MTensor_getFlattenedLength(T0); 782 | 783 | // check that we have the right amount of data 784 | UINT32 expectedlength = VW.m_height * VW.m_width * 3; 785 | 786 | if (len != (mint) expectedlength) 787 | { 788 | libData->Message("dimensionsfail"); 789 | return LIBRARY_FUNCTION_ERROR; 790 | } 791 | 792 | // get a buffer 793 | BYTE *pData = NULL; 794 | hr = VW.getWriteBuffer(&pData); 795 | if (FAILED(hr)) 796 | { 797 | libData->Message("getwritebufferfail"); 798 | return LIBRARY_FUNCTION_ERROR; 799 | } 800 | 801 | // fill the buffer 802 | mint *mtdata; 803 | int npixels = len / 3; 804 | mtdata = libData->MTensor_getIntegerData(T0); 805 | 806 | for (int i = 0; i < npixels; i++) { 807 | // we read R, G, B bytes and put B, G, R, A into the pixel buffer 808 | pData[4 * i + 2] = (BYTE) mtdata[3 * i ]; // R 809 | pData[4 * i + 1] = (BYTE) mtdata[3 * i + 1]; // G 810 | pData[4 * i ] = (BYTE) mtdata[3 * i + 2]; // B 811 | pData[4 * i + 3] = 0; // A 812 | } 813 | 814 | // write the sample 815 | hr = VW.writeFrame(pData); 816 | if (FAILED(hr)) 817 | { 818 | libData->Message("writeFramefail"); 819 | return LIBRARY_FUNCTION_ERROR; 820 | } 821 | 822 | // return the timestamp to Mathematica 823 | MArgument_setInteger(Res, VW.m_rtStart); 824 | 825 | return LIBRARY_NO_ERROR; 826 | } 827 | 828 | EXTERN_C DLLEXPORT int FinaliseSink(WolframLibraryData libData, mint Argc, MArgument * Args, MArgument Res) { 829 | 830 | hr = VW.finalise(); 831 | 832 | if (FAILED(hr)) 833 | { 834 | libData->Message("finalisefail"); 835 | return LIBRARY_FUNCTION_ERROR; 836 | } 837 | 838 | return LIBRARY_NO_ERROR; 839 | } 840 | 841 | 842 | -------------------------------------------------------------------------------- /MathMF/MathMF.m: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | BeginPackage["MathMF`",{"CCompilerDriver`"}] 4 | 5 | 6 | MFInitSourceReader::usage="MFInitSourceReader[filename] initialises the source reader. 7 | It returns {duration, framerate, width, height}" 8 | 9 | 10 | MFGrabFrame::usage="MFGrabFrame[type] grabs a frame from the input stream. 11 | Possible values for type are \"Byte\", \"Real\", \"ByteImage\" or \"RealImage\"" 12 | 13 | 14 | MFSourceTime::usage="MFSourceTime[] returns the timestamp of the last grabbed frame" 15 | 16 | 17 | MFInitSinkWriter::usage="MFInitSinkWriter[filename, width, height] initialises the sink writer" 18 | 19 | 20 | MFSendFrame::usage="MFSendFrame[data] sends data (an Image or image data array) to the output stream" 21 | 22 | 23 | MFFinaliseSink::usage="MFFinaliseSink[] is used to finish writing a video stream and close the output file." 24 | 25 | 26 | MFUnload::usage="MFUnload[] unloads the MathMF library. 27 | This is primarily for use when developing the library code and should not be needed in normal usage." 28 | 29 | 30 | MFVideo::nofile="The input file does not exist." 31 | 32 | 33 | MFVideo::nosource="There are no frames to read. Use MFInitSourceReader to open an input stream." 34 | 35 | 36 | MFVideo::fileexists="The output file already exists." 37 | 38 | 39 | MFVideo::sinkopen="The currently open output file will be closed." 40 | 41 | 42 | MFVideo::badtype="The output file must have a .wmv or .mp4 extension." 43 | 44 | 45 | MFVideo::odddims="The frame width and height must be even numbers." 46 | 47 | 48 | MFVideo::nosink="There is no output stream to send frames to. Use MFInitSinkWriter to open an output file." 49 | 50 | 51 | MFVideo::nosinkf="There is no output stream to finalise." 52 | 53 | 54 | MFVideo::baddata="The data must be an Image or a 2D or 3D numerical array." 55 | 56 | 57 | MFVideo::baddims="The data dimensions are incorrect." 58 | 59 | 60 | LibraryFunction::initsourcefail="Unable to initialise source reader. Possible reasons include: 61 | The file is not a valid media file. 62 | The file does not contain any video streams. 63 | The Media Foundation platform cannot decode the stream." 64 | 65 | 66 | LibraryFunction::finalisefail="The sink writer could not be finalised. Possible reasons include: 67 | The sink writer was not initialised. 68 | No frames were sent to the sink writer. 69 | The bit rate was too low and no samples were added to the stream." 70 | 71 | 72 | Begin["Private`"] 73 | 74 | 75 | (* ::Section:: *) 76 | (*Find or create the DLL*) 77 | 78 | 79 | MathMFlib=FindLibrary["MathMF"] 80 | 81 | 82 | If[MathMFlib===$Failed, 83 | Print["No MathMF.DLL found.\nMathMF will attempt to build the library from source."]; 84 | Module[{codefile,code}, 85 | codefile=FileNameJoin[ReplacePart[FileNameSplit[$InputFileName],-1->"MathMF.cpp"]]; 86 | If[!FileExistsQ[codefile], 87 | Print["No source file found at "<>codefile<>"\nAborting MFMath."];Abort[]]; 88 | code=Import[codefile,"Text"]; 89 | Needs["CCompilerDriver`"]; 90 | MathMFlib=CreateLibrary[code,"MathMF", 91 | "Language"->"C++", 92 | "CleanIntermediate"->True, 93 | "Libraries"->{"ole32.lib","mfreadwrite.lib","mfplat.lib","mfuuid.lib"}]; 94 | If[!FileExistsQ[MathMFlib], 95 | Print["Library creation failed.\nAborting MFMath."];Abort[], 96 | Print["MathMF.DLL successfully created at "<>MathMFlib]]]] 97 | 98 | 99 | (* ::Section:: *) 100 | (*Load the library functions*) 101 | 102 | 103 | (* ::Subsection:: *) 104 | (*Underlying LibraryFunction[] 's*) 105 | 106 | 107 | MFISR=LibraryFunctionLoad[MathMFlib,"InitSourceReader",{"UTF8String"},{Real,1}] 108 | 109 | 110 | MFGF=LibraryFunctionLoad[MathMFlib,"GrabFrame",{},{Integer,3}] 111 | 112 | 113 | MFSourceTime=LibraryFunctionLoad[MathMFlib,"SourceTime",{},Real] 114 | 115 | 116 | MFISW=LibraryFunctionLoad[MathMFlib,"InitSinkWriter",{"UTF8String",Integer,Integer,Integer,Real,Integer},"Void"] 117 | 118 | 119 | MFSF=LibraryFunctionLoad[MathMFlib,"SendFrame",{{Integer,3,"Constant"}},Integer] 120 | 121 | 122 | MFFS=LibraryFunctionLoad[MathMFlib,"FinaliseSink",{},"Void"] 123 | 124 | 125 | (* ::Subsection:: *) 126 | (*Create wrapper functions to check arguments and handle errors*) 127 | 128 | 129 | $sourceopen=False; 130 | $sinkopen=False; 131 | 132 | 133 | convertFromByteData[x_,"Byte"]:=x; 134 | convertFromByteData[x_,"Real"]:=x/255.; 135 | convertFromByteData[x_,"ByteImage"]:=Image[x,"Byte"]; 136 | convertFromByteData[x_,"RealImage"]:=Image[x/255.]; 137 | 138 | 139 | convertToByteData[x_Image]:=ImageData[x~ColorConvert~"RGB","Byte"]; 140 | convertToByteData[x_List/;ArrayQ[x,2|3,IntegerQ]]:=convertToByteData[Image[x,"Byte"]]; 141 | convertToByteData[x_List/;ArrayQ[x,2|3,NumericQ]]:=convertToByteData[Image[x]]; 142 | convertToByteData[__]=$Failed; 143 | 144 | 145 | MFInitSourceReader[fn_String]:=Catch[Module[{res}, 146 | If[!FileExistsQ[fn],Message[MFVideo::nofile];Throw[{}]]; 147 | res=MapAt[Round,Check[MFISR[fn],Throw[$Failed]],{{3},{4}}]; 148 | $sourceopen=True; 149 | res]] 150 | 151 | 152 | MFGrabFrame[s:("Byte"|"Real"|"ByteImage"|"RealImage")]:= 153 | If[$sourceopen, 154 | With[{m=LibraryFunction::endofstream}, 155 | Catch[convertFromByteData[Quiet[Check[MFGF[],$sourceopen=False;Throw[EndOfFile],{m}],{m}],s]]], 156 | Message[MFVideo::nosource];$Failed] 157 | 158 | 159 | MFGrabFrame[]:=MFGrabFrame["Real"] 160 | 161 | 162 | Options[MFInitSinkWriter]={"FrameRate"->29.97,"CompressionRatio"->100,"BitRate"->Automatic} 163 | 164 | 165 | MFInitSinkWriter[fn_String,w_Integer,h_Integer,OptionsPattern[]]:=Catch[ 166 | If[$sinkopen,Message[MFVideo::sinkopen];MFFinaliseSink[]]; 167 | If[FileExistsQ[fn],Message[MFVideo::fileexists];Throw[$Failed]]; 168 | If[OddQ[w]||OddQ[h],Message[MFVideo::odddims];Throw[$Failed]]; 169 | $mfswd={h,w,3}; 170 | Module[{FR,CR,BR}, 171 | FR=OptionValue["FrameRate"]; 172 | CR=OptionValue["CompressionRatio"]; 173 | BR=OptionValue["BitRate"]/.Automatic->Round[24FR w h /(1000CR)]; 174 | Which[ 175 | StringMatchQ[fn,__~~".wmv"], 176 | $mfswf=Identity;MFISW[fn,1,w,h,FR,BR];$sinkopen=True;, 177 | StringMatchQ[fn,__~~".mp4"], 178 | (* MP4 writing requires the data upside down *) 179 | $mfswf=Reverse;MFISW[fn,2,w,h,FR,BR];$sinkopen=True;, 180 | True,Message[MFVideo::badtype];$Failed]]] 181 | 182 | 183 | MFSendFrame[data_]:=Catch[ 184 | If[!$sinkopen,Message[MFVideo::nosink];Throw[$Failed]]; 185 | Module[{d=convertToByteData[data]}, 186 | If[d===$Failed,Message[MFVideo::baddata];Throw[$Failed]]; 187 | If[Dimensions[d]=!=$mfswd,Message[MFVideo::baddims];Throw[$Failed]]; 188 | MFSF[$mfswf@d]]] 189 | 190 | 191 | MFFinaliseSink[]:=If[!$sinkopen,Message[MFVideo::nosinkf],MFFS[];$sinkopen=False;] 192 | 193 | 194 | MFUnload[]:=LibraryUnload@MathMFlib 195 | 196 | 197 | (* ::Section:: *) 198 | (*Finish*) 199 | 200 | 201 | End[] 202 | 203 | 204 | EndPackage[] 205 | -------------------------------------------------------------------------------- /optional pre-built v10 DLL/MathMF.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimonWoods/MathMF/37bde0cee8dbd12649584644f2152741bb18fb1e/optional pre-built v10 DLL/MathMF.dll -------------------------------------------------------------------------------- /optional pre-built v9 DLL/MathMF.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimonWoods/MathMF/37bde0cee8dbd12649584644f2152741bb18fb1e/optional pre-built v9 DLL/MathMF.dll -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | MathMF 2 | ====== 3 | 4 | A *Mathematica* package for frame-by-frame import and export of videos using Windows Media Foundation. 5 | 6 | This was developed for *Mathematica* users on Windows who do not have Quicktime installed, because the built-in video import is limited to uncompressed AVI in those circumstances. The frame-by-frame export functionality may be useful even for users who do have Quicktime, as it does not require the entire image list to be created before export. 7 | 8 | For information on supported formats see the demo notebook. 9 | 10 | 11 | Installation 12 | ------------ 13 | 14 | Place the MathMF folder in your Applications directory. To get the path to this directory you can evaluate `FileNameJoin[{$UserBaseDirectory, "Applications"}]` in *Mathematica*. 15 | 16 | 17 | Loading 18 | ------- 19 | 20 | To load the package use ``Needs["MathMF`"]`` 21 | 22 | Upon loading, the package will look for the LibraryLink DLL "MathMF.dll". 23 | If the DLL is not found, the package will attempt to build it using `CreateLibrary`. 24 | If the build fails you will need to use the pre-built DLL (see below). 25 | 26 | 27 | Demo notebook 28 | ------------- 29 | 30 | The "MathMF Demo.nb" notebook contains simple examples of using the import (Source Reader) and export (Sink Writer) functionality. Be sure to change the example file paths to something suitable for your system. 31 | 32 | 33 | Pre-built DLL 34 | ------------- 35 | 36 | The pre-built DLL is provided in case the library cannot be created locally (for example if a suitable C compiler is not present). The DLL should be placed in the directory obtained from `FileNameJoin[{$UserBaseDirectory, "SystemFiles\\LibraryResources\\Windows-x86-64"}]` 37 | 38 | The pre-built DLL requires the Visual C++ Redistributable for Visual Studio 2012. If not already installed this can be obtained from Microsoft at http://www.microsoft.com/en-gb/download/details.aspx?id=30679 39 | 40 | 41 | --------------------------------------------------------------------------------